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.
142 lines
7.4 KiB
Markdown
142 lines
7.4 KiB
Markdown
---
|
|
id: engine-sqlite-window
|
|
name: sqlite engine-state window family — the durable single-node delete window
|
|
status: completed
|
|
depends_on: [engine-sqlite]
|
|
scope: narrow
|
|
risk: low
|
|
impact: component
|
|
level: implementation
|
|
tags: [wave-3, engines, sqlite, gc]
|
|
---
|
|
|
|
## Description
|
|
|
|
Implement the engine-state companion seam's **window family** on the
|
|
sqlite backend (`src/sqlite.rs` or its seam module): the
|
|
`EngineWindowState` impl per ADR-010 §2 **as reduced by ADR-012 §5** —
|
|
the window family only. The pin clauses and the sweep-lock body are
|
|
**dropped dead machinery** for sqlite (ADR-012 §5's table: pin clauses
|
|
**no**, sweep-lock clause **no** — the single-flighter is the
|
|
in-process lock; a sqlite fleet is a recorded non-option, ADR-010 §3).
|
|
Nothing of `EngineFleetState` may exist on this engine.
|
|
|
|
- **`ensure_gc_schema`** — creates **`gc_window` only**
|
|
(`gc_window (key BLOB PRIMARY KEY, visible_after INTEGER)`; the
|
|
timestamp encoding — `SystemTime` ↔ integer, sub-second precision
|
|
for the test tunings — is a recorded design point). Never `gc_pins`;
|
|
the embedder's liveness table is never created here (ADR-008's
|
|
schema-ownership rule — the sweep reads it and never owns it).
|
|
Called at store construction by the store core
|
|
(`store-sqlite-construction` wires the call); the clause body is
|
|
engine-side.
|
|
- **`window_stage`** — stage candidates into the durable window,
|
|
**idempotent per `(key, deadline)`** (`INSERT OR IGNORE` semantics —
|
|
the body's discipline; the store stages each candidate once per
|
|
sweep, and a re-stage of the same key at a new deadline updates the
|
|
row). Virgin store: tolerated (the table exists from construction;
|
|
a no-tables-yet database stages lazily or errors nothing — the
|
|
Backend trait's engine posture carries here).
|
|
- **`window_take(now)`** — take overdue candidates
|
|
(`visible_after <= now`): the rows **leave the window table and
|
|
return as keys** for the calling sweep to re-arbitrate and delete —
|
|
the executor is the next sweep (ADR-012 §6.7). Empty when nothing is
|
|
overdue; virgin store: empty. The take is one atomic step (selected
|
|
rows removed and returned together — a row is never handed out
|
|
twice).
|
|
- **Contract tests** per the applicability row (ADR-012 §5's table is
|
|
the contract-test matrix): the schema creates the window table only
|
|
(assert `gc_pins` does not exist); stage idempotence per
|
|
`(key, deadline)` incl. the re-stage-at-new-deadline form; take
|
|
returns exactly the overdue set, removes it, leaves future rows;
|
|
empty/virgin shapes; the timestamp encoding's boundary
|
|
(`visible_after == now` is overdue — `<=` per the seam doc).
|
|
|
|
The store core drives these clauses (`store-sweep-durable-window`);
|
|
this task ships the clauses and their engine-level contract tests.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] The sqlite backend implements `EngineWindowState` — the window
|
|
family only; no pin-clause or sweep-lock body exists anywhere in
|
|
the sqlite engine
|
|
- [x] `ensure_gc_schema` creates `gc_window` only; `gc_pins` is never
|
|
created by sqlite (contract-tested)
|
|
- [x] `window_stage` is idempotent per `(key, deadline)`; a re-stage
|
|
at a new deadline updates the row
|
|
- [x] `window_take` returns exactly the overdue set (`<= now`
|
|
boundary), removes it atomically, leaves future rows; empty on
|
|
none/virgin
|
|
- [x] Clause contract tests green; `cargo test` (default) green;
|
|
`cargo clippy --all-targets -- -D warnings` and
|
|
`cargo fmt --check` green
|
|
|
|
## References
|
|
|
|
- src/gc.rs (`EngineWindowState` — the seam contract and its clause docs)
|
|
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state.md §2
|
|
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §5 (the applicability table; the sqlite reduction), §6.7 (the executor)
|
|
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md §2 (the durable window's semantics)
|
|
- docs/architecture/decisions/005-pooled-cas-and-gc-mechanism.md (the delete-window protocol)
|
|
|
|
## Notes
|
|
|
|
- The `EngineWindowState` impl lives in `src/sqlite.rs` after the
|
|
`Backend` impl, as `impl EngineWindowState for SqliteBackend`. The
|
|
pin clauses and the sweep-lock body are absent by construction —
|
|
nothing of `EngineFleetState` is implementable or referenced on this
|
|
engine (ADR-012 §5's sqlite row: the single-flighter is the
|
|
in-process sweep lease on `GcHome`, a sqlite fleet is a recorded
|
|
non-option).
|
|
- **Timestamp encoding (recorded design point):** `visible_after`
|
|
travels as a signed i64 nanosecond count from the Unix epoch —
|
|
sub-second precision preserved for the test tunings, negatives for
|
|
pre-epoch instants, full i64 range. Helpers `time_to_nanos` /
|
|
`nanos_to_time` sit beside `size_u64` in `src/sqlite.rs`.
|
|
- **Staging discipline:** one `prepare_cached` upsert per row —
|
|
`INSERT … ON CONFLICT(key) DO UPDATE SET visible_after =
|
|
excluded.visible_after`. The row's primary key is the window
|
|
candidate's key, so a same-deadline re-stage is idempotent (one row,
|
|
unchanged value) and a new-deadline re-stage updates the row rather
|
|
than duplicating it. `INSERT OR IGNORE` alone would have made the
|
|
new-deadline update a no-op, so the upsert carries both clauses
|
|
of the idempotence contract.
|
|
- **Take atomicity:** `DELETE FROM gc_window WHERE visible_after <= ?
|
|
RETURNING key` — one statement on the single pooled connection, so
|
|
selection and removal are one atomic step (a row is never handed out
|
|
twice); `RETURNING` order is unspecified, so the returned keys are
|
|
sorted for determinism.
|
|
- **Virgin-store tolerance split per the sqlite engine's recorded
|
|
posture:** `window_take` is a read — a no-tables-yet database maps
|
|
to empty via the existing `is_no_such_table` tolerance; `window_stage`
|
|
is a write — a tableless database surfaces the engine error rather
|
|
than silently creating schema (the constructor-vs-lazy design point:
|
|
store construction always wires `ensure_gc_schema`, so the table
|
|
exists whenever the engine is in store service; the lazily staged
|
|
option was rejected as contradicting that recorded point).
|
|
- Schema timing unchanged from the pool table's recorded design point:
|
|
the backend constructor never creates `gc_window` itself — the store
|
|
construction call (`store-sqlite-construction`) wires
|
|
`ensure_gc_schema`, which is idempotent and creates `gc_window`
|
|
only. The clause-level tests use a `new_window_backend()` fixture
|
|
that mirrors that wiring.
|
|
- The boundary contract (`visible_after <= now`, inclusive) is
|
|
contract-tested, including the nanosecond slack row (visible at
|
|
`now + 1ns` stays).
|
|
|
|
## Summary
|
|
|
|
The sqlite engine-state seam's window family landed: `impl
|
|
EngineWindowState for SqliteBackend` in `src/sqlite.rs` —
|
|
`ensure_gc_schema` (creates `gc_window` only, never `gc_pins`,
|
|
contract-tested through `sqlite_master`), `window_stage` (key-keyed
|
|
upsert; idempotent per `(key, deadline)`, new-deadline re-stage
|
|
updates the row), and `window_take` (`DELETE … RETURNING` — one atomic
|
|
overdue take with the `<= now` boundary, future rows left in place).
|
|
Signed-nano timestamp encoding recorded; virgin-store tolerance split
|
|
read/write per the sqlite engine's posture. Six clause contract tests
|
|
added (schema window-only, staging idempotence + deadline update,
|
|
overdue-exact take + future rows left, the `<=` boundary with
|
|
nanosecond slack, and both virgin shapes). Verification: `cargo test`
|
|
green (58 lib + 5 integration), `cargo clippy --all-targets -- -D
|
|
warnings` green, `cargo fmt --check` green. |