--- id: suite-column-postgres name: Conformance suite — the postgres column (the SKIP-posture column) status: completed depends_on: [store-postgres-construction] scope: moderate risk: medium impact: phase level: implementation tags: [wave-4, suite, postgres] --- ## Description Grow the **postgres column** — the suite's first SKIP-posture column (README §SKIP posture: SQL-attached factories ride a server; when no server is reachable the column **skips cleanly** — a skip is not a failure — and the wave's acceptance requires green **against the server**). The column runs under `--features postgres` (feature `postgres`, default-off). - **Factories** (`tests/conformance/postgres_column.rs`): - dual-tier `kv = postgres + large = local` at a column-declared threshold and the **test tuning of the window delay (`0` — execute-on-next-sweep)**; - `kv-only(postgres)` at a column-declared threshold (the rejection row's host; threshold materializable per the README's configuration contract). **The alkstore pg-factory pattern, reused** (`alkstore-postgres` tests/contract_suite.rs): a **fresh engine-owned schema per `open()`** — unique per call (seeded per tag/pid/time so two factory instances never share a schema name, racing opens included), tracked per-instance in a schema registry, teardown `DROP SCHEMA IF EXISTS … CASCADE` over exactly the schemas the instance minted (idempotent, owned — never the shared server's other schemas; safe against an abandoned store: `CASCADE` removes dependents server-side while the store's pooled connections fail closed on next use). Panicking-row-safe — the sqlite column's instance-tracks-its-own-site pattern over schemas instead of temp dirs. - **Connection settings ride the environment** — the alkstore harness convention: `ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB` (db defaulting `postgres` if unset), a reachability probe gating the rows (the SKIP posture's factory-owner responsibility). **The harness server**: dockerized `pglo-poc` (`postgres:16-alpine`) on :15432, db `blobs`, user/password `postgres`/`poc` — verified live 2026-10-12; the container is long-running in the dev environment (the alkstore harness's same server). - **Mirror the form-independent reference rows** (README §The reference row set): INV-1 round-trips (content matrix × both put paths, in-body), INV-6 stat discipline, the `NoLivenessSources`/ `ProtectFailed` abort shapes (form-independent — abort before staging), and the kv-only rejection row (threshold-agnostic by discovery, unchanged). - **The durable-schedule sweep rows** — the sqlite column's family (README §The schedule-relative reading for SQL-backed columns): INV-2 dedup and INV-3 stage hygiene with counts read against the staged schedule, the two-sweep exact-count row over the full protection set (both tiers, the window observed), the sweep-vs-put race across the window delay (execution-time re-arbitration), the holder-stages-only lease shape, and INV-5 list correctness through durable-schedule outcomes. - **The dual-tier dispatch rows** mirror the sqlite column's family (pure-function routing, threshold boundaries, fall-through per op) with the placement oracle adapted by medium: kv entries live as rows in the pg schema's `blobs` table, large entries under the local root — the oracle distinguishes by medium (an independent connection counting the schema's rows vs the root's committed files). - **Virgin-store row**: reads are no-ops on a fresh schema (pool, both GC tables, and committed tree all empty) — the constructor always ensures the schema (the engine's recorded posture), this row being the facade form. - **Structural factory tests** (the mem/sqlite columns' pattern): factory isolation (fresh schema per open, nothing shared — two opens never observe one another) and teardown-against-a-panicking-row (schemas really dropped). - **README amendments** (the convention's normative test-side home): the postgres rows table in §Columns beyond the reference set; the column plan's SKIP-posture note now naming a landed column; current state (four columns; the first SKIP-posture column, green against the harness server). ## Acceptance Criteria - [ ] The postgres column green **against the harness server** (`pglo-poc`, :15432): the mirrored form-independent reference rows + the durable-schedule sweep rows + the dispatch rows + the virgin-store row, every row ADR-stamped and traceable to the backlog - [ ] Server-less runs skip cleanly (the reachability probe; the default-build gates stay green with the column skipped) - [ ] Factory isolation + idempotent teardown hold (fresh schema per open; `DROP SCHEMA … CASCADE` over exactly the minted schemas; teardown safe against a panicking row), structurally exercised - [ ] README amendments recorded (the postgres rows table; current state) - [ ] `cargo test` (default) green — the column compiles out; `cargo test --features postgres` green server-less (skipping) and green against the server at acceptance; `cargo clippy --all-targets -- -D warnings` and `cargo fmt --check` green (default + `--features postgres`) ## References - tests/conformance/README.md (the conventions this grows against; §SKIP posture, §Columns beyond the reference set, §The schedule-relative reading for SQL-backed columns) - tests/conformance/factory.rs (the factory contract this implements) - tests/conformance/rows.rs (the reference row set being mirrored) - tests/conformance/sqlite_column.rs (the SQL-backed column pattern — the durable-schedule rows' first host) - /workspace/@alkdev/alkstore/alkstore-postgres/tests/contract_suite.rs (the pg-factory pattern reused: fresh schema per open, the schema registry, the CASCADE teardown, the env-driven DSN) - docs/architecture/store-api.md (§Invariants — the test gate) - docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.7 (the schedule the sweep rows assert against) - docs/architecture/decisions/007-two-kv-engines-sqlite-and-postgres.md §5 (the exact-count sweep-outcome gate against dockerized postgres) ## Notes > Implemented in `tests/conformance/postgres_column.rs` + `main.rs` + > `README.md` (wave 4, following `suite-column-sqlite` — the > SQL-backed structural twin — and the alkstore pg-factory pattern). > Recorded design points, as resolved: > > - **Factories as specced** — `DualTierPostgresFactory` (kv = postgres > + large = local at the column-declared `TUNED_THRESHOLD` = 1024, > window delay `WINDOW_DELAY` = `Duration::ZERO`, the test tuning) > and `KvOnlyPostgresFactory` (`KV_ONLY_THRESHOLD` = 1024, the > rejection row's host). Both hold the DSN, a schema sequence > counter, and the per-instance schema registry (`Mutex>, > poisoned-lock via `unwrap_or_else(|e| e.into_inner())` per > convention); `open()` mints `alkblobs_suite_{tag}_{pid}_{nanos}_{seq}` > — unique per call, racing opens included — plus a fresh private > temp root for the large tier (dual-tier; kv-only needs none), > tracked per-instance. Teardown `DROP SCHEMA IF EXISTS "…" CASCADE` > over exactly the minted schemas (one admin connection, then the > drops) plus `remove_dir_all` over the vended roots (NotFound > tolerated) — idempotent, owned, panicking-row-safe. > - **SKIP posture as specced** — `harness_dsn()` rides > `ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB` (host required, non-empty; > port/user/password/db defaulting to the harness server's values — > :15432, `postgres`/`poc`, db `blobs` — the src/postgres.rs engine > tests' exact convention, the alkblobs adaptation of the alkstore > harness convention), `harness_ready()` probes with a 3 s-timed > admin connection, every runner gates on it first, and the skip > note prints once per skipped test. The acceptance run below is > against the harness server — a skip never substituted. > - **The medium oracle, adapted by medium** — an independent admin > connection per count (`SELECT COUNT(*) FROM "schema".blobs` / > `gc_window`) vs the root's committed files; `schema_exists` (the > `pg_namespace` probe) instruments the structural teardown > assertions. > - **Rows** — the sqlite column's inventory mirrored point for point > (the schedule-relative rows, the dispatch rows with the oracle's > medium swapped, the virgin-store row read against a fresh schema, > the structural factory tests adapted to schemas: two opens mint > distinct schemas and share nothing, teardown-against-a-panicking > row really drops the schemas); stamps cite the postgres rows of > ADR-007/012 (ADR-007 §5 replacing the sqlite arm's citations). > - **One cleanup caught in review-of-diff**: the `dual_tier` open > tracks the temp *base* dir and constructs the store against > `base.join("local-root")` — teardown removes the base directory > wholesale (the root's parent), so the site removal is one > operation like the sqlite column's. > - The `pglo-poc` container was already running in the dev > environment (verified with `docker ps` before implementation); no > environment changes were needed. ## Summary > Completed 2026-10-12 (wave 4). The postgres column — the suite's > first SKIP-posture column — landed green against the harness server > (`pglo-poc`, :15432): 17 conformance tests (the mirrored > form-independent reference rows — INV-1 round-trips, INV-6 stat > discipline, the `NoLivenessSources`/`ProtectFailed` abort shapes, > the kv-only rejection row — the durable-schedule sweep rows (INV-2 > and INV-3 schedule-relative readings, the two-sweep exact-count row > over the full protection set, the sweep-vs-put race across the > window delay, the holder-stages-only lease shape, INV-5 list > correctness), the dispatch rows with the placement oracle adapted to > the pg medium, and the virgin-store row), every row ADR-stamped and > traced to the backlog. Factory isolation (fresh schema per open, > registry-tracked) and idempotent `DROP SCHEMA … CASCADE` teardown > are structurally exercised (panicking-row-safe; `pg_namespace` > verified empty after runs — the shared server carries no residue). > README amended: the postgres rows table in §Columns beyond the > reference set, the column plan's SKIP-posture note naming the > landed column, and current state (four columns). Verification: > `cargo test` (default) 69/19/3 ok — the column compiles out; > `cargo test --features postgres` 102/36/3 ok green against the > server, and green skipping server-less (17 skip-noted columns rows; > default-build gates unaffected); `cargo clippy --all-targets -- -D > warnings` green (default, `--features postgres`, `--all-features`); > `cargo fmt --check` green.