From 36f84ae3e6b0fee54c99110065c310c3c6959ced Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Thu, 8 Oct 2026 11:50:46 +0200 Subject: [PATCH] Garbage: retry the apply after the deleting transaction has committed Co-Authored-By: Claude Opus 5.5 --- app/services/ContentService.ts | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/services/ContentService.ts b/app/services/ContentService.ts index 83abbc6..0f1345b 100644 --- a/app/services/ContentService.ts +++ b/app/services/ContentService.ts @@ -158,19 +158,23 @@ class ContentService { * what it found. Then the release is deleted if it turned out to be garbage (D42). */ async apply(task: InspectTask, probe: InspectProbe): Promise { - let result: ContentResult - try { - result = await this.applyProbe(task, probe) - } catch (error: any) { - // Another probe deleted a release this one writes to as garbage meanwhile (LOG.md 2026-10-08 (7)): - // the file's own release (nothing left to do), or one its par2 set joins (once more: the set - // then finds the release's files gone and makes its own). - if (error.code !== '23503') throw error - if (!(await db.from('files').where('id', task.fileId).select('id').first())) { - return { named: 0, contentType: probe.contentType, garbage: 'deleted meanwhile' } + let result: ContentResult | undefined + // Another probe deleted a release this one writes to as garbage meanwhile (LOG.md 2026-10-08 (7)): + // the file's own release (nothing left to do), or one its par2 set joins. Again once that + // deletion has committed: the set then finds the release's files gone and makes its own. + for (const wait of [0, 1000, 3000]) { + if (wait) await new Promise((resolve) => setTimeout(resolve, wait)) + try { + result = await this.applyProbe(task, probe) + break + } catch (error: any) { + if (error.code !== '23503' || wait === 3000) throw error + if (!(await db.from('files').where('id', task.fileId).select('id').first())) { + return { named: 0, contentType: probe.contentType, garbage: 'deleted meanwhile' } + } } - result = await this.applyProbe(task, probe) } + if (!result) throw new Error(`Applying the probe of file ${task.fileId} gave no result`) result.garbage = (await GarbageFilter.afterProbe(task.fileId, probe.contentType)) ?? undefined return result }