Change data capture
Build a consistent database-to-index projection by joining a snapshot to a retained log and applying replayable changes.
On this page
The question for today
What you’ll understand
- Explain the boundary that joins an initial snapshot to ongoing changes.
- Design idempotent projection updates with source identity and ordering.
- Calculate log retention risk during a stalled change-data consumer.
A search index needs all current product rows plus every subsequent update. Polling a modification timestamp can miss a delete and can race with a transaction that commits between scans. Change data capture exposes database changes through a more deliberate boundary, often using its transaction log. It provides an auditable source-to-projection path. It does not automatically make two independent databases one synchronous transactional system.
Join the snapshot and log without a hole
An initial snapshot establishes the existing rows at a consistent source view. The change stream must continue from a position coordinated with that snapshot, retaining all changes needed while the scan runs. Simply copying rows and then asking for changes from now creates a gap. Starting log reading before the scan without reconciling the overlap can instead duplicate or overwrite newer state. A connector's documented snapshot algorithm defines how it bridges these intervals; implement that contract rather than inventing a timestamp approximation.
PostgreSQL logical decoding translates WAL into a logical change stream through an output plugin. Replication slots retain resources for consumers but are not unlimited archives. Table identity and replica-identity configuration affect what information is available for updates and deletes. A stable primary key makes downstream addressing simpler. A key change may require removing the old projection identity.
Make replay harmless without losing order
A connector may replay changes after failure when the last downstream progress record was not durable. Use a stable event identity or a source ordering token together with the entity key, and commit projection effects with their applied progress where possible. An idempotent upsert prevents a repeated assignment from duplicating a row, but an increment operation is not idempotent. A delete also needs replay handling so a delayed older update cannot resurrect an entity after deletion.
If multiple workers process one key out of order, a last-write-wins policy needs a valid comparable source version, not the consumer's arrival clock. Source positions may be ordered only within a partition or source timeline. Preserve source transaction boundaries where a projection requires atomic visibility of related changes, or explicitly expose eventual intermediate states. Do not claim a global transaction order after splitting tables into independent topic partitions without an additional coordinating protocol.
Operate the retention dependency
A stopped consumer can cause the source to retain WAL, increasing disk pressure even while the application database continues operating. Monitor source retention bytes, connector lag, destination freshness, and failed records as separate signals. A configurable retention cap may protect the database at the cost of invalidating the consumer's recovery position. If required history is gone, restarting from an arbitrary current position silently loses changes; re-snapshot through a consistent rebuild procedure instead.
Projection rebuilds should have a named generation. Load a new generation, replay to a chosen freshness point, validate counts and representative records, and atomically switch readers when the destination supports it. Keep source-to-destination reconciliation independent of the transport's health check. A flowing stream can still drop fields or ignore deletes. Test duplicate changes, source failover, snapshot interruption, and schema evolution.
Work it through
Plan a stalled connector response
A stalled connector pins 80 GB of WAL. The allocated retention budget is 200 GB and retained history grows by 20 MB/s. A clean rebuild takes 90 minutes. Use decimal units and assume no other retention users.
Available retention headroom is 200 − 80 = 120 GB, or 120,000 MB.
At 20 MB/s the headroom lasts 120,000/20 = 6,000 seconds, or 100 minutes.
A 90-minute rebuild leaves only ten minutes of modeled margin, excluding validation and traffic changes; starting a rebuild near exhaustion is unsafe planning.
If preserving the old slot is unnecessary after a verified replacement snapshot boundary exists, retire it through the documented procedure. Never discard history first and pretend the old projection can still resume without a gap.
The connector is a source-storage dependency with approximately 100 minutes of modeled headroom, not merely a stale dashboard.
The living notebook
Explore the model
Interactive lab · queue
Observe retained change backlog
Treat equal-sized items as unapplied change work and vary λ and μ in max(0,4+(λ−μ)t). The separate M/M/1 residence estimate assumes Poisson arrivals and exponential single-server service. This lab does not model source snapshots, WAL retention, event ordering, or idempotent projection writes.
- Fluid backlog
- 4
- Net rate · λ − μ
- −1 /s
- M/M/1 mean residence
- 1 s
Fluid backlog starts at 4 and follows max(0, 4 + (λ − μ)t). The separate M/M/1 residence-time formula assumes Poisson arrivals, exponential service and λ < μ. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA consumer needs excess capacity to catch up, and a valid retained source boundary to make that catch-up correct.
Pause & reason
Check your understanding
Your turn
Try it yourself
A projection has applied entity version 42, then receives version 41 and a duplicate version 42. Under a valid monotonically increasing per-entity source version rule, what should it do? What changes if version 43 is a deletion?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Compare source versions, preserve a deletion marker where old events can return, and distinguish replay from a genuinely new change.
Compare with the solution
Ignore version 41 as stale and version 42 as already applied. Apply version 43's deletion while retaining sufficient version or tombstone information to reject older updates. Otherwise a delayed version 41 might recreate the entity. This requires the stated per-entity ordering guarantee and an atomic update of projection state and applied version; a consumer-arrival timestamp is not an equivalent source version.
Keep the idea
Take it with you
- A consistent snapshot-to-log boundary prevents missing or double-counted source intervals.
- Idempotency and source ordering solve different replay and staleness problems.
- Consumer lag can threaten source disk space and may require a verified projection rebuild.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.