Contract-suite queue-depth rows (task suite-queue-depth-rows): the job-handle validity predicate row (in-window heartbeat/ack landing, late-heartbeat/post-lapse-ack/retry/fail refusals past a 1 s stamp with the row untouched, ack_batch per-id predicate live-1/lapsed-0/nonexistent-0, fail(None)→"failed" and retry-at-budget→"max attempts exceeded"), the ADR-010 depth row (reclaim consumes an attempt with claimed_at/deadline refreshed and the original holder refusing, reclaim-exhaustion dead-lettering with the pre-claim sweep — get_job-visible "max attempts exceeded"+died_at, cancel unconditional delete with the not-an-interrupt refusal shape plus pending/dead/missing arms), and the no-stranded-rows sweep row (both states move with "expired", unexpired/never-expiring untouched, retention TTL enforcing with the moved+deleted sum, None=forever) — each version-stamped per the suite convention (ADR-010 §1–§5, ADR-019 §3, ADR-008 §5), wired into both engines' suite targets (SQLite tokio tests, pg harness_row!s). M-1's retention-failure pin dispositioned engine-side per the task's honest call: the storage-level DELETE failure is not injectable through the contract surface, so it lands in the SQLite substrate's trigger seam (sweep_rolls_back_the_retention_half_with_the_move — the move half rolls back with the retention half), with the pg twin verified structurally (both halves inside in_tx's frame) and the disposition recorded in the task Notes. Verified: sqlite suite 13/13, pg suite 13/13 vs harness twice (postgres/poc@:15432), substrate sweep-rollback tests 4/4, workspace build/test green, clippy -D warnings, fmt clean
This commit is contained in:
1 parent
250511d480
commit
5c9ae6a7fc
6 files changed
+746
-15
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: suite-queue-depth-rows
|
||||
name: Contract-suite rows — job-handle validity predicate + ADR-010 queue depth
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: moderate
|
||||
risk: medium
|
||||
@@ -63,16 +63,16 @@ outcomes never timing values. No two-task races.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Three rows exist in `properties.rs`, `pub async fn(&dyn
|
||||
- [x] Three rows exist in `properties.rs`, `pub async fn(&dyn
|
||||
StoreFactory)`, version-stamped (ADR-010 §2/§5, ADR-019 §3,
|
||||
ADR-008 §5 as applicable; stamps cite ADR § per the
|
||||
`version_stamp.rs` convention)
|
||||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [ ] SQLite column green server-less; pg column green against the
|
||||
- [x] Rows wired into both engines' `contract_suite.rs` targets
|
||||
- [x] SQLite column green server-less; pg column green against the
|
||||
harness server
|
||||
- [ ] The M-1 retention-failure pin's location decided and recorded in
|
||||
- [x] The M-1 retention-failure pin's location decided and recorded in
|
||||
Notes (suite row or engine-side, with the reason)
|
||||
- [ ] `cargo test -p alkstore-sqlite -p alkstore-postgres` green
|
||||
- [x] `cargo test -p alkstore-sqlite -p alkstore-postgres` green
|
||||
(pg rows skip cleanly server-less); clippy `-D warnings`; fmt clean
|
||||
|
||||
## References
|
||||
@@ -87,8 +87,93 @@ outcomes never timing values. No two-task races.
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
> Decisions of record the description didn't pin:
|
||||
|
||||
- **M-1 retention-failure disposition: pinned engine-side (SQLite
|
||||
substrate), not as a suite row.** The retention-DELETE failure is a
|
||||
storage-level fault with no injection seam through the contract
|
||||
surface (`StoreFactory` + the contract traits offer no way to fail
|
||||
one statement of one engine's sweep while the store stays live) — so
|
||||
the pin landed as `sweep_rolls_back_the_retention_half_with_the_move`
|
||||
in the SQLite substrate's queue test seam (a trigger-raised ABORT on
|
||||
`__alkstore_dead` DELETE — the exact mechanism review 001 §4
|
||||
sketched): a sweep with both an expired live row and an aged-out
|
||||
dead row fails entire (the move half's effects roll back with the
|
||||
retention half; unblocked, the re-sweep accounts 1 moved +
|
||||
1 retention-deleted — the sum). The pg twin was **not** given a
|
||||
live-injected leg: the engine has no client-side fault seam either,
|
||||
and its sweep frame is structurally the same guarantee verified by
|
||||
code-read — both halves (move + retention) run inside one `in_tx`
|
||||
`BEGIN…COMMIT/ROLLBACK` frame (queue.rs `sweep_expired`), so a
|
||||
retention-DELETE failure rolls the move back via the frame's error
|
||||
arm. Review gate should audit this disposition: pg's leg rests on
|
||||
frame structure, not a live fault test.
|
||||
- **The exhaustion-string pin is split across the two rows by
|
||||
trigger**: `fail(None)` → `"failed"` and retry-at-exhausted-budget →
|
||||
`"max attempts exceeded"` pin in `job_handle_validity_predicate`
|
||||
(both reachable without a lapse); *reclaim*-exhaustion →
|
||||
`"max attempts exceeded"` pins in `queue_depth_reclaim_and_dead_letter`
|
||||
where that trigger actually occurs (the pre-claim sweep at the
|
||||
budget-spent claim). The row-1 text's single-line mention of
|
||||
exhaustion thus rides the row that owns the exercising path.
|
||||
- **The `ack_batch` leg carries its own queue and a renewal, not a
|
||||
post-lapse fresh claim** — any later claim in a queue holding lapsed
|
||||
rows reclaims them FIFO-first (the predicate makes a lapsed claim
|
||||
reclaimable, so a "fresh" claim in the same queue picks the lapsed
|
||||
row ahead of the fresh one; found live on the first run against the
|
||||
real engine — the wave-3 lesson repeating). The leg therefore keeps
|
||||
one id's claim live through a heartbeat renewal while its sibling
|
||||
lapses on the 1 s stamp, in a dedicated short-visibility queue: three
|
||||
ids (live / lapsed / nonexistent), count 1, the lapsed row still
|
||||
`processing` afterward.
|
||||
- **cancel legs extend the task's list slightly in the same row**:
|
||||
cancel of a *dead* id returns `false` and deletes nothing (outside
|
||||
cancel's live-row reach), alongside the pinned processing-cancel
|
||||
(holder's next op refuses the lapse shape), pending-cancel
|
||||
(never claimable), and missing-id `false` legs.
|
||||
- **Timing posture realization**: sleeps are 2.5 s for 1 s
|
||||
visibility/claim stamps and 3.5 s for `expires: Some(2)` stamps (the
|
||||
extra second keeps the claim window — `expires_at > now` at claim —
|
||||
deterministic against second-resolution clocks); every assertion is
|
||||
a state outcome, none a duration. The deadline-boundary legs pin
|
||||
refusal-state both sides of the lapse (in-window ops land;
|
||||
post-lapse ops refuse) without asserting the boundary instant.
|
||||
- **Row 2's reclaim leg also pins the original holder refusing after
|
||||
the reclaim** (heartbeat → `false` with the row now `w2`'s) — the
|
||||
reclaim side of the same predicate, in the row that owns reclaims.
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
> Landed: the three queue-depth contract rows in
|
||||
> `alkstore-contract-suite/src/properties.rs` —
|
||||
> `job_handle_validity_predicate` (in-window heartbeat/ack landing;
|
||||
> late-heartbeat, post-lapse ack, retry, and fail refusals past a 1 s
|
||||
> stamp with the row untouched; `ack_batch` per-id predicate — live
|
||||
> 1, lapsed 0, nonexistent 0; `fail(None)` → `"failed"` and
|
||||
> retry-at-budget → `"max attempts exceeded"`),
|
||||
> `queue_depth_reclaim_and_dead_letter` (reclaim consumes an attempt
|
||||
> with `claimed_at`/deadline refreshed; original holder refuses;
|
||||
> reclaim-exhaustion dead-letters with the pre-claim sweep — dead row
|
||||
> `get_job`-visible with `"max attempts exceeded"` + `died_at`;
|
||||
> cancel unconditional delete with the not-an-interrupt refusal shape,
|
||||
> pending/dead/missing arms), and `sweep_no_stranded_rows`
|
||||
> (pending-expired + processing-expired both move with `"expired"`,
|
||||
> unexpired/never-expiring untouched; retention TTL deletes with the
|
||||
> moved+deleted sum; retention `None` = forever). All version-stamped
|
||||
> (ADR-010 §1–§5, ADR-019 §3, ADR-008 §5), re-exported from the suite
|
||||
> lib, and wired into both engines' `contract_suite.rs` targets
|
||||
> (SQLite three new `#[tokio::test]`s; pg three `harness_row!`s).
|
||||
>
|
||||
> M-1 follow-up: the sweep's retention-half rollback pinned in the
|
||||
> SQLite substrate (`block_dead_deletes` trigger seam +
|
||||
> `sweep_rolls_back_the_retention_half_with_the_move`) — see Notes for
|
||||
> the disposition.
|
||||
>
|
||||
> Verified: workspace `cargo build` and `cargo test` green (25 core +
|
||||
> 189 + 121 SQLite + 121 pg lib + 9 schema + harness crates);
|
||||
> SQLite contract suite 13/13 (three new rows, 8.5 s total); pg
|
||||
> contract suite 13/13 against the harness server (`pglo-poc` :15432,
|
||||
> `postgres/poc`/`blobs`) twice consecutively (15.3 s each — the sleep
|
||||
> legs really ran, not skipped); the substrate rollback tests 4/4;
|
||||
> clippy `--workspace --all-targets -D warnings` clean;
|
||||
> `cargo fmt --check` clean.
|
||||
Reference in new issue
Block a user