api: backfill api poc - #476
Conversation
|
Note: the inline-comment service was unavailable during this review (repeated Commits
Review The PR adds an optional backfill phase to
|
| select { | ||
| case <-done: | ||
| case <-time.After(60 * time.Second): | ||
| r.mgr.Logger().Warn("Waiting on pending backfill acks for input %v; verify downstream batching policy has a period to flush partial batches.", r.typeStr) | ||
| <-done | ||
| } |
There was a problem hiding this comment.
Backfill ack barrier can hang indefinitely and a graceful shutdown cannot break it.
The ack goroutine spawned below only unblocks on rChan or r.shutSig.HardStopChan() (async_reader.go#L259-L265). So if a trailing partial backfill batch is sitting in a downstream batching policy with no period — exactly the scenario the new warning describes — pendingAcks.Wait() never returns, and the <-done after the 60s warning blocks forever.
Crucially this differs from the pre-existing pendingAcks wait in loop(), which only runs at shutdown. Here the barrier runs mid-stream while the input is otherwise healthy, and because the backfill loop has already stopped producing, nothing will ever fill that downstream batch. TriggerStopConsuming() does not release it (the goroutine ignores SoftStopChan), so loop() never reaches r.shutSig.TriggerHasStopped() and WaitForClose blocks until its own context deadline. Only TriggerCloseNow() escapes.
Suggested fix: also select on r.shutSig.SoftStopChan() in the wait (returning ok = false, same as the hard-stop case) so a graceful shutdown can abort a stuck backfill barrier, and/or re-emit the warning periodically rather than once.
|
|
||
| if interruptedByHardStop.Load() { | ||
| ok = false | ||
| } |
There was a problem hiding this comment.
A nacked backfill batch still lets BackfillComplete fire, contradicting the guarantee stated above.
The doc comment on runBackfillPhase says "ok must be false so the caller never calls BackfillComplete for unconfirmed rows", but the only thing that clears ok is a hard stop. If a backfill batch is delivered and then permanently nacked downstream, res != nil is passed to aFn, pendingAcks drains normally, ok stays true, and BackfillComplete is invoked at async_reader.go#L279-L287 — so a CDC connector persists its post-backfill resume position for rows that never landed. That is silent snapshot data loss.
Note this is only masked when the input is wrapped in AutoRetryNacksBatched; a BackfillAsync reader that does not use it (nothing in the API requires it) gets the unsafe behaviour. Either the nack result should be tracked and clear ok, or the guarantee in the comment and in BackfillCompleter's godoc (interface.go#L426-L434) needs to be reworded to make clear it means "settled", not "delivered".
| r.readBackoff.Reset() | ||
| mRcvd.Incr(int64(msg.Len())) | ||
| r.mgr.Logger().Trace("Consumed %v backfill messages from '%v'.\n", msg.Len(), r.typeStr) | ||
|
|
||
| resChan := make(chan error, 1) | ||
| tracing.InitSpans(r.mgr.Tracer(), traceName, msg) |
There was a problem hiding this comment.
input_latency_ns is not recorded for the backfill phase.
The steady-state loop this block was adapted from captures startedAt := time.Now() before dispatch and reports mLatency.Timing(time.Since(startedAt).Nanoseconds()) in the ack goroutine (async_reader.go#L333-L357). The backfill loop increments mRcvd but never touches mLatency, so the entire snapshot phase — often the longest-running and slowest-acking part of a CDC input — is invisible in the input latency metric.
Suggested fix: capture startedAt here and record mLatency.Timing(...) in the backfill ack goroutine, matching the steady-state loop.
| // ErrBackfillComplete is returned by a SnapshotBatchInput's | ||
| // BackfillReadBatch method to indicate that its snapshot phase has been |
There was a problem hiding this comment.
The godoc for this new exported symbol references SnapshotBatchInput, which does not exist anywhere in the tree. The interface introduced by this PR is BackfillBatchInput (input.go#L478-L484). Since this is public API documentation, it should name the real type.
The same snapshot/backfill mix-up shows up in the error string "snapshot phase complete", and in interface.go's "distinct initial backfill (backfill) phase" — worth a sweep before this leaves POC state.
|
Commits
Review Reviewed the new optional backfill API: The findings below are about the ack barrier's failure modes, which are the part of this design most worth settling before it stops being a POC.
|
The Problem
With CDC solutions there's a common requirement to snapshot (backfill) existing data before switching to realtime streaming - two logical steps with different checkpointing needs. Snapshotting data is usually one atomic operation which once completed checkpoints the current watermark before switching to realtime streaming and continually checkpointing the watermark as the cursor moves ahead through the WAL.
Currently we have a single
ReadBatchmodel where managing the tracking of snapshot completion and gaining confidence in delivery before watermarking the current position results in horrible, error prone code that each connector has to own. Such challenges can include:Proposed Solution
This change (currently a proof-of-concept to validate its usefulness) adds a new optional API for a "backfill" stage that gets called before the main
ReadBatchloop begins, creating a clearer, codified separation between the snapshot and streaming stages.The optional API could look something like this: