Files
alkblobs/tasks/engine-pg-lo-window.md
T
glm-5.3-flash c8fc519978 feat(gc): the pg-lo engine-state window family — the conditional window host (wave 5)
- impl EngineWindowState on PgLoBackend: ensure_gc_schema creates
  gc_window only (never gc_pins, never the embedder's liveness
  table), window_stage upserts idempotent per (key, deadline),
  window_take is the one atomic DELETE ... RETURNING step over the
  <= boundary with the 42P01 -> empty virgin-take split
- nothing of EngineFleetState exists on this engine (ADR-012 §5's
  pg-lo row: fleet pins live on the kv host, sweep lock no); the
  one-window-host dormancy posture is recorded in the impl doc
- five contract tests mirror the postgres/sqlite suites (schema-only
  + row shape, stage idempotence/restage, take-exact-set/never-twice,
  the <= boundary with nanosecond slack, both virgin shapes);
  env-driven, skipping cleanly server-less, green live against the
  harness server

Verification: cargo test (default + --features pg-lo, server-less
and live), clippy -D warnings (default, pg-lo, + the pure builds
"pg-lo,mem" and "postgres,pg-lo"), cargo fmt --check, cargo doc --no-deps
2026-10-12 07:40:59 +00:00

9.4 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
engine-pg-lo-window pg-lo engine-state window family — the conditional window host completed
engine-pg-lo
narrow low component implementation
wave-5
engines
pg-lo
gc

Description

Implement the engine-state companion seam's window family on the pg-lo backend (src/pg_lo.rs): the EngineWindowState impl per ADR-012 §5's pg-lo row — ensure_gc_schema creates gc_window only, the window clauses conditional per the one-window-host rule. Nothing of EngineFleetState may exist on this engine (the pg-lo row: pin clauses no — fleet pins live on the kv host, §4; sweep-lock clause no). The postgres engine's full-family impl (src/postgres.rs, wave 4) and the sqlite window-only impl (src/sqlite.rs, wave 3) are the reference implementations — same semantics, different dialect.

  • ensure_gc_schema — creates gc_window only (gc_window (key bytea PRIMARY KEY, visible_after bigint) per ADR-010 §2's schema, in the engine's own schema). Never gc_pins (contract-tested absent); the embedder's liveness table is never created here (ADR-008's schema-ownership rule — the sweep reads it and never owns it). Whether the store core calls this clause at construction is the one-window-host rule's business (store-pg-lo-construction wires it): the window hosts on the kv engine when it is SQL-backed (pg-lo's clauses dormant on a dual-SQL instance — kv=sqlite/postgres + large=pg-lo), and on pg-lo only when the kv engine is not SQL-backed (the kv=mem + large=pg-lo shape). The clause body exists and is contract-tested per applicability regardless of dormancy.
  • 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/postgres engines' 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 (DELETE … RETURNING, selection and removal together; a row is never handed out twice). Empty when nothing is overdue; virgin store: empty.
  • Timestamp encoding — the crate-wide encoding: signed i64 nanosecond count from the Unix epoch, carried as BIGINT (sub-second precision for the test tunings, pre-epoch negatives, full i64 range); per-engine bodies of the shared helpers (the shared-helper home decided in engine-pg-lo).
  • Virgin-store tolerance split — the postgres engine's recorded posture: window_take is a read (SQLSTATE 42P01 maps to empty on a schema-less database); window_stage is a write (surfaces the engine error — never silently creating schema; store construction always wires ensure_gc_schema when the engine hosts).
  • 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; the <= boundary incl. nanosecond slack; pre-epoch encoding; both virgin shapes.

The store core drives these clauses only on the host topology (store-pg-lo-construction); this task ships the clauses and their engine-level contract tests.

Acceptance Criteria

  • The pg-lo backend implements EngineWindowState — the window family only; no pin-clause or sweep-lock body exists anywhere in the pg-lo engine (nothing of EngineFleetState)
  • ensure_gc_schema creates gc_window only; gc_pins is never created by pg-lo (contract-tested); the embedder's liveness table is never created
  • window_stage idempotent per (key, deadline); a re-stage at a new deadline updates the row
  • window_take returns exactly the overdue set (<= now boundary), removes it atomically, leaves future rows; empty on none/virgin
  • Contract tests env-driven (ALKBLOBS_PG_*), skipping cleanly server-less; green against the harness server (pglo-poc, :15432, db blobs, postgres/poc) at acceptance; the janitor discipline rides from the start (amendment A2)
  • cargo test (default) green; cargo test --features pg-lo green server-less (skipping); cargo clippy --all-targets -- -D warnings and cargo fmt --check green (default + --features pg-lo); the pg-lo pure builds clean (--no-default-features --features "pg-lo,mem" and --features "postgres,pg-lo" — the literal kv-less config is not a gate, see store-pg-lo-construction)

References

  • src/gc.rs (EngineWindowState — the seam contract and its clause docs)
  • docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §5 (the applicability table — the pg-lo row; the one-window-host rule), §6.7 (the executor)
  • docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §2 (the clause bodies; the gc_window schema)
  • docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md §2 (the durable window's semantics)
  • src/sqlite.rs, src/postgres.rs (the window family's reference implementations — same semantics, different dialects)
  • tasks/engine-pg-lo.md (the driver posture this rides; the shared-helper design point)

Notes

Implemented in src/pg_lo.rs only. PgLoBackend gained a window_table field (the quoted "schema"."gc_window" reference, quoted-identifier boundary, constructed in new and new_schemaless — the two #[cfg(test)] failure-injection struct literals picked up the new field mechanically) and a gc_window_ddl body beside bootstrap_ddl. The EngineWindowState impl mirrors the postgres reference implementation statement-for-statement (same upsert / same single DELETE … RETURNING take, this engine's pg_err/err mapping and time_to_nanos from the shared pg_common home); the per-engine reverse decoder (nanos_to_time) lives in the test module like the postgres suite's. Recorded design points:

  • The gc_window-only schema row — ensure_gc_schema runs CREATE SCHEMA IF NOT EXISTS + CREATE TABLE gc_window (key bytea PRIMARY KEY, visible_after bigint NOT NULL) only; the schema's DDL names no gc_pins and no liveness table (ADR-010 §2's schema-ownership rule). The engine's constructor continues to create only lo_entries — the GC table is created by the clause alone, and whether the store core calls it at construction is the one-window-host rule's business (store-pg-lo-construction wires the active host) — untouched this task.
  • Nothing of EngineFleetState — no pin clause, no sweep-lock body anywhere in the module (grep-clean; only doc mentions carry the names), and gc_pins's absence is contract-tested (gc_schema_creates_window_table_only asserts the engine-owned schema holds exactly ["gc_window", "lo_entries"]).
  • Dormancy posture recorded — the impl doc states the one-window-host rule reading: clauses dormant on a dual-SQL instance (kv=sqlite/postgres + large=pg-lo stages on the kv engine), active on the kv=mem + large=pg-lo shape; the clause body exists and is contract-tested per applicability regardless.

No store-core changes (store.rs untouched — the sweep's durable form drives the seam through KvEngine, which needs no new arm for this task).

Summary

Completed 2026-10-12 (wave 5). src/pg_lo.rs implements EngineWindowState — the window family only (ADR-012 §5's pg-lo row): ensure_gc_schema creates gc_window (key bytea PRIMARY KEY, visible_after bigint) in the engine's own schema and never gc_pins or the embedder's liveness table (contract-tested — the schema holds exactly gc_window + lo_entries); window_stage upserts idempotently per (key, deadline) with same-deadline idempotence and new-deadline update; window_take(now) is the one atomic DELETE … RETURNING step over visible_after <= now (the <= boundary pinned), keys sorted, with the 42P01 → empty virgin-take read tolerance and stage-less-write surfacing. Timestamps ride the crate-wide i64-nanos encoding (pg_common::time_to_nanos; the reverse decoder is the test module's per-engine body). Five contract tests mirror the postgres/sqlite suites: schema-only + row-shape + idempotence-of-call, stage idempotence/restage-at-new- deadline, take-exact-overdue-set/never-twice/future-left, the <= boundary with nanosecond slack, and both virgin shapes (schema-less take empty twice, schema-less stage surfaces the engine error, empty-window take). Env-driven (ALKBLOBS_PG_*), skipping cleanly server-less, green live against the harness server (23/23 in-module tests, per-test dedicated databases + DatabaseJanitor) with zero LO residue on the shared db gate green. Verified: cargo test (69/19/3 ok; 92/19/3 with --features pg-lo server-less), clippy -D warnings green (default, --features pg-lo, --no-default-features --features "pg-lo,mem", --no-default- features --features "postgres,pg-lo"), cargo fmt --check green, cargo doc --no-deps clean. Remaining: constructor wiring is store-pg-lo-construction.