File size: 3,306 Bytes
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 |
import { client, collections } from "$lib/server/database";
import { migrations } from "./routines";
import { acquireLock, releaseLock, isDBLocked, refreshLock } from "./lock";
import { isHuggingChat } from "$lib/utils/isHuggingChat";
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 collections.migrationResults.find().toArray();
// if all the migrations._id are in the migrationResults, we can exit early
if (
migrations.every((m) => migrationResults.some((m2) => m2._id.toString() === m._id.toString()))
) {
console.log("[MIGRATIONS] All migrations already applied.");
return;
}
console.log("[MIGRATIONS] Begin check...");
// connect to the database
const connectedClient = await client.connect();
const hasLock = await acquireLock();
if (!hasLock) {
// another instance already has the lock, so we exit early
console.log(
"[MIGRATIONS] Another instance already has the lock. Waiting for DB to be unlocked."
);
// block until the lock is released
while (await isDBLocked()) {
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();
}, 1000 * 10);
// iterate over all migrations
for (const migration of migrations) {
// check if the migration has already been applied
const existingMigrationResult = migrationResults.find(
(m) => m._id.toString() === migration._id.toString()
);
// check if the migration has already been applied
if (existingMigrationResult) {
console.log(`[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)
) {
console.log(
`[MIGRATIONS] "${migration.name}" should not be applied for this run. Skipping...`
);
continue;
}
// otherwise all is good and we cna run the migration
console.log(`[MIGRATIONS] "${migration.name}" not applied yet. Applying...`);
await collections.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(connectedClient);
});
} catch (e) {
console.log(`[MIGRATION[] "${migration.name}" failed!`);
console.error(e);
} finally {
await session.endSession();
}
await collections.migrationResults.updateOne(
{ _id: migration._id },
{
$set: {
name: migration.name,
status: result ? "success" : "failure",
},
},
{ upsert: true }
);
}
}
console.log("[MIGRATIONS] All migrations applied. Releasing lock");
clearInterval(refreshInterval);
await releaseLock();
}
|