Files
alkstore/tasks/mem-engine-queues.md
T
glm-5.3-flash 61a1148165 mem engine queues — the wave-6 mechanism task lands: coverage reconciliation against the seam task's foundation, with the six unpinned engine-side pins (mem-engine-queues)
The queue mechanism (ADR-010 state machine over the in-memory job
table, mem's third-owner equal-jitter curve + opts resolution)
already landed implemented with the seam task's whole-engine scope
(mem-engine-seam-tx Note 1); this task's work, per the disposition the
notify/listen twin recorded, was reconciling the description's pin
list against the existing suite and adding the missing engine-side
unit pins — no production code changed, the realization verified
correct as-found.

Already pinned by the seam task (reconciled, recorded in Notes):
lifecycle + stamps + resolved get_job values, claim ordering, extent
clamps at entry, worker-id validation, the uniform validity predicate,
late-heartbeat refusal + absolute-reset renewal, reclaim-eats-attempt
+ loser-ack-refuses, the pinned dead-letter default strings, dead-row
get_job visibility, both-states sweep + retention + scoping, the tx
stamp shapes, and the resolution legs + base-5 curve range/cap/spread.

Added (6): the in-process claim-race atomicity pin (8 concurrent
workers over 4 rows — every row handed exactly once through the
guarded section, the description's claim-vs-claim bullet); the
claim-vs-expiry boundary determinism pin (at now == deadline: the d <
now reclaim predicate and the d >= now holder-op predicate are
coherent — no reclaim, heartbeat/ack still valid; one second past:
reclaimable with its attempt consumed; lapsed ids silently uncounted
by ack_batch); the engine-side mirror of the suite's
backoff_curve_equivalence row — base 2 s at [1,2]/[2,4]/[4,8] driven
end-to-end through claim -> retry(None) -> get_job's run_at with the
Some(5)-verbatim and Some(0)-ready-now override legs, exact under the
frozen clock — both through the handle and pinned at backoff_delay_s
itself (the cap leg stays in resolution.rs's base-5 pins per
suite-opts-backoff-rows' recorded disposition); the D-12 pre-claim
sweep pin (a lapsed row with its budget spent claims nothing and
dead-letters with the exhaust string at the next claim, never
strands); and unconditional cancel across both states (the holder's
post-cancel ack refusing — the not-an-interrupt honesty). One
test-authoring note recorded: heartbeat's absolute reset moves the
deadline, so boundary legs stage on fresh rows to keep each
observation exact.

Task file: status completed, Notes (the seam-carried disposition +
reconciliation inventory + the six adds + the heartbeat-observation
note) + Summary filled. Verified: cargo test -p alkstore-mem 97/97
(91 + 6 new; new tests green solo x3), workspace cargo test green
(core 25, suite-harness 3, mem 97, pg 124+25+9, SQLite 191+25),
clippy --all-targets -D warnings clean, cargo fmt --check clean,
cargo check --target wasm32-unknown-unknown -p alkstore-mem clean
2026-10-10 12:15:04 +00:00

9.8 KiB
Raw Blame History

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
mem-engine-queues Mem engine — queues (in-memory job table, ADR-010 state machine, engine-owned backoff curve) completed
mem-engine-seam-tx
mem-engine-notify-listen
moderate medium component implementation
wave-6
mem-engine
queues

Description

The queue mechanism: the ADR-010 state machine realized over an in-memory job table — all driver-free.

  • Queue handle surface (ADR-019 §1): enqueue (auto-commit, resolving over the handle's opts), claim_one/claim_batch (extents: n <= 0 claims nothing), ack_batch (per-id uniform predicate, lapsed/non-claimed silently not counted), cancel (unconditional delete), get_job, and the queue-scoped sweep_expired (no-stranded-rows: every past-expiry row in any state moves to dead + retention enforcement).
  • Claim discipline: exactly-once handout, worker_id claimant identity (non-empty), claim ordering priority DESC → ready-time → enqueue FIFO. Visibility expiry and reclaim — reclaim consumes an attempt; the dual-execution window is contract-honest (a worker past its deadline may complete its work but its ack will not land). Under a single engine instance the claim race is in-process — the guarded merge discipline must still make claim-vs-claim atomic and claim-vs-expiry deterministic at the row level.
  • JobHandle ops (ADR-019 §3): ack/retry/fail one-shot terminal, heartbeat(&self) repeatable absolute-reset renewal; the uniform validity predicate (processing + unexpired deadline); deadline lapse refuses all ops, false not error. retry(err, None) computes the queue's equal-jitter exponential delay — mem's own implementation (ADR-012 §2's one-owner rule, third owner; the SQLite engine's resolution.rs is reference, not a dependency); retry(err, Some(d)) overrides verbatim; fail = immediate dead-letter with the engine-default strings ("failed" / "max attempts exceeded").
  • Opts stamping (ADR-020): resolution rules (delay-over-run_at, relative expires) are mem-owned (third copy); handle-enqueue stamps the handle's opts; the tx/no-handle shapes stamped plain 300/3/5/none in mem-engine-seam-tx. get_job shows resolved values.
  • Dead letters: move-to-dead storage, get_job sees dead rows (last_error/died_at), dead_letter_retention_s (None = forever) enforced by sweep_expired. No redrive API.
  • Timestamps and expiry checks are tokio-time/clock reads against the state core's clock accessor; expiry is checked at claim/renew boundaries (no ambient sweeper — sweep_expired is caller-driven, cadence recipe unchanged).

Acceptance Criteria

  • Full Queue/JobHandle surfaces over the in-memory job table
  • The ADR-010 state machine properties unit-pinned: visibility reclaim consumes an attempt, dead-letter moves, no-stranded-rows sweep, handle-op validity predicate, late-heartbeat boundary
  • Equal-jitter curve + opts resolution pinned to the identical numeric behavior the suite's equivalence rows assert (per the engine-side pin posture used for the cap legs)
  • Numeric-domain guards (extents) at entry — pinned
  • cargo check --target wasm32-unknown-unknown -p alkstore-mem clean; workspace gates green

References

  • docs/architecture/queues.md; core-contract.md §queues
  • docs/architecture/decisions/010-queue-semantics-depth.md; 019-mechanism-handle-surfaces.md §1/§3; 020 §1–§3; 023 §2
  • docs/architecture/engine-mem.md §Mapping the contract (queues row)
  • tasks/pg-engine-queues.md (the structural twin's task of record — the wave-4 rhythm and the resolution rule set)

Notes

Filled by implementation agent, 2026-10-10.

  1. The implementation already landed in the seam task — mem-engine-seam-tx Note 1 records that the mem engine's full Store surface (including the MemQueueHandle/MemJobHandle mechanism in alkstore-mem/src/queue.rs and mem's third-owner resolution.rs arithmetic) is all-or-nothing per trait impl, so this task found its mechanism implemented. Its work was the coverage reconciliation against the description's pin list plus the missing engine-side unit pins — the same disposition the notify/listen twin task recorded (mem-engine-notify-listen Notes 1–3).
  2. Coverage reconciliation: already pinned by the seam task's suite in queue.rs/resolution.rs/tx.rs — the full lifecycle + stamp resolutions (300/3/5/none + per-job override, resolved values shown by get_job), claim ordering (priority DESC → ready-time ASC → id ASC), extent clamps (n = 0/n < 0 → empty at the entry, before any effect), empty-worker_id rejection, the uniform validity predicate (lapse refuses all ops, refusals leave the row intact), late-heartbeat refusal + absolute-reset renewal (with claimed_at/attempts untouched), reclaim-consumes-an- attempt + the loser's ack refusing (the dual-execution honesty), retry/fail default strings ("max attempts exceeded" / "failed")
    • caller strings via the exhaust path, dead-row get_job visibility with last_error/died_at, the both-states no-stranded-rows sweep + retention (incl. forever-None) + queue scoping, the tx/no-handle stamp shapes (plain 300/3/5/none, outbox 60/5/5 in tx.rs), and the resolution legs (delay-wins- over-run_at, run_at literal, neither→now, negative-delay total, relative-expires) + the base-5 curve range/cap/spread pins in resolution.rs.
  3. What this task added — the six unpinned shapes: (a) concurrent_claims_hand_out_work_exactly_once (8 racing workers over 4 rows via spawned claim_batch handles: every row exactly once — the guarded-section claim-vs-claim atomicity pin the description's "claim race is in-process" bullet asks for); (b) claim_vs_expiry_is_deterministic_at_the_row_boundary (at now == claim_expires_at: no reclaim (d < now false), the holder's heartbeat/ack still valid (d >= now) — the two predicates' boundary coherence; one second past: reclaimable with its attempt consumed; the lapsed id silently uncounted by ack_batch); (c) the_equal_jitter_curve_mirrors_the_suite_rows_ numeric_bounds (base 2, max_attempts 7 — the suite row's own shape: three claim→retry(None) cycles asserting [1,2]/[2,4]/[4,8] read back through get_job's run_at, exact under the frozen clock; plus the override legs Some(5) verbatim and Some(0) ready-now per the boundary-total rule) — the engine-side pin of AC3's "identical numeric behavior"; the cap leg stays pinned in resolution.rs's base-5 tests per suite-opts-backoff-rows's recorded disposition; (d) backoff_base_two_lands_in_the_suite_ rows_exact_bounds in resolution.rs — the same base-2 bounds at the arithmetic function itself; (e) the_pre_claim_sweep_dead_ letters_exhausted_reclaimables (the D-12 shape: a lapsed row with its budget spent claims nothing and dead-letters with the exhaust string at the next claim — never strands claimable); (f) cancel_is_unconditional_across_states (pending + processing deleted, row-less Ok(false), the holder's post-cancel ack refusing — the not-an-interrupt honesty).
  4. No production code changed — the seam task's realization was verified correct as-found; this task is tests only (+5 in queue.rs, +1 in resolution.rs, 97 mem tests total). One test-authoring note: heartbeat's absolute reset moves the deadline (now + extend), so a boundary-reclaim leg staged after a heartbeat on the same row reads the shifted deadline — the boundary test stages its legs on fresh rows to keep each observation exact.

Summary

Filled on completion, 2026-10-10.

Completed the mem engine's queues task against the seam task's foundation: reconciled the description's pin list against alkstore-mem/src/queue.rs + resolution.rs + tx.rs's existing suite (13 pre-existing queue mechanism tests + the resolution and tx stamp pins), found the full Queue/JobHandle surfaces and nearly all ADR-010 properties already pinned, and added the six gaps: the in-process claim-race atomicity pin (8 concurrent workers, exact-once handout over the guarded section), the claim-vs-expiry boundary determinism pin (the d < now / d >= now predicates' edge-second coherence + the lapsed ack_batch non-count), the engine-side mirror of the suite's backoff_curve_equivalence row (base 2 → [1,2]/[2,4]/ [4,8] end-to-end through claim→retry(None)→get_job.run_at with the Some(5)/Some(0) override legs, and the same bounds pinned at backoff_delay_s itself; the 1-hour cap stays in resolution.rs's base-5 pins per the recorded suite disposition), the D-12 pre-claim exhausted-reclaimable dead-letter sweep, and the both-states unconditional cancel. Acceptance criteria: full surfaces ✓ (seam task, lifecycle test), ADR-010 properties ✓ (seam tests + the new boundary/reclaim/sweep pins), curve + opts resolution numeric equivalence ✓ (new base-2 pins), extent guards ✓ (seam clamp test), gates ✓. Verified: cargo test -p alkstore-mem 97/97 (91 + 6 new, new tests green solo ×3), workspace cargo test green (core 25, suite-harness 3, mem 97, pg 124+25+9, SQLite 191+25), cargo clippy --all-targets -- -D warnings clean, cargo fmt --check clean, cargo check --target wasm32-unknown-unknown -p alkstore-mem clean — the wave-6 wasm gate stays green.