Files
Eric Mc Sween 528e587038 Merge pull request #35377 from overleaf/em-change-library-mongo-cluster
Add auxiliary Mongo cluster support for library collections

GitOrigin-RevId: 1cfdbe4bbeb6c95f1158834ae60a4e34bef7bb87
2026-09-17 11:26:00 +00:00

118 lines
3.5 KiB
JavaScript

import Path from 'node:path'
import { db, auxInternalDb } from './mongodb.mjs'
import { fileURLToPath } from 'node:url'
const __filename = fileURLToPath(import.meta.url)
const __dirname = Path.dirname(__filename)
// Migrations tagged 'auxiliary' track their execution history on the
// auxiliary Mongo cluster (when one is configured) instead of the primary
// one, so a migration already marked executed against the primary cluster
// still runs once the auxiliary cluster is provisioned. When no auxiliary
// cluster is configured, everything falls back to the primary cluster.
const auxMigrations = auxInternalDb
? auxInternalDb.collection('migrations')
: null
class Adapter {
constructor(params) {
if (
!process.env.SKIP_TAG_CHECK &&
!process.argv.includes('create') &&
!(process.argv.includes('-t') || process.argv.includes('--tag'))
) {
console.error("ERROR: must pass tags using '-t' or '--tag', exiting")
process.exit(1)
}
this.params = params || {}
this.migrationTagsByName = new Map()
}
getTemplatePath() {
return Path.resolve(__dirname, '20000000000000_template.mjs')
}
async connect() {
return { db }
}
disconnect() {
return Promise.resolve()
}
async _getMigrationTags(name) {
if (!this.migrationTagsByName.has(name)) {
const { dir, migrationExtension } = this.params
const migrationPath = Path.resolve(dir, `${name}.${migrationExtension}`)
let tags = []
try {
const { default: migration } = await import(migrationPath)
tags = migration.tags || []
} catch (err) {
if (err.code !== 'ERR_MODULE_NOT_FOUND') {
throw err
}
// The migration file no longer exists on disk (e.g. it was removed
// long after it ran everywhere). We can't know if it was tagged
// 'auxiliary', so it stays tracked as a primary-only migration.
}
this.migrationTagsByName.set(name, tags)
}
return this.migrationTagsByName.get(name)
}
async _migrationsCollectionFor(name) {
if (!auxMigrations) {
return db.migrations
}
const tags = await this._getMigrationTags(name)
return tags.includes('auxiliary') ? auxMigrations : db.migrations
}
async getExecutedMigrationNames() {
const primaryNames = (
await db.migrations.find({}, { projection: { name: 1 } }).toArray()
).map(migration => migration.name)
if (!auxMigrations) {
return primaryNames.sort()
}
// Once an auxiliary cluster is configured, an 'auxiliary'-tagged
// migration counts as executed only if it ran against the auxiliary
// cluster — a stale primary-side record from before the auxiliary
// cluster existed is ignored, so the migration runs again on the
// auxiliary cluster.
const nonAuxPrimaryNames = []
for (const name of primaryNames) {
const tags = await this._getMigrationTags(name)
if (!tags.includes('auxiliary')) {
nonAuxPrimaryNames.push(name)
}
}
const auxNames = (
await auxMigrations.find({}, { projection: { name: 1 } }).toArray()
).map(migration => migration.name)
return [...nonAuxPrimaryNames, ...auxNames].sort()
}
async markExecuted(name) {
const collection = await this._migrationsCollectionFor(name)
return await collection.insertOne({
name,
migratedAt: new Date(),
})
}
async unmarkExecuted(name) {
const collection = await this._migrationsCollectionFor(name)
return await collection.deleteOne({
name,
})
}
}
export default Adapter