feat(store): the sweep's durable form — staged candidates, next-sweep execution

The first durable delete window (single-node), beside the inline form:

- sweep_window branches on the delete window's host — the kv-engine
  enum (KvEngine::Sqlite -> durable; KvEngine::Mem -> inline body,
  extracted verbatim so the wave-2 mem/local suites stay green
  unchanged)
- the durable form, per sweep under the lease and after the shared
  protect consult: window_take(now) executes the overdue set with
  execution-time re-arbitration through the same arbitrated_delete
  path (consult_protect = false — the sweep's protect consult covers
  its execution phase; vanished candidates settle into the cancelled
  count), then marks unchanged, then stages the candidate-dead set at
  visible_after = mark_end + window (the constructor-tunable delay;
  ZERO = execute-on-next-sweep)
- report counts exact relative to the schedule (staged-this-sweep vs
  executed-this-sweep); aborts execute nothing, stage nothing, delete
  nothing
- unit tests: two-sweep exact-count shapes at delay 0, future-delay
  staging with idempotent re-stage, the race across the delay (put
  during the window survives; the unpinned twin deletes), the
  vanished-candidate form, and the three abort shapes on sqlite

Verification: cargo test (default 69 lib tests), --all-features (124),
--no-default-features --features mem (101), clippy -D warnings
(default and --all-features), cargo fmt --check — all green.
This commit is contained in:
glm-5.3-flash committed 2026-10-12 05:05:58 +00:00
1 parent 2993df0e36
commit fb8326c4d5
3 files changed
+737 -42

No files matched your search

+67 -2
View File
@@ -1,7 +1,7 @@
---
id: store-sweep-durable-window
name: the sweep's durable-window form — staged candidates, next-sweep execution
status: pending
status: completed
depends_on: [store-sqlite-construction]
scope: moderate
risk: medium
@@ -94,6 +94,71 @@ window, single-node:
> To be filled by implementation agent
The durable form's shape as it landed:
- **The branch is the kv-engine enum match itself** (`sweep_window`'s
`match &self.kv`): `KvEngine::Sqlite(engine)` runs the durable form,
`KvEngine::Mem(_)` runs the inline form. The two durable helpers are
generic over `W: EngineWindowState` (the seam has `Backend`
associated types, so a `&dyn` handout is not object-safe — the
concrete `&SqliteBackend` passes directly). Wave 5's pg-lo slots one
more match arm in.
- **The inline body was extracted verbatim** into
`sweep_window_inline` (the wave-2/all-features mem+local suites run
green unchanged); the protect consult stays in `sweep_window` above
the branch, shared by both forms.
- **The protect-consult posture on overdue execution is recorded as a
doc point** on `execute_overdue_window`: the consult already ran for
the sweep (its abort, `ProtectFailed`, precedes execution) and its
coverage extends to the executor phase, so the overdue
re-arbitrations carry `consult_protect = false` — the inline form's
same posture.
- **Malformed window rows are fatal** (`Error::KeyInvalid` via the
shared `typed_keys` boundary) — the pool-list discipline extended to
the window read.
- **`visible_after` overflow is `Err(Error::Io)`** (checked clock add);
no panics on the library path.
- **Report shape**: `candidates_staged` = staged this sweep;
`deleted`/`cancelled_by_arbitration` = the overdue set executed this
sweep (a vanished candidate settles into the cancelled count). The
`sweep()` doc states the exact-count invariants relative to each
form's schedule.
## Summary
> To be filled on completion
> To be filled on completion
The sweep's durable SQL-backed form landed beside the inline form in
`src/store.rs` (the first durable delete window, single-node; ADR-008
§2 staged re-arbitrated delete, ADR-012 §5/§6.7 the-executor-is-the-
next-sweep):
- `sweep_window` branches on the delete window's host — the kv-engine
enum (`KvEngine::Sqlite` → durable; `KvEngine::Mem` → inline,
extracted verbatim). Under the lease: the protect consult (abort
`ProtectFailed` precedes any execution), then for the durable form
**execute overdue** — `window_take(now)` → execution-time
re-arbitration through `arbitrated_delete(key, false)` in
~100-key batches (vanishes count as cancelled) — then **mark**
(unchanged), then **stage** at `visible_after = mark_end + window`
(the constructor-tunable delay; `Duration::ZERO` = execute-on-next-
sweep). Report counts exact relative to the schedule.
- Abort shapes unchanged and unit-tested on sqlite:
`NoLivenessSources`/`ProtectFailed`/`SweeperLock` abort before
anything is executed or staged; the `ProtectFailed` test proves the
overdue set is left waiting in the window (abort precedes
`window_take`).
- Unit tests (`sqlite_window_tests`): two-sweep exact counts at delay 0
(sweep 1 stages both tiers' dead entries without executing; sweep 2
executes exactly the staged set), future-delay staging (no execution
before the window closes; re-stage is idempotent), the race across
the delay (a put landing during the window survives via the
execution-time re-arbitration; the unpinned twin deletes; the
cancelled candidate re-stages and executes once unpinned), the
vanished-candidate form, and the three abort shapes.
Verification: `cargo test` (69 lib + 5 integration, default), 124
tests `--all-features`, 101 tests `--no-default-features --features
mem`, `cargo clippy --all-targets -- -D warnings` (default and
`--all-features`), and `cargo fmt --check` all green; the mem/local
wave-2 suites pass unchanged (the inline form untouched).