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

178 lines
9.4 KiB
Markdown

---
id: engine-pg-lo-window
name: pg-lo engine-state window family — the conditional window host
status: completed
depends_on: [engine-pg-lo]
scope: narrow
risk: low
impact: component
level: implementation
tags: [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
- [x] 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`)
- [x] `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
- [x] `window_stage` idempotent per `(key, deadline)`; a re-stage at a
new deadline updates the row
- [x] `window_take` returns exactly the overdue set (`<= now`
boundary), removes it atomically, leaves future rows; empty on
none/virgin
- [x] 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)
- [x] `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`.