Files
alkstore/tasks/pg-fix-tx-wake.md
T

4.7 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
pg-fix-tx-wake Fix tx enqueue/publish paths issuing no pg_notify wake (review 002 Finding 2 — F-1's root cause) pending
narrow low component implementation
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