--- id: pg-fix-open-path name: Fix `max_size: 0` open hang + DSN options override (review 002 Finding 3 + options note) status: pending depends_on: [] scope: single risk: low impact: component level: implementation tags: [wave-4-fixes, postgres-engine, open] --- ## Description Two open-path fixes in `alkstore-postgres/src/store.rs` (`open_store`), from the wave-4 general review: **Finding 3 (MEDIUM, live-proven)**: `PgOpts { max_size: 0 }` hangs `open` forever. Site: `store.rs:162-165` + the bootstrap `pool.get()` at `store.rs:177` — deadpool (0.13/0.14) does not validate `max_size` and defaults to no timeouts, so a `max_size: 0` pool can never grant a checkout: `open` blocks forever at the bootstrap checkout (no `BuildError`, no default pool timeouts) instead of failing typed. Probe (removed): `open` with `max_size: 0` timed out at 5 s, never returned. Fix shape (the review's): validate `max_size > 0` in `open_store` — typed `Error::Database` **before any round trip** (the engine-wide validate-at-entry posture; the source-chain detail should say what was rejected, e.g. "max_size must be positive"). The alternative shape (configure deadpool timeouts so the hang surfaces as an error) is weaker — a timeout-configured hang still burns the consumer's time; prefer the guard. Do both only if trivial. **Minor note (same file, same surface)**: `store.rs:152-156` — `pool_cfg.options(...)` **replaces** any `options=` present in the consumer's DSN (tokio-postgres's setter semantics) — a silent config override. Preferred fix: **append** to the DSN's existing options string (tokio-postgres `Config::get_options()` reads what the parse carried) so the engine's `synchronous_commit` SET rides alongside whatever the consumer configured. If appending proves impractical, document at the site and in the crate docs that engine-configured options win — but append is the honest default (the consumer's DSN options were there first). Tests (against the harness server): `max_size: 0` fails typed and *promptly* (assert the error; the test's own structure bounds the hang — a regression re-hangs and fails the test by timeout); a DSN carrying pre-existing `options=` plus `open` succeeds and `synchronous_commit` is still observable via `SHOW` on both settings (the append path doesn't break the engine's SET); the existing opts-flow tests stay green. ## Acceptance Criteria - [ ] `max_size: 0` fails `open` with a typed `Database` error before any round trip — pinned by test (prompt failure, no hang) - [ ] The consumer DSN's `options=` survive: appended, not replaced — pinned by test (DSN with options + engine SET coexist, `SHOW` observable) - [ ] If append proved impractical instead: the engine-wins semantics documented at the site and in crate docs (record the decision in Notes) - [ ] Existing open/opts tests stay green - [ ] `cargo test -p alkstore-postgres` (harness server), clippy `-D warnings`, fmt clean; gates green server-less ## References - docs/reviews/002-wave-4-general-review.md §Finding 3 + §Minor notes (the options bullet) - docs/architecture/engine-postgres.md (Connection architecture) - docs/architecture/decisions/008-contract-v1-pinning.md §6 (config split) - tasks/pg-engine-open-opts.md (the open task of record; Notes carry the connect-options SET mechanics this task extends) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion