postgres_cdc: fail the snapshot barrier on nack (CON-504) - #4685
Conversation
…over rejected rows The snapshot ack barrier (#4584) treated a nack exactly like an ack: the ackFn ignored its error argument, so with auto_replay_nacks disabled a rejected snapshot batch released the barrier, the replication slot was promoted, and the rejected rows were skipped forever on restart. A nacked batch also resolved its checkpoint slot, letting later acks acknowledge LSNs past undelivered streaming rows. A nack now never resolves the checkpoint (nothing can be acknowledged past its rows, with an error log identifying the pinned position), and a nacked snapshot batch fails the promotion barrier: the input restarts without promoting the slot and the snapshot re-runs. The gate error resets per connection attempt so one nack cannot livelock reconnects. Same pattern as the review-hardened mssqlserver (#4677) and oracledb (#4675) gates.
| // The input outlives reconnects: clear any nack recorded by a previous | ||
| // snapshot attempt so the promotion barrier judges only this run. |
There was a problem hiding this comment.
Feels somewhat of a redundant comment.
| iamAuthEnabled bool | ||
| } | ||
|
|
||
| func (p *pgStreamInput) recordSnapshotNack(err error) { |
There was a problem hiding this comment.
Is it worth moving recordSnapshotNack, snapshotNackError and resetSnapshotGate to the bottom of the file? Keeping the important, relevant or public ones (Connect, ReadBatch and Close) at the top?
|
Closing: this PR's premise misread the framework contract. |
Part of CON-504 (CDC at-least-once / ack-gated progress).
Follow-up to #4584: the snapshot ack barrier treated a nack exactly like an ack. The ackFn ignored its error argument, so with
auto_replay_nacks: falsea rejected snapshot batch released the barrier, the replication slot was promoted, and the rejected rows were skipped forever on restart. A nacked batch also resolved its checkpoint slot, letting later acks acknowledge LSNs past undelivered streaming rows.This applies the pattern hardened through the review rounds on #4675 and #4677:
checkpoint_limit-fills consequence so operators can attribute it.Proof of Work
TestIntegrationPostgresSnapshotNackFailsBarrier: the consumer nacks the first snapshot batch (auto_replay_nacks: false), asserting the slot is not promoted while the snapshot is rejected; the input then self-heals — re-runs the snapshot, delivers every row, and only then promotes. Verified it fails against the pre-fix code: the rejected rows are never redelivered (the snapshot should have re-run and re-delivered every row after the nack, 5m timeout).