116 lines
5.4 KiB
Markdown
116 lines
5.4 KiB
Markdown
---
|
|
id: pg-suite-infra-hardening
|
|
name: Postgres test-infra hardening — F-1's defense-in-depth candidates
|
|
status: completed
|
|
depends_on: []
|
|
scope: single
|
|
risk: low
|
|
impact: isolated
|
|
level: implementation
|
|
tags: [wave-5, postgres-engine, tests]
|
|
---
|
|
|
|
## Description
|
|
|
|
Land the defense-in-depth test-infra hardening the wave-4 review
|
|
recorded for F-1 (`tasks/review-wave-4.md` Notes) — **explicitly not
|
|
load-bearing since `pg-fix-tx-wake` retired F-1's engine-side root
|
|
cause** (the compose test is deterministic, 20/20 solo runs green at
|
|
the fix-batch gate). This task exists because wave 5 was the named
|
|
home ("wave 5's suite hardening, or a wave-5-adjacent test-infra
|
|
task") and the candidates are cheap; if the wave-5 window gets tight,
|
|
cancelling this task loses nothing but insurance.
|
|
|
|
Work (the review's candidate dispositions, cheapest first):
|
|
|
|
- **(a) Harden `must_recv_event`'s deadline** — replace the 20 ms
|
|
polling loop with a parked `recv().await` under a timeout wrapper;
|
|
the deadline assert still bounds the property, the poll-loop's
|
|
wake-subscription interaction surface goes away.
|
|
- **(b) A `LOG_LAGGED`-style diagnostic counter on the bridge** —
|
|
discriminates "wake never arrived" from "wake arrived, re-drain
|
|
missed" if a wake-shaped flake ever recurs. Land only if it is a
|
|
small, honest addition (a test-observation counter, `#[cfg(test)]`-
|
|
gated or eprintln-postured per the house test style); skip and
|
|
record if it wants more machinery than that.
|
|
|
|
Candidate (c) (leave the skip-posture as-is) is the do-nothing arm —
|
|
adopted by default for anything not landed.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [x] `must_recv_event` parks on `recv().await` under a timeout
|
|
wrapper (no polling loop); the pg stream tests stay green
|
|
against the harness server (a full stream-module run)
|
|
- [x] Candidate (b) either landed small or recorded as skipped with
|
|
the reason
|
|
- [x] `cargo test -p alkstore-postgres` green (server-less skips
|
|
clean); clippy `-D warnings`; fmt clean
|
|
|
|
## References
|
|
|
|
- tasks/review-wave-4.md (F-1's Notes — the candidate dispositions)
|
|
- tasks/review-wave-4-fixes.md (F-1 retired; defense-in-depth posture)
|
|
- tasks/pg-fix-tx-wake.md (the engine-side fix that made this
|
|
non-load-bearing)
|
|
|
|
## Notes
|
|
|
|
Decisions of record:
|
|
|
|
- **(a)** landed as specified: `must_recv_event` is now
|
|
`timeout(15 s, receiver.recv())` — parked, no polling loop. The
|
|
old helper's `try_recv`-loop `Err` arm split into the parked
|
|
shape's two failure arms: `Some(Err(e))` → the same
|
|
"receiver errored instead of idling" panic (an error during the
|
|
wait surfaces immediately, as before), and `None` → a new
|
|
"receiver closed before the event arrived" panic (the parked form
|
|
makes the terminal arm visible, the poll form never saw it).
|
|
`try_recv` is otherwise untouched as a test tool (other pg test
|
|
modules still drain with it).
|
|
- **(b)**: the `wait_wake`'s `Lagged(n)` arm now logs (eprintln,
|
|
house posture — matching notify.rs's `bridge_loop`; it also makes
|
|
forwarder.rs's "the receiver bridge's `Lagged` arm logs / recovers"
|
|
doc claim true at both bridges — that claim was half-wrong on the
|
|
stream side). The arm still treats lag as a wake (re-drain).
|
|
The stream name is threaded into `wait_wake`'s signature so the
|
|
log attributes lag to the stream, not anonymous.
|
|
- **(b)'s full bridge-side counter — skipped, recorded**: surfacing
|
|
the bridge's wake-observation count into the deadline panic would
|
|
require plumbing a counter Arc across the `dyn EventReceiver`
|
|
trait boundary (a test downcast or a keyed global registry) —
|
|
more machinery than the small-honest bar. The discriminator's job
|
|
is covered test-side instead: with the parked recv, a "wake arrived,
|
|
re-drain missed" arm is structurally implausible (the re-drain
|
|
reads exactly what a re-read reads; only a read error could miss,
|
|
and it surfaces `Err`), so the deadline panic self-diagnoses: it
|
|
reads the durable rows beyond the receiver's position via
|
|
`read_since(offset, 1000)` and names the residual class — rows
|
|
undelivered (wake/re-drain class; wait_wake's Lagged log speaks to
|
|
feed lag there) vs no rows (publish-visibility class). Test
|
|
harness only; no engine-state change.
|
|
- The deadline stays 15 s (the review's bound of record; unchanged
|
|
— only the wait shape moved from poll to park).
|
|
|
|
## Summary
|
|
|
|
**Landed**: `must_recv_event` re-shaped from the 20 ms `try_recv`
|
|
polling loop to a parked `recv().await` under a 15 s timeout wrapper
|
|
(stream_tests.rs — the sole pg copy; the SQLite twin untouched, this
|
|
task is pg-specific), with a self-diagnosing deadline panic
|
|
(`read_since` presence check naming the failure class); the stream
|
|
bridge's `Lagged(n)` arm now logs the lag (`stream.rs` `wait_wake`,
|
|
stream name threaded in — the surfaced-not-silent posture now true
|
|
at all wake-feed bridges); the literal bridge-side diagnostic counter
|
|
skipped with reason above (the deadline diagnosis covers the
|
|
discrimination test-side).
|
|
|
|
**Verified**: the pg stream module 21/21 green against the harness
|
|
server (one full module run, ~19 s at `--test-threads=4`);
|
|
`tx_publishes_compose_with_the_handle` 5 solo re-runs green
|
|
(determinism re-check under the new parked shape); two consecutive
|
|
full pg harness runs green (121 lib + 25 contract-suite + 9 schema,
|
|
~72 s each); `cargo test -p alkstore-postgres` green server-less
|
|
(harness rows skip cleanly); workspace `cargo test` green (12 test
|
|
binaries); `cargo clippy --all-targets -- -D warnings` clean;
|
|
`cargo fmt --check` clean. |