feat(engines): the postgres kv engine — the fleet host's first engine (wave 4)
The 'postgres' kv-tier engine (ADR-007, feature 'postgres', default-off) against the wave-1 trait: the sqlite engine's structural twin on a Pooled-Postgres driver cluster. - One engine-owned schema (default 'alkblobs'; quoted-identifier boundary via the alkstore quote_identifier pattern) holding the B5 row shape — key bytea PK / value bytea NOT NULL / size bigint NOT NULL, WITH (fillfactor = 90) — idempotently bootstrapped at construction; autovacuum and the ~40x small-tier storage overhead recorded as posture, not silence - deadpool pool with DEFAULT_MAX_SIZE = 8 (load-bearing, POC #5 B2); per-connection prepare_cached discipline (B5: statements die with sessions); the synchronous_commit parity knob as connect options on every pooled connection (default 'on', configurable, alkstore src/opts.rs mechanics) - CAS first-writer-wins (INSERT ... ON CONFLICT DO NOTHING) on both put forms; metadata-only size probe riding the PK index (EXPLAIN gate-tested); EXISTS has; materializing get cursor; degenerate in-memory stage with both hygiene forms (fail_commit injection) - list: the single-statement MVCC-snapshot cursor (portal-batched row stream, keys only); crate-internal batch-delete helper (DELETE ... WHERE key = ANY() over bytea[]), scope-noted until wave 6; virgin-store read-only split via SQLSTATE 42P01 mapping - 15 in-module unit tests, env-driven (ALKBLOBS_PG_*), skipping cleanly server-less; green against the dockerized pglo-poc harness server (:15432) Verified: cargo test; cargo test --features postgres (84 lib tests); cargo test --all-features (139); clippy all-targets, default + features + all-features, -D warnings; fmt --check; doc --no-deps.
This commit is contained in:
1 parent
7ea6dd6bcb
commit
1efc46f953
2 files changed
+1360
-5
No files matched your search
+1291
-2
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: engine-postgres
|
id: engine-postgres
|
||||||
name: postgres kv engine — the fleet-capable engine
|
name: postgres kv engine — the fleet-capable engine
|
||||||
status: pending
|
status: completed
|
||||||
depends_on: []
|
depends_on: []
|
||||||
scope: narrow
|
scope: narrow
|
||||||
risk: medium
|
risk: medium
|
||||||
@@ -174,8 +174,74 @@ the engine and its in-module unit tests only.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
> To be filled by implementation agent
|
> Implemented in `src/postgres.rs` (wave 4, this task's only code
|
||||||
|
> change; `src/lib.rs`'s `#[cfg(feature = "postgres")] pub mod
|
||||||
|
> postgres;` gate pre-existed). Recorded design points, as resolved:
|
||||||
|
>
|
||||||
|
> - **Opts shape** — a crate-internal `PgOpts` (`schema` /
|
||||||
|
> `max_size` / `synchronous_commit`), mirroring alkstore's
|
||||||
|
> `src/opts.rs`; the constructor takes the tokio-postgres
|
||||||
|
> `Config`-parseable connection string first, `PgOpts` second.
|
||||||
|
> `max_size = 0` fails at entry, before any round trip; an empty
|
||||||
|
> schema string selects the default.
|
||||||
|
> - **Schema name** — default `"alkblobs"`; every statement composes
|
||||||
|
> the table through `quote_identifier` (the alkstore boundary), so a
|
||||||
|
> hostile schema name (embedded `"`, reserved-word-shaped) is tested
|
||||||
|
> green. `size`/`value`/`key` are quoted in DDL per the alkstore
|
||||||
|
> hazard list, plain in DML (`key`, `value`, `size` non-reserved).
|
||||||
|
> - **Pool default** — `DEFAULT_MAX_SIZE = 8`, `RecyclingMethod::Fast`
|
||||||
|
> (alkstore precedent); deadpool-postgres 0.14 / tokio-postgres 0.7.
|
||||||
|
> - **list fetch shape** — single `SELECT key … ORDER BY key` through
|
||||||
|
> the deadpool client: one statement = one MVCC snapshot
|
||||||
|
> (vacuum-ghost-stable); tokio-postgres's row stream drains the
|
||||||
|
> server-side portal in bounded batches and the collection
|
||||||
|
> materializes only the key column. Ordering by key is engine-local
|
||||||
|
> (the trait leaves ordering unspecified).
|
||||||
|
> - **Virgin-store discriminant** — SQLSTATE 42P01
|
||||||
|
> (`is_undefined_table`), mapped on reads (absent/empty), delete
|
||||||
|
> (`Missing`), and `delete_batch` (`Ok(0)`); writes and staged
|
||||||
|
> commits surface `Error::Io`. The schema-less fixture connects via
|
||||||
|
> a `#[cfg(test)] new_schemaless` constructor (the sqlite
|
||||||
|
> `new_tableless` pattern) against a schema that was never
|
||||||
|
> bootstrapped.
|
||||||
|
> - **Stage hygiene mechanics** — the staged buffer is
|
||||||
|
> `std::mem::take`n before the pool checkout, so a failed commit
|
||||||
|
> (injection or engine error) strands no staged bytes; the
|
||||||
|
> `#[cfg(test)] fail_commit` knob mirrors sqlite/mem.
|
||||||
|
> - **No new dependencies** — `postgres = ["dep:tokio-postgres",
|
||||||
|
> "dep:deadpool-postgres"]` was already in `Cargo.toml`; `list` uses
|
||||||
|
> the client's collected query (row-stream under the hood) rather
|
||||||
|
> than pulling futures-util for `query_raw`.
|
||||||
|
> - **Test harness quirks** — `simple_query("SHOW …")` returns
|
||||||
|
> `SimpleQueryMessage`s (skip to the `Row` variant); `size`'s
|
||||||
|
> EXPLAIN plan gate embeds the key as a `'\x…'` literal (PREPARE
|
||||||
|
> cannot wrap EXPLAIN) and asserts `Index Scan` + `blobs_pkey`.
|
||||||
|
> - Isolation convention: one fresh schema per test
|
||||||
|
> (`abt_{tag}_{pid}_{seq}`), the alkstore suite-factory pattern, on
|
||||||
|
> the shared harness database.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
> To be filled on completion
|
> Completed 2026-10-12 (wave 4). `src/postgres.rs` implements the
|
||||||
|
> seven-method `Backend` + `put_staged` behind feature `postgres`
|
||||||
|
> (default-off) — deadpool pool (default 8, load-bearing posture
|
||||||
|
> recorded), per-connection `prepare_cached` discipline, the
|
||||||
|
> `synchronous_commit` parity knob wired as connect options on every
|
||||||
|
> pooled connection (default `on`, configurable), one engine-owned
|
||||||
|
> schema (`alkblobs`, quoted-identifier boundary) holding the B5 row
|
||||||
|
> shape with `fillfactor = 90` idempotently bootstrapped at
|
||||||
|
> construction, CAS first-writer-wins via `ON CONFLICT DO NOTHING` on
|
||||||
|
> both put forms, the metadata-only `size` probe (PK-index-tested via
|
||||||
|
> EXPLAIN), the degenerate in-memory stage with both hygiene forms, the
|
||||||
|
> single-MVCC-snapshot `list` cursor, and the crate-internal
|
||||||
|
> `delete_batch` (`key = ANY($1)` over `bytea[]`, scope-noted until
|
||||||
|
> wave 6). Natively async — no spawn_blocking. 15 in-module unit tests,
|
||||||
|
> env-driven (`ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB`), skipping
|
||||||
|
> cleanly server-less; all green against the dockerized pglo-poc
|
||||||
|
> harness server (:15432). Verified: `cargo test` (69/19/3 ok),
|
||||||
|
> `cargo test --features postgres` (84/19/3 ok, server live), `cargo
|
||||||
|
> test --all-features` (139/54/4 ok), `cargo clippy --all-targets` and
|
||||||
|
> `--all-features --all-targets -- -D warnings` green, `cargo fmt
|
||||||
|
> --check` green, `cargo doc --no-deps` clean. Engine-state clause
|
||||||
|
> families remain `engine-postgres-fleet`; constructor wiring remains
|
||||||
|
> `store-postgres-construction`.
|
||||||
Reference in new issue
Block a user