Files
alkstore/tasks/pg-engine-integration.md

11 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
pg-engine-integration Postgres engine — integration (full-surface wiring, suite adoption, crate docs) completed
pg-engine-notify-listen
pg-engine-streams
pg-engine-queues
pg-engine-locks
pg-engine-scheduler-outbox
moderate medium phase implementation
wave-4
postgres-engine
integration

Description

Close the wave's integration gaps once every mechanism is wired — the structural twin of wave 3's integration task (no lint-suppression removal on this side: the pg engine is greenfield, no #![allow] posture ever existed — the sweep below is the same discipline without that step):

  • Full-surface sweep: every Store/TxHandle/mechanism trait method implemented (no todo!/unimplemented!/error-stub remaining); the engine crate's lib re-exports its public surface (open, PgOpts, the concrete store type) per the module-per-file convention. Genuinely dead code must be cut, not suppressed — clippy -D warnings green without allows.
  • Contract-suite adoption (the plan's "waves 3 and 4 adopt the harness"): implement StoreFactory for the pg engine — a fresh schema per open (the SQLite factory's fresh-temp-file precedent; the POC's shared-server parallel-interference caveat is answered by exactly this isolation — each property's rows live in its own schema, so parallel property runs never observe one another), idempotent teardown (DROP SCHEMA … CASCADE; safe against an abandoned store per the factory contract's teardown posture). Pin the factory's isolation/idempotence contract (the SQLite factory's pin shape). Wire the engine's backlog column — the rows this engine owns now, running against the pg factory:
    • The ADR-023-stamped rows (extent-clamp, duration-refusal, encode_payload round-trip) — already written suite-side, factory-parameterized; they run against pg by this task's factory existing (verify, don't rewrite).
    • The pg arm of the engine-scoped rows: PayloadTooLarge produced (the pg rejection arm — oversized notify/notify_tx rejected client-side with the limit in the variant; the row text the SQLite integration task left for "wave 4's adoption"), drop = rollback no-ghosts, in-tx read-your-own-writes, enqueue-opts resolution, receiver close arms (the pg arm: stays open across reconnects, closes at shutdown — the row's engine-asymmetric legs may need the row split or parameterized per the suite's convention; implementer's choice, documented).
    • The SQLite-scoped rows (plain-path open §3, poll cadence §4) simply don't run against pg — the SQLite integration task's Notes already pinned that disposition.
  • Crate docs: the engine crate's lib-level doc comments state the multi-host posture and the listener budget line (ADR-016, deployment.md) — the identity statements this crate owns (the open task drafted them; this task verifies they reflect the finished engine).
  • Gates: full workspace build/test/clippy/fmt (the pg tests skip cleanly server-less but must be green against the harness server); coverage spot-check on the engine crate (no large uncovered regions outside error arms — the wave-3 gate's bar).

Acceptance Criteria

  • No todo!/unimplemented!/error-stub in the engine crate; full trait surface implemented; clippy -D warnings green without allows
  • StoreFactory implemented for pg (fresh schema per open, idempotent CASCADE teardown); the factory's isolation/idempotence contract pinned
  • The backlog column runs green against the pg factory: the three ADR-023 rows verified (not rewritten), the pg arm of the five engine-scoped rows present + stamped
  • The PayloadTooLarge pg arm row present (the SQLite integration task's deferred adoption discharged)
  • Crate lib docs carry the multi-host + listener-budget posture statements, reflecting the finished engine
  • Workspace gates green: cargo build, cargo test (incl. against the harness server), clippy -D warnings, fmt clean
  • Coverage spot-check: no large uncovered regions outside error arms

References

  • docs/plans/implementation.md (Wave 4 section)
  • docs/architecture/core-contract.md (§Verification backlog)
  • docs/architecture/decisions/022-contract-suite-layout.md
  • docs/architecture/decisions/023-fourth-review-round.md (backlog additions)
  • docs/architecture/decisions/016-deployment-honesty.md §5
  • alkstore-sqlite/tests/contract_suite.rs (the factory precedent)
  • alkstore-contract-suite/src/properties.rs (the rows to run/adopt)

Notes

Decisions of record the description didn't pin:

  • No stubs were found to sweep — the mechanism tasks retired the wave-3 error-stub surface as they landed; the sweep verified the absence (todo!/unimplemented!/error-stub grep clean; clippy -D warnings green with zero allow(...) lint attributes anywhere in the crate). The sweep's cut-not-suppress residue was doc-staleness, not code: store.rs's module docs still described the trait stubs ("wiring lands with the … task") and lib.rs's tail still described the wave-4 build order — both rewritten to the finished-engine posture.
  • Test-observation accessors honestly #[cfg(test)]-gated (the SQLite integration task's precedent): pool(), forwarder(), schema(), listener_application_name(), and the listener_application_name field carried #[cfg_attr(not(test), allow(dead_code))]/ #[allow(dead_code)] gates (the tasks' "tests exercise it now / gate until then" posture). All five are lib-dead — the lib build reaches the fields directly — so they are #[cfg(test)]-gated with no allows, and PgStore::new (left unused by the direct struct construction in open_store) was cut. No ADR-018 register entries: nothing diverged from a lineage-kept set (the pg engine is greenfield; these were this repo's own task-staged accessors).
  • The PayloadTooLarge pg arm is a new suite row, not a parameterized split — payload_too_large_produced_on_pg joins payload_too_large_never_produced_on_sqlite as its own factory-functioning row (one normative owner per arm's property; the two arms' assertions differ, so a shared row text would have carried engine-conditional branches — the drift surface ADR-022's one-text rule exists to avoid). Both carry the same ADR-008 §5 / ADR-016 §5 stamps; the matchability posture (engine-agnostic code writes one match) is stated on both. The limit pin cites the contract number (8000) — pg-column-specific, so it's contract text here, not engine detail.
  • The receiver-arms row ran unchanged against pg — no row split needed. The row's disposal-close legs drive the close through the store-handle drop (the dispose carrier every engine implements); pg's cause asymmetry (stays open across reconnects, closes only at engine shutdown — ADR-021 §5's pg arm) is documented in the pg engine's own module docs and engine tests (receiver_stays_open_across_reconnects_closes_at_shutdown, engine_shutdown_closes_the_subscription_terminally) rather than in the row text.
  • The factory tracks its own schemas — teardown drops exactly the schemas the instance minted (a per-instance registry), never a fixed prefix or the shared server's other schemas (a prefix- or name-based DROP would be a footgun against the harness's co-tenanted server). DROP SCHEMA IF EXISTS … CASCADE per minted name: IF EXISTS is the idempotence (a second teardown, or a schema an abandoned store's bootstrap re-created, are both safe shapes). Admin connection per teardown call — no shared client lifetime to outlive an await.
  • Suite rows skip cleanly server-less via a reachability probe in the test target (harness_ready() — one cheap admin connect with a 3 s timeout); the factory's open itself fails typed (Database) on an unreachable server. Same posture as the engine's own test modules.
  • The trait-surface acceptance test renamed — store_trait_methods_are_wiring_stubs → store_trait_methods_are_wired (the old name was the wave-3 stub-era label; the test's assertions were already full-surface).

Summary

Landed: the pg engine's backlog column — PgFactory implemented in the new integration test target (alkstore-postgres/tests/ contract_suite.rs), a fresh engine-owned schema per open (unique per call; parallel property runs never observe one another), idempotent owned-teardown (DROP SCHEMA IF EXISTS … CASCADE over exactly the instance's minted schemas); the factory's isolation/idempotence contract pinned (opens_are_isolated_and_teardown_is_idempotent — cross-open row invisibility server-side + double-teardown + pg_namespace proof). All ten rows green against the harness server: the exemplar

  • the three ADR-023 rows (verified — not rewritten; factory- parameterized text ran as-is) + the engine-scoped five (drop=rollback no-ghosts, in-tx read-your-own-writes, enqueue-opts resolution, receiver close/save arms — ran unchanged) + the new payload_too_large_produced_on_pg row (the SQLite integration task's deferred adoption discharged; typed PayloadTooLarge { limit: 8000 } on notify and notify_tx, client-side rejection pinned, re-exported from the suite lib). Server-less runs skip cleanly (reachability probe).

Full-surface sweep: no todo!/unimplemented!/error-stub anywhere in the engine crate (verified by grep + the trait surface exercised end-to-end); stale stub-era doc text cut (store.rs's "wiring lands with the … task" paragraph, lib.rs's wave-4 build- order tail → the finished-engine statement including the suite target pointer); test-observation accessors honestly #[cfg(test)]-gated (no lint allows anywhere in the crate) and the unused PgStore::new cut; the trait-surface test renamed to its finished posture. Crate lib docs already carried the multi-host + listener-budget (max_size + 1, deadpool#360) identity statements — verified accurate against the finished engine, no change needed beyond the tail.

Verified: workspace cargo build/cargo test green against the harness server (25 core + 3 harness + 111 pg + 10 pg suite + 9 pg schema + 188 sqlite + 10 sqlite suite = 356 tests, 0 failures; server-less pg suite skips clean); clippy --workspace --all-targets -D warnings clean; cargo fmt --check clean. Coverage spot-check (cargo-llvm-cov, engine crate, harness server up): 93.27% lines, every file ≥85% (stream.rs 88.31%, queue.rs 94.99%, scheduler.rs 92.84%...); misses are error arms (commit/rollback-failure discards, Codec posture guards), the drive_sync CurrentThread/foreign-runtime defensive branches, and Debug impls — no large uncovered regions outside error arms, the wave-3 gate's bar. The streams task's pre-existing flake (tx_publishes_compose_with_the_handle) did not recur in these runs (untouched by this change).