Fixing an issue with multi-tenant app migration failures - the app migration context wasn't getting the tenant ID as needed. Also added some better logging as well as cleaning up the queues properly to remove these dud events.

This commit is contained in:
mike12345567 2024-06-07 22:50:06 +01:00
parent 67c6c156e3
commit ee391b30d6
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) {