105 lines
4.7 KiB
Markdown
105 lines
4.7 KiB
Markdown
---
|
|
id: pg-fix-tx-wake
|
|
name: Fix tx enqueue/publish paths issuing no pg_notify wake (review 002 Finding 2 — F-1's root cause)
|
|
status: pending
|
|
depends_on: []
|
|
scope: narrow
|
|
risk: low
|
|
impact: component
|
|
level: implementation
|
|
tags: [wave-4-fixes, postgres-engine, tx, wake]
|
|
---
|
|
|
|
## Description
|
|
|
|
Fix the HIGH finding from the wave-4 general review
|
|
(`docs/reviews/002-wave-4-general-review.md` §Finding 2, live-proven):
|
|
none of the tx-side enqueue/publish paths run `pg_notify` in the
|
|
caller's transaction. Sites: `alkstore-postgres/src/tx.rs:290-329`
|
|
(`publish_tx`/`publish_with_key_tx`), `tx.rs:272-288` (`enqueue_tx`),
|
|
`tx.rs:545-562` (`outbox_enqueue_tx`). `notify_tx` proves `pg_notify`
|
|
inside a tx is native commit-atomic (delivers at the caller's commit)
|
|
— the exact mechanism these paths have available and don't use. A
|
|
*committed* `publish_tx`/`enqueue_tx` never wakes a registered
|
|
listener (deterministic silence, live-proven), while the auto-commit
|
|
`publish`/`Queue::enqueue` wake immediately.
|
|
|
|
**This is F-1's root cause, engine-side** — `tasks/review-wave-4.md`
|
|
F-1's "root cause unresolved" and the gate's "the tx INSERT's commit
|
|
*is* the NOTIFY carrier" conclusion were both wrong. Mechanism: the
|
|
subscriber bridge attach-read + drain race the commit; when both beat
|
|
the commit the bridge drains empty and parks on `wait_wake` — and no
|
|
wake ever comes from a tx-committed publish, so the 15 s deadline
|
|
exhausts (~2% under the 8-thread suite).
|
|
|
|
**Conformance angle**: engine-postgres.md's mapping names `pg_notify`
|
|
as the streams wake trigger; core-contract.md pins "events never
|
|
require polling to become visible" and the tx/no-ghosts rows pin
|
|
rollback dropping the event row *with its wake*. SQLite's watcher
|
|
fires on any committed write, so its tx path wakes — the asymmetry
|
|
would also have failed wave 5's cross-engine equivalence rows.
|
|
|
|
**Fix shape** (the review's):
|
|
|
|
- `publish_with_key_tx`: after the INSERT, one
|
|
`SELECT pg_notify($1, '')` inside the caller's tx — the channel is
|
|
the stream name (the mechanism-name-is-the-channel realization,
|
|
already pinned in `stream.rs`'s docs). Empty payload: the wake
|
|
carries `Wake { channel }` only, never payloads (ADR-008 §3). This
|
|
is the **contract obligation** — the load-bearing fix.
|
|
- `enqueue_tx` and `outbox_enqueue_tx`: add the same wake (channel =
|
|
queue name; the outbox's derived `__alkstore_outbox:{name}`) for
|
|
**posture parity** with SQLite's watcher — a weaker obligation (the
|
|
queues row's pinned re-poll safety net covers correctness; the wake
|
|
is latency posture), but the asymmetry is free to close and the
|
|
review recommends it.
|
|
- `run_once` is a pull op, consumer-driven — no wake needed there
|
|
(do not add one).
|
|
|
|
**Record updates**: this fix retires F-1's engine arm. Update
|
|
`tasks/review-wave-4.md`'s F-1 note (engine arm retired by this fix;
|
|
the wave-5 suite-hardening candidates — parked-recv deadline, lag
|
|
diagnostics — remain valid as defense-in-depth but are no longer
|
|
load-bearing for this flake) and `docs/plans/implementation.md`'s
|
|
review-rounds line for the general review.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `publish_with_key_tx` issues the `pg_notify` wake commit-atomically
|
|
(channel = stream name, empty payload) — pinned by test: a
|
|
registered listener receives the wake at/after the caller's
|
|
commit, deterministically
|
|
- [ ] The no-ghosts arm pinned: a *rolled-back* `publish_tx` produces
|
|
no wake (the wake is commit-atomic, not statement-atomic)
|
|
- [ ] `enqueue_tx` and `outbox_enqueue_tx` issue their wakes (channel =
|
|
queue name / derived backing queue) — pinned by test
|
|
- [ ] `tx_publishes_compose_with_the_handle` is deterministic: repeated
|
|
solo runs (≥ 20) green with zero deadline misses
|
|
- [ ] Auto-commit `publish`/`enqueue` behavior unchanged (still wake;
|
|
no double-wake regression pinned where the suite observes it)
|
|
- [ ] `tasks/review-wave-4.md` F-1 note + `docs/plans/implementation.md`
|
|
review-rounds line updated to record the retirement
|
|
- [ ] `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 2 (the finding,
|
|
the probe, the fix shape, the F-1 mechanism)
|
|
- docs/architecture/engine-postgres.md (pg_notify as the streams wake
|
|
trigger)
|
|
- docs/architecture/core-contract.md (streams visibility; tx/no-ghosts
|
|
rows)
|
|
- docs/architecture/decisions/008-contract-v1-pinning.md §3 (Wake
|
|
carries channel only)
|
|
- tasks/review-wave-4.md (F-1's record — updated by this task)
|
|
- alkstore-postgres/src/tx.rs (`notify_tx` — the proven commit-atomic
|
|
mechanics these paths adopt)
|
|
|
|
## Notes
|
|
|
|
> To be filled by implementation agent
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |