Repository navigation
feat: Work queue core — leased work items, with token cleanup and retention as periodic kinds #278
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestneeds-triageMaintainer needs to evaluate this issueMaintainer needs to evaluate this issue
Description
Parent
What to build
The durable work queue, end to end, with maintenance as its first callers. Refresh-token cleanup and the queue's own retention purge stop being private timers. Every worker schedules them as unscoped periodic work items. A worker claims one, runs it under a lease, and records the outcome in WorkItems. A worker that dies, pauses or shuts down never leaves an item stuck in Running.
Everything follows #276's Implementation Decisions. In short:
- Table: the
WorkItemsentity, with Fluent configuration,HasComment, a migration and the regenerateddocs/schema.sql. - Producing:
IWorkQueue.EnqueueAsyncis the only insert path, with opt-inDedupKeycoalescing. - Store:
- a claim over
FOR UPDATE SKIP LOCKEDthat incrementsAttempt; - a 2 s heartbeat that extends the 60 s lease and writes the progress snapshot, fenced by
Id,AttemptandRunning; - lease recovery with the 3-stall limit;
- release on graceful shutdown without a stall;
Failedwith the error on a handler exception, with no retry.
- a claim over
- Executor: one item at a time per worker, first in, first out.
- Scheduler: one
IWorkScheduleper periodic kind; due-ness derived from the table, so overdue work runs right after downtime. - Kinds: a
WorkKindenum,IWorkPayloadwith a static abstract kind,IWorkHandler<TPayload>in the Worker, and a registry that fails startup unless every kind has exactly one payload and exactly one handler. Noswitchon the kind. - First kinds:
TokenCleanupreplacesTokenCleanupService's timer, andWorkItemRetentiondeletes finished items older than the retention period. - Settings: poll 5 s, heartbeat 2 s, lease 60 s, 3 stalls, retention 30 days, token cleanup 24 h, retention purge 24 h.
Scope routing by storage config is not in this ticket. Both kinds here are unscoped; routing ships with its first caller, the crawl ticket.
Acceptance criteria
- Token cleanup runs as a
TokenCleanupwork item on its interval, andTokenCleanupService's own timer is gone. - Finished items older than the retention period are deleted by a
WorkItemRetentionitem. - Two workers that find the same periodic item due produce one queued item; the second insert is absorbed.
- Contract tests on real PostgreSQL (integration project):
- concurrent claimers never get the same item;
- after a reclaim, the old attempt's heartbeat and completion affect no rows;
- an expired lease puts the item back in
Queuedwith a stall, and the 3rd stall fails it as stalled; -
DedupKeycoalesces queued items only.
- Executor and scheduler unit tests with a substituted store and
FakeTimeProvider:- the heartbeat cadence;
- a fenced heartbeat (0 rows) cancels the handler;
- shutdown releases the item without a stall;
- an exception fails the item;
- only one item runs at a time;
- due-ness is computed from the last finished item.
- Both kinds have a test that a second run converges. The existing token cleanup tests move onto its handler.
- Startup fails for a kind without a payload or a handler, or with two of either.
- Architecture rules: only
IWorkQueueinserts work items, and handlers live only in the Worker. Each is shown failing on a deliberate violation. - An ADR records the hand-rolled PostgreSQL work queue over a library, citing the evaluation on feat: Work queue — API requests Worker jobs, admin sees queued and running work #273.
- PostgreSQL-only store members carry
// Stryker disable allwith ADR 0005 rows; everything else reaches 100% mutation score. - CLAUDE.md describes the work queue, and the Worker section no longer describes the token cleanup timer.
First caller
The TokenCleanup and WorkItemRetention kinds.
Design
Decided with the maintainer on 2026-10-18 (#276, Implementation Decisions). The library evaluation is in #273.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestneeds-triageMaintainer needs to evaluate this issueMaintainer needs to evaluate this issue
Activity
github-actions commentedon Oct 18, 2026
Moved back to
needs-triage:docs/agents/design-process.md("What every issue carries") requires these parts beforeready-for-agent:Add them, then label the issue
ready-for-agentagain.feat: PostgreSQL integration tests and the work queue core (#277, #278)…