Skip to content

Commit ffe83ed

Browse files
ralyodioclaude
andauthored
Re-queue behind a dead BullMQ job, not just an absent one (#286)
The sweep asked whether a BullMQ job existed for a stranded row and skipped the row if one did. Existence is the wrong question: BullMQ retains a job after it finishes, so a failed or completed job is still findable by id and is emphatically not scheduled. Every row whose attempt had failed sat queued forever behind the corpse of the attempt that failed it — which is exactly the stranding this sweep was written to undo. It cost a working deploy. With the GIF frame-pattern bug fixed and shipped and the row reset to queued, nothing rendered for five minutes and the sweep logged nothing, because it was skipping the row on every pass. A job in a live state is still left alone. A dead one is removed so its id is free, and the row is scheduled again. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent bb8e9d2 commit ffe83ed

1 file changed

Lines changed: 17 additions & 5 deletions

File tree

‎lib/ads/video/sweep.ts‎

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,24 @@ export async function processDueVideoRenders(
4949
const hash = row.render_hash as string;
5050
const profile = (row.output_profile as string) ?? "default";
5151

52-
// Already scheduled: re-adding is harmless because BullMQ dedupes on the
53-
// job id, but checking first keeps the log honest about what this did.
54-
const existing = await queue.getJob(renderJobId(hash, profile)).catch(() => null);
52+
// Already scheduled? Existence is not the question — BullMQ retains a job
53+
// after it finishes, so a failed or completed job is still findable and is
54+
// emphatically not scheduled. Treating "a job exists" as "work is coming"
55+
// left rows queued forever behind the corpse of the attempt that failed
56+
// them, which is precisely the stranding this sweep exists to undo.
57+
//
58+
// A job in a live state is left alone. A dead one is removed so the id is
59+
// free, then re-added below.
60+
const jobId = renderJobId(hash, profile);
61+
const existing = await queue.getJob(jobId).catch(() => null);
5562
if (existing) {
56-
skipped++;
57-
continue;
63+
const jobState = await existing.getState().catch(() => "unknown");
64+
if (jobState === "failed" || jobState === "completed") {
65+
await existing.remove().catch(() => {});
66+
} else {
67+
skipped++;
68+
continue;
69+
}
5870
}
5971

6072
// A row with no design cannot be rendered and re-queueing it forever would

0 commit comments

Comments
 (0)