Reference solution
The findings below are ordered by priority.
Findings
1. Interval renewals can overlap and fail without stopping the worker
Location: src/cleanup-worker.ts:33-42
setInterval starts another async renewal even when the previous one is pending. Responses can complete out of order and replace a newer lease with an older value. A rejected renewal is also left on an unobserved promise, so deletion continues instead of treating renewal failure as lost leadership.
Run a serialized renewal loop, await each result, and funnel null and rejection through one leadership-loss path before scheduling another renewal.
2. Release uses the superseded acquisition lease
Location: src/cleanup-worker.ts:55-57
Successful renewals replace #lease, but cleanup releases the original acquired value. Because release compares the current lease value, it cannot release the renewed lease and ownership remains until expiry.
Stop renewal, join any pending renewal, and release the latest successfully stored lease.
3. The retention cutoff moves during one leadership term
Location: src/cleanup-worker.ts:45-52
The cutoff is recalculated for every batch. A long run therefore begins deleting records that were not eligible when the term started, contrary to the stable-snapshot contract and making the run's scope timing-dependent.
Capture the cutoff once after acquiring leadership and reuse it for all batches in that term.
4. A leadership term has no 1,000-row limit
Location: src/cleanup-worker.ts:44-53
The loop continues while full batches are returned and never tracks a total. A large backlog can monopolize the database indefinitely rather than yielding after five 200-row batches.
Track the cumulative count, cap each next batch by the remaining allowance, and finish the term at 1,000 rows.
5. Stop and failure do not guarantee joined lease release
Location: src/cleanup-worker.ts:19-31,44-57
stop only flips a flag and clears the interval. It does not await #running, an executing delete, or a renewal callback. If stop wins while acquire is pending, a subsequently acquired lease returns without release; a deletion rejection also bypasses the release at the bottom of #run.
Make stop idempotently join the run and pending renewal, with #run using finally to clear scheduling and release any current ownership on every exit.
Reasonable non-findings
- Acquiring leadership before the first delete is correct.
- A 200-row repository limit is within the per-batch cap.
- Repository enforcement of fencing tokens is still required even with correct worker-side sequencing.