Spaces:
Running
Running
File size: 3,446 Bytes
98b1c51 4dbcbb6 ddbe7d2 4dbcbb6 a4c3fca 4dbcbb6 98b1c51 4dbcbb6 ddbe7d2 4dbcbb6 98b1c51 4dbcbb6 a4c3fca 4dbcbb6 a4c3fca 4dbcbb6 ddbe7d2 4dbcbb6 a4c3fca 4dbcbb6 a4c3fca 4dbcbb6 a4c3fca 4dbcbb6 29b7d2a 4dbcbb6 29b7d2a ddbe7d2 4dbcbb6 ddbe7d2 4dbcbb6 29b7d2a ddbe7d2 29b7d2a 4dbcbb6 98b1c51 4dbcbb6 98b1c51 4dbcbb6 98b1c51 4dbcbb6 ddbe7d2 4dbcbb6 98b1c51 4dbcbb6 98b1c51 4dbcbb6 ddbe7d2 4dbcbb6 a4c3fca 4dbcbb6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
import { Database } from "$lib/server/database";
import { migrations } from "./routines";
import { acquireLock, releaseLock, isDBLocked, refreshLock } from "./lock";
import { isHuggingChat } from "$lib/utils/isHuggingChat";
import { logger } from "$lib/server/logger";
const LOCK_KEY = "migrations";
export async function checkAndRunMigrations() {
// make sure all GUIDs are unique
if (new Set(migrations.map((m) => m._id.toString())).size !== migrations.length) {
throw new Error("Duplicate migration GUIDs found.");
}
// check if all migrations have already been run
const migrationResults = await Database.getInstance()
.getCollections()
.migrationResults.find()
.toArray();
logger.info("[MIGRATIONS] Begin check...");
// connect to the database
const connectedClient = await Database.getInstance().getClient().connect();
const lockId = await acquireLock(LOCK_KEY);
if (!lockId) {
// another instance already has the lock, so we exit early
logger.info(
"[MIGRATIONS] Another instance already has the lock. Waiting for DB to be unlocked."
);
// Todo: is this necessary? Can we just return?
// block until the lock is released
while (await isDBLocked(LOCK_KEY)) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
return;
}
// once here, we have the lock
// make sure to refresh it regularly while it's running
const refreshInterval = setInterval(async () => {
await refreshLock(LOCK_KEY, lockId);
}, 1000 * 10);
// iterate over all migrations
for (const migration of migrations) {
// check if the migration has already been applied
const shouldRun =
migration.runEveryTime ||
!migrationResults.find((m) => m._id.toString() === migration._id.toString());
// check if the migration has already been applied
if (!shouldRun) {
logger.info(`[MIGRATIONS] "${migration.name}" already applied. Skipping...`);
} else {
// check the modifiers to see if some cases match
if (
(migration.runForHuggingChat === "only" && !isHuggingChat) ||
(migration.runForHuggingChat === "never" && isHuggingChat)
) {
logger.info(
`[MIGRATIONS] "${migration.name}" should not be applied for this run. Skipping...`
);
continue;
}
// otherwise all is good and we can run the migration
logger.info(
`[MIGRATIONS] "${migration.name}" ${
migration.runEveryTime ? "should run every time" : "not applied yet"
}. Applying...`
);
await Database.getInstance()
.getCollections()
.migrationResults.updateOne(
{ _id: migration._id },
{
$set: {
name: migration.name,
status: "ongoing",
},
},
{ upsert: true }
);
const session = connectedClient.startSession();
let result = false;
try {
await session.withTransaction(async () => {
result = await migration.up(Database.getInstance());
});
} catch (e) {
logger.info(`[MIGRATIONS] "${migration.name}" failed!`);
logger.error(e);
} finally {
await session.endSession();
}
await Database.getInstance()
.getCollections()
.migrationResults.updateOne(
{ _id: migration._id },
{
$set: {
name: migration.name,
status: result ? "success" : "failure",
},
},
{ upsert: true }
);
}
}
logger.info("[MIGRATIONS] All migrations applied. Releasing lock");
clearInterval(refreshInterval);
await releaseLock(LOCK_KEY, lockId);
}
|