Merge pull request #13891 from Budibase/fix/app-migration-multi-tenant-failure

Fixing multi-tenant app migration issue
This commit is contained in:
Michael Drury 2024-06-07 23:03:02 +01:00 committed by GitHub
commit ca370ba214
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 43 deletions

View File

@ -21,6 +21,7 @@ let cleanupInterval: NodeJS.Timeout
async function cleanup() { async function cleanup() {
for (let queue of QUEUES) { for (let queue of QUEUES) {
await queue.clean(CLEANUP_PERIOD_MS, "completed") await queue.clean(CLEANUP_PERIOD_MS, "completed")
await queue.clean(CLEANUP_PERIOD_MS, "failed")
} }
} }

View File

@ -48,8 +48,6 @@ export async function checkMissingMigrations(
}, },
{ {
jobId: `${appId}_${latestMigration}`, jobId: `${appId}_${latestMigration}`,
removeOnComplete: true,
removeOnFail: true,
} }
) )

View File

@ -12,54 +12,56 @@ export async function processMigrations(
migrations: AppMigration[] migrations: AppMigration[]
) { ) {
console.log(`Processing app migration for "${appId}"`) console.log(`Processing app migration for "${appId}"`)
// have to wrap in context, this gets the tenant from the app ID
await locks.doWithLock( await context.doInAppContext(appId, async () => {
{ await locks.doWithLock(
name: LockName.APP_MIGRATION, {
type: LockType.AUTO_EXTEND, name: LockName.APP_MIGRATION,
resource: appId, type: LockType.AUTO_EXTEND,
}, resource: appId,
async () => { },
await context.doInAppMigrationContext(appId, async () => { async () => {
try { try {
let currentVersion = await getAppMigrationVersion(appId) await context.doInAppMigrationContext(appId, async () => {
let currentVersion = await getAppMigrationVersion(appId)
const pendingMigrations = migrations const pendingMigrations = migrations
.filter(m => m.id > currentVersion) .filter(m => m.id > currentVersion)
.sort((a, b) => a.id.localeCompare(b.id)) .sort((a, b) => a.id.localeCompare(b.id))
const migrationIds = migrations.map(m => m.id).sort() const migrationIds = migrations.map(m => m.id).sort()
let index = 0 let index = 0
for (const { id, func } of pendingMigrations) { for (const { id, func } of pendingMigrations) {
const expectedMigration = const expectedMigration =
migrationIds[migrationIds.indexOf(currentVersion) + 1] migrationIds[migrationIds.indexOf(currentVersion) + 1]
if (expectedMigration !== id) { if (expectedMigration !== id) {
throw new Error( throw new Error(
`Migration ${id} could not run, update for "${id}" is running but ${expectedMigration} is expected` `Migration ${id} could not run, update for "${id}" is running but ${expectedMigration} is expected`
) )
}
const counter = `(${++index}/${pendingMigrations.length})`
console.info(`Running migration ${id}... ${counter}`, {
migrationId: id,
appId,
})
await func()
await updateAppMigrationMetadata({
appId,
version: id,
})
currentVersion = id
} }
})
const counter = `(${++index}/${pendingMigrations.length})`
console.info(`Running migration ${id}... ${counter}`, {
migrationId: id,
appId,
})
await func()
await updateAppMigrationMetadata({
appId,
version: id,
})
currentVersion = id
}
} catch (err) { } catch (err) {
logging.logAlert("Failed to run app migration", err) logging.logAlert("Failed to run app migration", err)
throw err throw err
} }
}) }
} )
)
console.log(`App migration for "${appId}" processed`) console.log(`App migration for "${appId}" processed`)
})
} }

View File

@ -1,9 +1,23 @@
import { queue } from "@budibase/backend-core" import { queue, logging } from "@budibase/backend-core"
import { Job } from "bull" import { Job } from "bull"
import { MIGRATIONS } from "./migrations" import { MIGRATIONS } from "./migrations"
import { processMigrations } from "./migrationsProcessor" import { processMigrations } from "./migrationsProcessor"
const appMigrationQueue = queue.createQueue(queue.JobQueue.APP_MIGRATION) const MAX_ATTEMPTS = 3
const appMigrationQueue = queue.createQueue(queue.JobQueue.APP_MIGRATION, {
jobOptions: {
attempts: MAX_ATTEMPTS,
removeOnComplete: true,
removeOnFail: true,
},
maxStalledCount: MAX_ATTEMPTS,
removeStalledCb: async (job: Job) => {
logging.logAlert(
`App migration failed, queue job ID: ${job.id} - reason: ${job.failedReason}`
)
},
})
appMigrationQueue.process(processMessage) appMigrationQueue.process(processMessage)
async function processMessage(job: Job) { async function processMessage(job: Job) {