Scheduler and worker loops survive a database restart
Test and deploy / test (push) Successful in 1m0s
Test and deploy / deploy (push) Successful in 2m52s

index-scheduler's loop ended when Postgres restarted (the active-groups
query was outside its try), leaving live indexing stopped for ~3 hours
while the process stayed up. Each step's error is now caught and retried
next round; worker:spots and worker:deferred wait 30 s and claim again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daan MeijerandClaude Opus 5.5 committed 2026-10-07 13:02:08 +02:00
1 parent 61192690a7
commit e3e3ecc93d
3 files changed
+23 -4

No files matched your search

+11 -4
View File
@@ -117,11 +117,18 @@ export default class IndexScheduler extends BaseCommand {
} }
} }
// Run immediately, then wait a full interval after each run so runs never overlap. // Run immediately, then wait a full interval after each run so runs never overlap. A failed
// round (e.g. Postgres restarting) is logged and the next one tries again: an error that escaped
// here once ended the loop while the process stayed up, and live indexing stopped for ~3 hours
// (LOG.md 2026-10-07 (3)).
while (!Shutdown.requested) { while (!Shutdown.requested) {
await schedule() for (const step of [schedule, backfill, split]) {
await backfill() try {
await split() await step()
} catch (err: any) {
this.logger.error(`Scheduler round failed, retrying next round: ${err.message}`)
}
}
await Shutdown.sleep(indexerConfig.intervalMs) await Shutdown.sleep(indexerConfig.intervalMs)
} }
await this.pool.shutdown() await this.pool.shutdown()
+6
View File
@@ -31,6 +31,7 @@ export default class DeferredWorker extends BaseCommand {
let done = 0 let done = 0
try { try {
while (!Shutdown.requested && (!this.limit || done < this.limit)) { while (!Shutdown.requested && (!this.limit || done < this.limit)) {
try {
const batch = await DeferredService.claim(Math.min(concurrency * 4, this.limit ? this.limit - done : Infinity)) const batch = await DeferredService.claim(Math.min(concurrency * 4, this.limit ? this.limit - done : Infinity))
if (batch.length === 0) { if (batch.length === 0) {
if (this.limit) break if (this.limit) break
@@ -48,6 +49,11 @@ export default class DeferredWorker extends BaseCommand {
) )
done += batch.length done += batch.length
this.logger.info(`${done} deferred files processed: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ')}.`) this.logger.info(`${done} deferred files processed: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ')}.`)
} catch (err: any) {
// E.g. Postgres restarting: wait and claim again, instead of ending the loop (LOG.md 2026-10-07 (3)).
this.logger.error(`Round failed, retrying in 30 s: ${err.message}`)
await Shutdown.sleep(30_000)
}
} }
} finally { } finally {
await NntpService.shutdown() await NntpService.shutdown()
+6
View File
@@ -33,6 +33,7 @@ export default class SpotsWorker extends BaseCommand {
let done = 0 let done = 0
try { try {
while (!Shutdown.requested && (!this.limit || done < this.limit)) { while (!Shutdown.requested && (!this.limit || done < this.limit)) {
try {
const batch = await SpotService.claim(Math.min(BATCH, this.limit ? this.limit - done : Infinity)) const batch = await SpotService.claim(Math.min(BATCH, this.limit ? this.limit - done : Infinity))
if (batch.length === 0) { if (batch.length === 0) {
if (this.limit) break if (this.limit) break
@@ -55,6 +56,11 @@ export default class SpotsWorker extends BaseCommand {
) )
done += batch.length done += batch.length
this.logger.info(`${done} spots: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ')}.`) this.logger.info(`${done} spots: ${Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ')}.`)
} catch (err: any) {
// E.g. Postgres restarting: wait and claim again, instead of ending the loop (LOG.md 2026-10-07 (3)).
this.logger.error(`Round failed, retrying in 30 s: ${err.message}`)
await Shutdown.sleep(30_000)
}
} }
} finally { } finally {
await NntpService.shutdown() await NntpService.shutdown()