|
import { client, collections } from "$lib/server/database"; |
|
import { migrations } from "./routines"; |
|
import { acquireLock, releaseLock, isDBLocked, refreshLock } from "./lock"; |
|
import { isHuggingChat } from "$lib/utils/isHuggingChat"; |
|
|
|
const LOCK_KEY = "migrations"; |
|
|
|
export async function checkAndRunMigrations() { |
|
|
|
if (new Set(migrations.map((m) => m._id.toString())).size !== migrations.length) { |
|
throw new Error("Duplicate migration GUIDs found."); |
|
} |
|
|
|
|
|
const migrationResults = await collections.migrationResults.find().toArray(); |
|
|
|
|
|
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..."); |
|
|
|
|
|
const connectedClient = await client.connect(); |
|
|
|
const lockId = await acquireLock(LOCK_KEY); |
|
|
|
if (!lockId) { |
|
|
|
console.log( |
|
"[MIGRATIONS] Another instance already has the lock. Waiting for DB to be unlocked." |
|
); |
|
|
|
|
|
|
|
while (await isDBLocked(LOCK_KEY)) { |
|
await new Promise((resolve) => setTimeout(resolve, 1000)); |
|
} |
|
return; |
|
} |
|
|
|
|
|
|
|
const refreshInterval = setInterval(async () => { |
|
await refreshLock(LOCK_KEY, lockId); |
|
}, 1000 * 10); |
|
|
|
|
|
for (const migration of migrations) { |
|
|
|
const existingMigrationResult = migrationResults.find( |
|
(m) => m._id.toString() === migration._id.toString() |
|
); |
|
|
|
|
|
if (existingMigrationResult) { |
|
console.log(`[MIGRATIONS] "${migration.name}" already applied. Skipping...`); |
|
} else { |
|
|
|
if ( |
|
(migration.runForHuggingChat === "only" && !isHuggingChat) || |
|
(migration.runForHuggingChat === "never" && isHuggingChat) |
|
) { |
|
console.log( |
|
`[MIGRATIONS] "${migration.name}" should not be applied for this run. Skipping...` |
|
); |
|
continue; |
|
} |
|
|
|
|
|
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(LOCK_KEY, lockId); |
|
} |
|
|