Five tasks against the wave-3 contract surface as reviewed (no amendments from the gate): - engine-postgres: the fleet-capable kv engine — the B5 row shape (fillfactor 90) in one engine-owned schema, the deadpool pool with per-connection prepared-statement discipline, the synchronous_commit parity knob wired per-connection, ON CONFLICT DO NOTHING CAS, the stable MVCC-snapshot list cursor, the batch-delete helper (key = ANY($1), consumer wave 6), the read-only virgin-store split - engine-postgres-fleet: the engine-state seam's full clause families (ADR-012 §5's postgres row — every family yes): gc_pins + gc_window, the joint tx ownership rule in the JointTx shape, pin_commit atomicity, pin_renew/pin_check/arbitrate, the pg advisory lock as the sweep lease's shipped provider; fleet activation stays wave 6 - store-postgres-construction: KvEngine::Postgres slots in, the KvCursor arm, the dual-tier/kv-only constructors (DSN + opts + threshold + window delay), the non-fleet posture recorded, the pg-pure build verified - suite-column-postgres: the first SKIP-posture column — the alkstore pg-factory pattern reused (fresh schema per open, CASCADE teardown, env-driven ALKBLOBS_PG_* settings), durable-schedule sweep rows, acceptance green against the harness server (pglo-poc :15432, verified live) - review-wave-4: the engine-wave gate (engine-vs-contract conformance) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 4
11 KiB
11 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| engine-postgres-fleet | postgres engine-state clause families — the fleet host's full seam | pending |
|
moderate | medium | component | implementation |
|
Description
Implement the engine-state companion seam's full clause families on
the postgres backend (src/postgres.rs or its seam module): the
EngineWindowState impl and the EngineFleetState impl per
ADR-012 §5's postgres row — every family yes (postgres is the
fleet host, ADR-012 §4). This is the seam implementation pattern
pg-lo's window family reuses (wave 5) and the fleet wave's mechanics
ride (wave 6).
EngineWindowState(the window family — active durably single-node, fleet-active when declared):ensure_gc_schema— createsgc_pins+gc_window(the fleet host's both-tables row; unconditionally — the applicability row does not condition on the fleet declaration):gc_pins (key bytea, owner bytea, expiry bigint)andgc_window (key bytea, visible_after bigint)per ADR-010 §2's schema.owneris the store-instance id (ADR-011's vocabulary); the pin row's identity is(key, owner)— protection composes by row. 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-postgres-constructionwires the call); the clause body is engine-side.window_stage— stage candidates into the durable window, idempotent per(key, deadline)(the upsert discipline — a same-deadline re-stage is idempotent, a new-deadline re-stage updates the row; the sqlite engine's recorded semantics).window_take(now)— take overdue candidates (visible_after <= now, the<=boundary the seam doc pins): the rows leave the window table and return as keys — one atomic step (selected rows removed and returned together; a row is never handed out twice). Empty when nothing is overdue; virgin store: empty.- Timestamp encoding (recorded design point): the sqlite
engine's signed i64 nanosecond count from the Unix epoch, carried
as
BIGINT— the seam's crate-wide encoding (sub-second precision for the test tunings, pre-epoch negatives, full i64 range); the sqlite engine'stime_to_nanos/nanos_to_timehelpers are reference, not a dependency (per-engine bodies, one encoding).
EngineFleetState(the fleet family — the postgres host's alone; fleet activation is wave 6: underfleet: falsethe store core never calls these — the clause bodies exist and are contract-tested per applicability):JointTx— the pg transaction the pooled client carries (recorded design point: the concrete pooled-transaction type and how it keeps its connection checked out for the tx's life;Sendper the trait bound). Ownership rule, pinned in the signature: the store core holds the tx frombegin_joint_txuntilpin_commitconsumes it; the engine contributes mechanics and never owns it (ADR-010 §2's ruling).begin_joint_tx— open the tx on a pooled connection, uncommitted untilpin_commit.joint_put— the entry half: the engine's put mechanics (theON CONFLICT DO NOTHINGCAS clause) executed inside the held tx, same first-writer-wins semantics as the publicput.pin_commit(tx, entries)— the joint terminal step: the pin-row inserts and the commit — publish and protection land in one step (ADR-005's pin-before-publish, fleet form). On any failure the entire tx aborts: no pin rows, no published entry, zero residue — the stage-hygiene invariant's tx form. Consumes the tx.pin_renew(key, owner)—truewhen a live (un-expired)(key, owner)row existed and its expiry moved to this call's successor time; an expired or absent row is never resurrected:false, nothing updated (ADR-012 §2's expiry semantics verbatim). Named design point — the successor-time source: the clause signature carries no expiry (ADR-010 §2: the TTL lives with the store's renewal schedule, the engine owns only the SQL) while the row update needs the new expiry — decide the wiring consistent with the seam doc's reading (the natural shape: the engine computesexpiry = now + TTLfrom the shipped pin-TTL constant — ADR-008's default 60 s, constructor-tunable when wave 6's fleet constructor lands — and the store owns the TTL/3 cadence, i.e. when renewal is called) and record it.pin_check(key)— classify against un-expired pin rows: any un-expired(key, *)row exists →PinState::Protected; elseUnprotected. The engine never decides deletion — it only classifies.arbitrate(key)— the full delete-time arbitration (ADR-008): the un-expired pin rows plus the embedder's registered liveness tables, under theSELECT … FOR UPDATEfinger-lock keyed at delete time, folded into one answer —true= liveness protects the key at this instant. The engine-side registration of named liveness-table references is part of this body (ADR-012 §6.6: the fleet form ofregister_liveness_sourceregisters a named table reference in the shared engine) — a crate-internal engine-side mechanism the store core's fleet registration verb forwards to (the facade verb itself is wave 6). The engine holds table references, never liveness semantics; the referenced tables are never created by the engine (the schema-ownership rule). Contract tests use a test-owned embedder table.sweep_lock_acquire()/sweep_lock_release()— the sweep lease's shipped provider: the pg advisory lock with ADR-013 §2's named exclusive semantics — atomic test-and-set (pg_try_advisory_lock), one sweeper per pool,Ok(true)= held,Ok(false)= contention (the second sweeper's sweep aborts withGcAbortCause::SweeperLockas report data — the store core's reading, wave 6). Named design points: session-scoped vs transaction-scoped (session-scoped — ADR-013 §2's holdership rule requires holdership to end at owner death/session loss, which only the session scope provides); the lock key (a fixed key, schema-qualified or namespaced so co-tenant engines on one instance never collide); and acquire/release on the same pooled connection (advisory locks are per-session — the lease must hold its client for its duration; the release-on-another-connection hazard is the recorded pitfall).
- Contract tests per the applicability row (ADR-012 §5's table is
the contract-test matrix): the schema creates both tables (and the
embedder's liveness table never); stage idempotence per
(key, deadline)incl. the re-stage-at-new-deadline form; take returns exactly the overdue set, removes it, leaves future rows; the<=boundary; the joint tx atomicity (a forced failure → no pin rows, no entry, zero residue);pin_renew's live/expired/absent semantics;pin_check's two-variant classification;arbitratefolding pin rows + a registered test-owned liveness table under the finger-lock; the advisory lock's acquire/contention/release shape and the session-loss reaping semantics (a dropped client's lock is gone — testable by dropping the lease's connection). - Tests need a live server — the alkstore harness convention:
env-driven (
ALKBLOBS_PG_*), skip cleanly server-less, acceptance requires green against the harness server (pglo-poc, :15432, dbblobs,postgres/poc).
The store core drives these clauses in wave 6 (fleet mode); the
non-fleet store construction consumes ensure_gc_schema only
(store-postgres-construction). This task ships the clauses and their
engine-level contract tests.
Acceptance Criteria
- The postgres backend implements
EngineWindowStateandEngineFleetStatecompletely — every clause family of ADR-012 §5's postgres row ensure_gc_schemacreatesgc_pins+gc_window; the embedder's liveness table is never created by the engine (contract-tested)window_stageidempotent per(key, deadline);window_takereturns exactly the overdue set (<= nowboundary), removes it atomically, leaves future rows; empty on none/virgin- The joint tx atomicity holds:
pin_commitpublishes entry + pin rows in one step; any failure aborts with zero residue (contract-tested) pin_renewnever resurrects an expired/absent row;pin_checkclassifies per the two-variantPinState;arbitratefolds pin rows + registered liveness-table references under theFOR UPDATEfinger-lock (contract-tested with a test-owned embedder table)- The advisory lock: atomic test-and-set, contention →
Ok(false), release no-op-safe, session-loss reaping; the same-connection acquire/release discipline recorded - Named design points recorded: the
JointTxconcrete type, thepin_renewsuccessor-time source, the timestamp encoding, the lock key + scope, the lease's connection holding - Contract tests env-driven, skipping cleanly server-less; green against the harness server at acceptance
cargo test --features postgresgreen server-less (skipping);cargo clippy --all-targets -- -D warningsandcargo fmt --checkgreen (default +--features postgres)
References
- src/gc.rs (
EngineWindowState+EngineFleetState— the seam contracts and their clause docs) - docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §2 (the clause bodies; the tx-ownership ruling; the advisory lock)
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §3 (fleet activation is a declaration), §4 (the GC-state host), §5 (the applicability table — postgres all-yes), §6.6 (the liveness registration's API home), §6.7 (the executor)
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md §2 (the fleet mechanism: pin rows, expiry, TTL 60 s, the advisory lock, the staged window)
- docs/architecture/decisions/013-family-composition-reactive-boundary-and-feature-graph.md §2 (the sweep lease's named exclusive semantics)
- src/sqlite.rs (the window family's reference implementation — same semantics, different dialect)
- tasks/engine-postgres.md (the pool/schema/driver posture this rides)
Notes
To be filled by implementation agent
Summary
To be filled on completion