From 6d48c70157c852fd48ee172cdd9d98de4e17f228 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 6 Jul 2026 19:41:21 +0000 Subject: [PATCH] fix(prober-queue): time out scans from job enqueue time, not created_at First live scans false-failed ('scan timed out', completed:0): backlog rows sat 'queued' ~68 min before the sweep enqueued them, and the reconcile timed them out immediately because it measured age from created_at. Measure from job.timestamp so freshly-enqueued jobs get the full window. Co-Authored-By: Claude Opus 4.8 --- lib/prober-queue.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/prober-queue.ts b/lib/prober-queue.ts index 4a1bba7b..12b8f90c 100644 --- a/lib/prober-queue.ts +++ b/lib/prober-queue.ts @@ -151,10 +151,10 @@ export async function processDuePortScans( for (const scan of (runningRows ?? []) as ScanRow[]) { const job = (await q.getJob(scan.id)) as Job | undefined; - const age = Date.now() - new Date(scan.created_at).getTime(); if (!job) { - if (age > RUNNING_TIMEOUT_MS) { + // No job and an old row => the job was lost (not just slow to enqueue). + if (Date.now() - new Date(scan.created_at).getTime() > RUNNING_TIMEOUT_MS) { await markFailed(supabase, scan.id, "prober job missing"); failed++; } @@ -168,7 +168,10 @@ export async function processDuePortScans( } else if (state === "failed") { await markFailed(supabase, scan.id, job.failedReason ?? "prober job failed"); failed++; - } else if (age > RUNNING_TIMEOUT_MS) { + } else if (Date.now() - job.timestamp > RUNNING_TIMEOUT_MS) { + // Measure the timeout from when the job was ENQUEUED (job.timestamp), not + // when the scan row was created — a row can sit `queued` a long time + // before a job exists, and timing out from created_at kills fresh jobs. await markFailed(supabase, scan.id, "scan timed out"); failed++; }