postgres_cdc: add signalling support#4576
Conversation
| if msg.LSN != nil { | ||
| if resolveFn, err := cp.Track(ctx, msg.LSN, 0); err == nil { | ||
| if maxLSN := resolveFn(); maxLSN != nil && *maxLSN != nil { | ||
| if err := pgStream.AckLSN(ctx, **maxLSN); err != nil { | ||
| p.logger.Warnf("failed to ack signal LSN: %s", err) | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
This inline ack can advance Postgres's confirmed flush LSN past data that hasn't been delivered downstream yet, breaking at-least-once delivery (CONTRIBUTING §5.4.2).
Data messages processed earlier in the loop are only added to the batcher (batcher.Add) and are not tracked in the checkpointer until flushBatch runs after the loop. Here the signal is tracked with cp.Track(ctx, msg.LSN, 0) and immediately resolved via resolveFn(). When there are no other in-flight (tracked-but-unresolved) batches, resolveFn() returns the signal's LSN and AckLSN confirms it to Postgres — even though lower-LSN rows are still sitting unflushed in the batcher (and rows for tables not in data-collections won't be recovered by the re-snapshot).
Failure scenario: batcher holds data1, data2 (batch policy not yet triggered, all prior flushed batches already acked), then a signal row is processed. AckLSN(signalLSN) advances confirmed_flush_lsn past data1/data2. A crash before those are delivered+acked downstream loses them permanently, since Postgres won't resend anything <= signalLSN.
The signal LSN should be gated behind the same downstream-ack path as the buffered data (e.g. flush the batcher first so buffered rows are tracked ahead of the signal), rather than acked immediately.
| if pending, signal := p.controlSig.IsPending(); pending && signal.IsSnapshot() { | ||
| p.logger.Infof("%q signal pending, triggering re-snapshots", signal.Type) | ||
|
|
||
| p.streamConfig.StreamOldData = true | ||
| p.streamConfig.ForceSnapshot = true | ||
| defer func() { p.streamConfig.ForceSnapshot = false }() | ||
|
|
||
| p.streamConfig.SnapshotTables = signal.TableNames(p.streamConfig.DBSchema) | ||
| defer func() { p.streamConfig.SnapshotTables = nil }() | ||
|
|
||
| p.controlSig.Reset() |
There was a problem hiding this comment.
p.controlSig.Reset() clears the pending signal before the stream that consumes it is successfully created. If NewPgStream (line 479) fails — a routine, expected condition for a network-backed CDC connector on reconnect — Connect returns an error and the framework retries, but IsPending() is now false, so the else-branch runs and no re-snapshot is performed.
If the signal row's LSN was already acked in the prior stream (so streaming resumes past it and the row is never re-read), the user's execute-snapshot request is silently dropped with no warning logged. Reset() should only be called once the stream that will act on the signal has been successfully constructed.
|
Commits Review
|
| - task: cdb:setup | ||
| - cmd: echo "Inserting test data, please hold..." | ||
| - task: sqlcl:pdb:data:users | ||
| # - task: sqlcl:pdb:data:users |
There was a problem hiding this comment.
This comments out the test-data insertion step in the Oracle benchmark setup. Two problems:
- It is unrelated to this PR's
postgres_cdcsignalling feature — changes should stay within the scope agreed for the PR (CONTRIBUTING.md §3.1.1). - Disabling the data load means the Oracle benchmark no longer inserts any rows.
This looks like an accidental local-debugging leftover (correlates with the temp commit) — please revert.
| n % 100, | ||
| ((n % 1000) + (n % 100) / 100.0)::decimal(10,2) | ||
| FROM generate_series(1, 150000) AS n; | ||
| FROM generate_series(1, 1) AS n; |
There was a problem hiding this comment.
The benchmark seed is reduced from generate_series(1, 150000) to generate_series(1, 1) here (and products.sql from 150000 to 100). This shrinks the dataset to a trivial size, which defeats throughput benchmarking — certification requires benchmarks run across throughput levels to establish CPU/memory trendlines (CONTRIBUTING.md §1.3.4–§1.3.5).
This appears to be an accidental local-debug leftover — please restore the original row counts.
| # processors: | ||
| # - benchmark: | ||
| # interval: 1s | ||
| # count_bytes: true | ||
| file: | ||
| path: "./benchmark_results.json" | ||
| codec: lines | ||
| # drop: {} | ||
|
|
||
| logger: | ||
| level: INFO | ||
| level: DEBUG |
There was a problem hiding this comment.
These edits look like committed local-debugging state rather than an intentional benchmark config: the benchmark processor + drop output are commented out and replaced with a file sink, the batching count: 1000 was removed, and the logger is switched to DEBUG. Writing every record to a file and emitting debug logs will skew the benchmark measurements this harness exists to produce (CONTRIBUTING.md §1.3.4).
Please restore the benchmarking output (benchmark processor / drop) and INFO logging.
|
Commits
The first commit Review The core signalling implementation (
|
This change features a new signal channel (backed by PostgreSQL) that allows you to signal the connector to perform a snapshot on an adhoc basis.
This process will: