Expand description
CompletionBackend implementation for Postgres (Wave 4h / Q1).
Transport design (per RFC-v0.7 Q1 adjudication):
- Durable source of truth is the
ff_completion_eventoutbox table — abigserialevent_id column sequenced across all 256 hash partitions gives a single total order. pg_notify('ff_completion', event_id::text)fires from theff_completion_event_notify_trgtrigger (seemigrations/0001_initial.sql§Section 8). NOTIFY is the wake signal only — the payload carries only the event_id cursor so we sidestep Postgres’s 8 KB NOTIFY payload cap and the natural coalescing (pgcollapses duplicate payloads on the same channel within a transaction).- Each subscriber owns a long-lived tokio task holding a
PgListener(one dedicated pg connection) + issuesSELECT ... WHERE event_id > $last_seenon every wake to catch bursts NOTIFY coalesced. - On LISTEN-connection loss (pg restart, PgBouncer eviction,
network drop),
PgListener::recv()surfaces an error; we re-establish LISTEN + replay fromlast_seen_event_id. - Subscriber drop ends the task via
tx.closed().
Missed NOTIFY between commit and LISTEN start is covered by
dependency_reconciler’s scan — out of scope here.
Coordination with Wave 4e (stream LISTEN/NOTIFY via
super::listener::StreamNotifier): completion subscribers own
their own PgListener instance; they do NOT share the
StreamNotifier task. Completion fanout is low cardinality
(engines subscribe, not every execution) so a per-subscriber
connection is cheap — reuse is a future optimisation once Wave 4e
stabilises.
Structs§
- Postgres
Completion Stream - Stream adapter returned to callers. Holds the
mpsc::Receiverplus the task handle that feeds it; dropping the stream drops the receiver,tx.closed()fires in the task, and the task exits — which aborts the ownedJoinHandlecleanly on drop.
Constants§
- COMPLETION_
CHANNEL - Channel name fired by
ff_notify_completion_event()after everyff_completion_eventINSERT.