Files
alkblobs/tasks/review-wave-4.md
T

211 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: review-wave-4
name: Wave 4 review gate — the postgres engine vs the contract
status: completed
depends_on: [suite-column-postgres]
scope: narrow
risk: low
impact: phase
level: review
tags: [wave-4, review]
---
## Description
The wave-4 review gate — the engine-wave review shape
(docs/plans/implementation.md §Review gates: "engine-vs-contract
conformance"), before wave 5 decomposes. The gate reads the
implementation against the pinned ADR text line by line (the
wave-1/2/3 gates' method).
Gate checks:
- **Engine-vs-contract conformance**: the suite column green against
dockerized postgres (server-less runs skip cleanly — a skip never
substitutes for acceptance); the engine-state clause families
contract-tested per the applicability row (ADR-012 §5's postgres
row — every family yes); virgin-store semantics; the `size`
metadata-only discipline (the size-column probe never loads the
value blob).
- **The postgres posture vs ADR-007**: the B5 deltas carried as
requirements, not hedges — the `synchronous_commit` parity knob
(configurable, per-connection wiring, default `on`); pooled
connections with per-connection prepared-statement discipline
(statements die with sessions); autovacuum + fillfactor tuning
stated in the module doc; the ~40× small-tier storage overhead
accepted and documented; the B5 tuning numbers recorded as
POC-derived posture defaults (ADR-007 §6's deferred-cost pattern).
- **The sweep-safety proof vs ADR-007 §5**: `list()` complete and
well-formed as a stable MVCC-snapshot cursor over a vacuuming
table; the batch-delete helper (`key = ANY($1)`) contract-tested;
the exact-count sweep-outcome rows green against the server.
- **The seam families vs ADR-010 §2 / ADR-012 §4/§5**: the full
clause families on postgres (the fleet host) — `ensure_gc_schema`
(both tables, the embedder's liveness table never created),
`window_stage`/`window_take` (the sqlite semantics' dialect twin),
the joint tx ownership rule honored in the `JointTx` shape (the
store holds; the engine contributes), `pin_commit`'s atomicity
(publish + protection in one step, zero residue on failure),
`pin_renew`'s never-resurrect semantics, `pin_check`'s
classification-only discipline, `arbitrate`'s finger-lock fold,
the advisory lock's ADR-013 §2 lease semantics (atomic
test-and-set, session-scoped holdership, same-connection
release). **Fleet activation absent by construction** — no
`fleet:` parameter, no store-core fleet wiring (wave 6); the
clause bodies exist and are contract-tested per applicability.
- **The non-fleet posture vs ADR-012 §3**: the constructors are
implicitly `fleet: false` — the in-process protocol verbatim; the
recorded posture names wave 6's slotting-in as a non-event.
- **Async/blocking discipline**: tokio-postgres natively async — no
`spawn_blocking` seam (the sqlite discipline does not carry); no
blocking I/O on the async path; the pool is the concurrency
posture; no `unwrap`/`expect` outside tests.
- **Named design points audited as resolved** (each recorded in its
task's Notes): the schema name + quoted-identifier boundary; the
pool default size; the prepared-statement discipline; the DSN +
opts constructor shape; the virgin-store tolerance split; the
list-cursor fetch shape; the timestamp encoding; the `JointTx`
concrete type; the `pin_renew` successor-time source; the lock
key + scope; the lease's connection holding.
- **Family conventions**: no comments in code (doc comments on public
API fine), thiserror, tokio-only async, no unwrap/expect outside
tests, naming, one-module-per-file re-export shape, the
`#![allow(dead_code)]` scope notes matching their remaining scope
(the batch-delete helper's wave-6 note intact).
- **Gates green**: `cargo test` (default), `cargo test --features
mem`, `cargo test --features postgres` (server-less: skipping),
`cargo test --all-features`, `cargo check --no-default-features
--features postgres` (the pg-pure build), `cargo clippy
--all-targets -- -D warnings` (default + `--features postgres` +
`--all-features`), `cargo fmt --check`.
Findings are fixed inline where cheap, or recorded as wave-5
amendments where they shape the next decomposition. The gate's
completion is what unblocks wave 5's decomposition.
## Acceptance Criteria
- [x] Every gate check above read and verified against the cited ADR
text (not from memory of it)
- [x] Named design points each decided, recorded, and consistent with
the specs
- [x] Findings fixed inline or recorded as wave-5 amendments with
owners
- [x] All gates re-run green after any fixes
- [x] Wave 5 decomposition unblocked (recorded in this task's Summary)
## References
- docs/plans/implementation.md (§Wave 4, §Review gates — engine wave
reviews)
- docs/architecture/decisions/007-two-kv-engines-sqlite-and-postgres.md
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §2
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §3/§4/§5/§6.6/§6.7
- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md §2/§4
- docs/architecture/store-api.md
- tasks/review-wave-3.md (the gate method; the structural twin's gate)
## Notes
> Gate completed 2026-10-12, the wave-1/2/3 method: every gate check
> read against the pinned text (ADR-007 §4/§5/§6, ADR-010 §2,
> ADR-012 §3/§4/§5, ADR-013 §2/§4, store-api.md, the seam trait in
> src/gc.rs), then verified against the landed diff (1efc46f…0fb05fc)
> and re-run gates. Findings fixed inline:
>
> - **PG-1 — in-instance sweep-lease reaping after session loss
> (fixed, ADR-013 §2).** `sweep_lock_acquire` answered `Ok(false)`
> from the mutex even when the held lease's session had died —
> blocking re-acquisition on the same backend until an explicit
> release, against the holdership rule ("holdership ends at …
> engine-session loss"). Fixed: the acquisition answers against a
> live session only (dead checkouts are reaped in-instance); new
> test `sweep_lock_in_instance_session_loss_reaps_the_lease`
> (terminates the lease's own held backend via
> `pg_terminate_backend` and re-acquires without a release). The
> foreign-session reaping test (the wave-4 test) is unchanged.
> - **PG-2 — the `PgJointTx` mid-flight-drop hazard (doc fix +
> amendment A1 below).** deadpool's `Fast` recycle never resets
> transaction state, so a `PgJointTx` dropped between
> `begin_joint_tx` and `pin_commit` returns a pooled *dirty*
> session to the pool. The clause bodies' own failure paths all
> ROLLBACK before surfacing (wave-4 contract holds — the zero-
> residue atomicity test proves it); the hazard is the store core's
> future use, where Rust `?` propagation between the two calls is
> the natural error shape. Not fixable inline without async-drop or
> a seam reshape — recorded on [`PgJointTx`]'s doc and as amendment
> A1 for wave 6.
> - **PG-3 — engine-level pg tests leaked per-test schemas on the
> shared harness db (fixed).** 380 `abt_*` schemas had accumulated
> (the suite column's `alkblobs_suite_*` teardown was verifiably
> clean — 0 residue — the leak was src/postgres.rs's + src/store.rs
> construction tests' per-test schemas). Added crate-internal
> `test_support::SchemaJanitor` (a drop-guard running
> `DROP SCHEMA IF EXISTS … CASCADE` synchronously over a std
> thread + a one-shot current-thread runtime — no async-drop
> exists), the `Harness` deref-guard wrapper for the engine tests',
> and `fresh_opts` carrying a janitor in the construction tests. A
> one-time manual cleanup dropped the legacy residue (the 16
> hostile-name stragglers dropped via `quote_ident` — incidentally
> exercising the boundary). Post-fix: **0 schemas residue** after
> full `--features postgres` and `--all-features` runs.
>
> Amendments for the next waves (owners recorded here, no spec text
> changed this gate):
>
> - **A1 (wave 6, owner: the fleet-GC mechanism task)** — the joint-tx
> drop discipline: the store core's joint-tx driver owes `pin_commit`
> on **every** path from `begin_joint_tx` onward (PG-2). Design it
> catch-propagate-terminate, or grow an explicit abort verb via a new
> ADR (the seam shape is pinned; `pin_commit`'s terminal-consume
> reading stays).
> - **A2 (wave 5, owner: the pg-lo engine task)** — pg-lo's engine
> tests share the `pglo-poc` cluster: adopt `SchemaJanitor` (or the
> column's own teardown pattern) from the start — the wave-4 lesson
> is not to re-learn it.
>
> Minor observations, no action: `sweep_lock_acquire`'s mutex check is
> not atomic with its checkout (two racing acquires both check out;
> the advisory lock itself arbitrates and exactly one stores its
> checkout) — sound by server arbitration. `list` rides the collected
> query rather than `query_raw` — the recorded design point; the
> bounded-memory claim holds for the keys-only materialization.
>
> Verification (post-fixes, all live unless noted): `cargo test`
> 69/19/3 ok; `cargo test --features mem` 124/54/4 ok; `cargo test
> --features postgres` 103/36/3 ok against the pglo-poc server, 51
> clean skip notes server-less; `cargo test --all-features` 158/71/4
> ok against the server; `cargo test --no-default-features
> --features postgres` (pg-pure) 76/19/3 ok; clippy `-D warnings`
> green (default / --features postgres / --all-features / pg-pure);
> `cargo fmt --check` green; `cargo doc --no-deps` clean; shared-db
> schema residue 0.
## Summary
> Completed 2026-10-12 (wave 4). The engine-wave gate read the landed
> postgres engine against the contract line by line and found it
> conformant: the suite column green against the harness server (36
> conformance rows live; server-less runs skip cleanly — a skip never
> substituted), every clause family of ADR-012 §5's postgres row
> contract-tested (both-tables schema + never-the-embedders,
> stage-idempotence/take-atomicity/the `<=` boundary, joint-tx
> atomicity with zero residue, `pin_renew` never-resurrect,
> two-variant `pin_check`, the folded `FOR UPDATE` arbitrate, the
> advisory lease's ADR-013 §2 semantics), the B5 deltas carried as
> requirements (the per-connection `synchronous_commit` knob tested,
> per-connection `prepare_cached`, fillfactor 90 stamped and
> reloption-asserted, autovacuum/40× overhead stated in the module
> doc), the `list` MVCC-snapshot cursor + the batch-delete helper
> exact-count-gated vs server, the schema-less virgin-store split,
> the size metadata-only EXPLAIN gate, and the non-fleet/posture
> recordings verbatim where wave 6 needs them. All 11 named design
> points audited as decided and recorded in the wave-4 task Notes.
> Three findings fixed inline (in-instance lease reaping — new test;
> the `PgJointTx` mid-flight-drop hazard — doc + amendment A1; the
> engine-test schema janitor — 380 leaked schemas → 0), two
> amendments recorded for waves 5/6 with owners (A1 the joint-tx
> drop discipline; A2 pg-lo adopts the janitor). All gates re-run
> green after the fixes (the verification block is in Notes). Wave 5
> (pg-lo large engine) decomposition is unblocked; its review gate is
> the engine-wave shape again.