Files
alkblobs/tasks/store-sweep-durable-window.md
T
glm-5.3-flash 97cdf682c4 docs(review): the wave-3 review gate — every gate check green
Reviewed the wave-3 commits (engine-sqlite, engine-sqlite-window,
store-sqlite-construction, store-sweep-durable-window,
suite-column-sqlite) against the cited ADR/spec text line by line:
ADR-003/007/008 §2, ADR-010 §2, ADR-012 §1/§5/§6.7, store-api.md,
implementation.md §Review gates — the engine-vs-contract conformance
(suite column green server-less in the default build; the window
family contract-tested per its applicability row; virgin-store
semantics incl. the no-tables-yet form; the size metadata-only
discipline proven index-riding), the durability posture (WAL +
synchronous=NORMAL verified at open; the single-writer ceiling posture,
not hedge), the durable sweep form vs ADR-008 §2/ADR-012 §6.7 (stage at
mark_end + window; next-sweep execution with raced execution-time
re-arbitration; schedule-relative exact counts; aborts stage and
execute nothing), the seam reduction (gc_window only; no pin rows, no
sweep-lock body, no EngineFleetState), and the blocking discipline
(all rusqlite in spawn_blocking). Named design points audited as
resolved; family conventions clean; no wave-4 amendments arise.

Inline fixes (review-of-record):
- src/gc.rs: the seam's window_stage doc retired the wave-1
  lazy-create hedge for the shipped constructor-at-open posture
  (a tableless database surfaces the engine error on stage;
  per-engine tolerances are each engine's recorded posture)
- tasks/engine-sqlite-window, store-sweep-durable-window,
  suite-column-sqlite: stray placeholder lines above the filled
  Notes/Summary removed (the wave-2 gate's same cleanup)
- tests/conformance/README.md: trailing newline added

Verification (all re-run green after the fixes):
- cargo test (default): 69 lib + 19 conformance + 3 git-interop
- cargo test --features mem / --all-features: 124 lib + 54
  conformance + 4 git-interop each
- cargo clippy --all-targets -- -D warnings (default + all-features)
- cargo fmt --check
- cargo doc --no-deps at the pre-existing warning baseline (26)

Wave 4 (postgres) decomposition is unblocked.
2026-10-12 05:22:29 +00:00

160 lines
8.4 KiB
Markdown

---
id: store-sweep-durable-window
name: the sweep's durable-window form — staged candidates, next-sweep execution
status: completed
depends_on: [store-sqlite-construction]
scope: moderate
risk: medium
impact: phase
level: implementation
tags: [wave-3, store, gc, sweep]
---
## Description
Grow the sweep's **SQL-backed form** (ADR-008 §2; ADR-012 §5/§6.7)
beside the inline form in `src/store.rs` — the first durable delete
window, single-node:
- **The branch**: the sweep runs the durable form when the kv engine
hosts the window (sqlite — the one-window-host posture); the inline
form is preserved **verbatim** for non-SQL engines (mem/local — the
wave-2 sweep tests stay green unchanged). The branch keys off the
engine representation (the `KvEngine` match), never off a flag.
- **The single-node posture is unchanged** (ADR-008 §2): pins, RAII
guards, and the arbitration lock stay in-process (`GcHome`) — only
the *persistence* of the delete window (the staged-candidate table)
is engine rows.
- **The durable form's protocol** (per sweep, under the lease):
1. **Execute overdue** (before/alongside marking — ADR-012 §6.7):
`window_take(now)` returns the overdue keys; each is
**re-arbitrated at execution time** through the same
`arbitrated_delete` path and deleted in batches. The protect-
consult posture on this path is a recorded design point (the
sweep's protect consult covers its execution phase — the same
reasoning that keeps the inline form's candidates at
`consult_protect = false`). A vanished candidate (deleted during
the delay by a direct delete) settles into the cancelled count —
the vanished-candidate accounting carries over.
2. **Mark**: enumerate the pool, compute the live set, compute the
candidate-dead set (unchanged).
3. **Stage**: `window_stage(candidates)` at
`visible_after = mark_end + window` — the constructor-tunable
delay (ADR-008 §2; the store carries the value its constructor
took).
4. **Report**: `candidates_staged` = candidates staged this sweep;
`deleted`/`cancelled_by_arbitration` = the executed overdue set.
**Exact-count invariants are asserted relative to this schedule**
(ADR-012 §6.7) — staged-this-sweep and executed-this-sweep are
different sets (the delay separates them); the suite's sqlite
column asserts the two-sweep shapes (`suite-column-sqlite`).
- **Abort shapes unchanged**: `NoLivenessSources`/`ProtectFailed`/
`SweeperLock` abort before anything is staged or executed (nothing
deleted, `Ok(SweepReport)`); operational failures (engine I/O) are
the `Err` half (ADR-012 §1).
- **The sweep-vs-put race under the durable form**: a put landing
during the window delay pins in-process (pin-before-publish); the
execution-time re-arbitration sees the pin and cancels the candidate
— the test-asserted property (a visible entry is never deleted while
liveness protects it; an in-flight put is never deleted by a sweep
started before it committed) holds **across the delay**, and is
unit-tested in that form.
- **Unit tests** in `store.rs`: two-sweep exact-count shapes (sweep 1
stages N at delay 0 → sweep 2 executes exactly N — deleted +
cancelled == N against the staged set); the race across the delay
(put during the window survives; the unpinned twin deletes); the
abort shapes on a sqlite store; the vanished-candidate form; the
inline form untouched (the mem/local suites stay green).
## Acceptance Criteria
- [ ] The sweep branches on window hosting; the inline form's behavior
and its wave-2 tests unchanged
- [ ] Durable form: overdue rows execute with execution-time
re-arbitration before/alongside marking; candidates stage at
`visible_after = mark_end + window`
- [ ] Report counts exact relative to the schedule (unit-tested
two-sweep shapes); aborts delete nothing
- [ ] The race property unit-tested across the delay (a put landing
during the window survives the next sweep's execution)
- [ ] The protect-consult posture on overdue execution recorded
- [ ] `cargo test` (default) green; `cargo clippy --all-targets -- -D
warnings` and `cargo fmt --check` green
## References
- src/store.rs (the sweep — `sweep`/`sweep_window`/`arbitrated_delete`)
- docs/architecture/decisions/005-pooled-cas-and-gc-mechanism.md (§Sweep — the protocol)
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md §2 (the staged re-arbitrated delete; the delay)
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §1 (abort-as-data), §5 (single-node posture unchanged), §6.7 (the executor is the next sweep)
- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md §2 (the lease semantics)
- src/gc.rs (`GcHome` arbitration; `EngineWindowState` the store core calls)
## Notes
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
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).