Change data capture, applied to documents rather than rows
In short
Change data capture is the practice of identifying which records changed since a known point, so that downstream work is proportional to the change rather than to the size of the corpus. A database can do this exactly, by reading its own write log. A document corpus usually cannot, so you choose a signal — an event, a timestamp or a hash — and accept what that signal cannot see.
Key takeaways
- CDC makes the cost of a run proportional to what changed, not to the size of the corpus.
- Log-based capture is exact because the log is the database's own record of every commit.
- Documents have no such log, so all 3 document mechanisms are approximations with known gaps.
- Deletes are the hardest signal: an absent record emits nothing and needs a full listing to spot.
- Whatever you pick, a periodic full comparison is the only part of the design that self-heals.
Change data capture is the practice of identifying which records changed since a known point, so that downstream work is proportional to the change rather than to the size of the corpus. Reprocessing 40 documents instead of 400,000 is the point, and the rest of the design follows from how confidently you can name those 40.
The term came from database engineering, where it can be exact. A relational engine writes every committed change to a durable log before treating it as done — PostgreSQL calls that the write-ahead log, MySQL the binary log. Reading it yields every insert, update and delete in commit order, nothing missed and nothing inferred.
What a table row has that a document does not
A shared drive, a mailbox, an SFTP dump or a content system rarely offers anything comparable. Some expose a change feed, many expose a listing with a modification time, plenty expose nothing beyond a scheduled export. Three properties the database log gives away for free go missing.
- Completeness. The log records every commit by construction. A listing records state at the moment you asked, so a change made and undone between 2 polls leaves no trace.
- Ordering. The log is a sequence, so 2 edits to one record arrive in order. A poll returns a snapshot, and a document edited twice shows only the second edit.
- Deletion. A removed row appears in the log as a delete. A removed document simply stops appearing, detectable only by listing everything and comparing — hence making a delete travel all the way to the last cache.
The three places a document change signal comes from
Cost decides between them more often than teams expect: reading every object is fine across 40,000 documents and untenable across 40 million.
| Mechanism | How it learns | Cost per run | Blind to |
|---|---|---|---|
| Event or notification | The source pushes a message on change, or exposes a feed you read forward | Proportional to changes; near zero when nothing happened | Anything that happened while the subscription was down, and any dropped message |
| Timestamp or cursor poll | You ask for records modified since the value the last run stored | One listing call per page, whatever the real change count | Deletes, edits preserving the timestamp, edits inside the cursor's own second |
| Content hash comparison | You read every object, hash it, compare against the stored hash | A full read of the corpus, every run | Changes outside the hashed bytes, plus who changed it and in what order |
What a capture step owes the rest of the pipeline
Capture is not finished when it emits a list of changed keys. Four obligations follow, and dropping any of them produces a failure that surfaces weeks later somewhere else.
- Emit an identity, not a path. Downstream needs a stable key to update against, or a document that moves folders arrives as a new record.
- Stamp each change with the run that saw it, so "which run introduced this passage" stays answerable — the argument in tracing an answer back to the record and the run that produced it.
- Advance the cursor only after downstream work succeeded. A cursor advanced at capture time turns one processing failure into a permanent gap.
- Schedule a reconciliation sweep. A full listing compared against what you hold, weekly or monthly, is the only part of the design that repairs its own misses.
Three jobs this term gets used to cover and does not
- It is not replication. Copying a changed record into a second store is the step after capture, and a tool doing both is doing 2 things.
- It is not conflict resolution. When 2 systems report conflicting updates to one entity, capture reports both and decides nothing — the precedence policy in the golden record in a messy source set settles it.
- It is not freshness. Detecting an edit within a second and indexing it overnight still serves a day-old answer, which is why the commitment is measured at serving time — what a freshness commitment obliges you to build.
One practical consequence for anyone deciding what to build. Where the source is mainstream and a maintained connector exists, capture is commodity plumbing; where it is a bespoke internal system, nobody else will ever write it and it is core work — the line drawn in splitting a stack into commodity and core, usually the first question on a product build. Both sit in data readiness and pipelines, part of the engineering library.
Frequently asked questions
Short answers to the follow-ups this page tends to raise.
What is change data capture in simple terms?
It is working out which records changed since a known point, so the next run processes only those. Without it every run reprocesses the whole corpus, affordable at 10,000 documents and impossible at 10 million. In a database it is exact, because the engine keeps a durable log of every commit. In a document corpus it is an approximation built from events, timestamps or hashes.
Why is change data capture harder for documents than for database rows?
Because documents have no guaranteed change log. A relational engine writes every commit to a write-ahead or binary log before acknowledging it, so reading that log yields every insert, update and delete in order. A shared drive typically offers a listing with a modification time and nothing more: no ordering, no record of edits later undone, no signal when a document is deleted.
How does a pipeline detect deleted documents?
Either the source emits a delete event, or you enumerate the full key set and treat what has vanished as removed. There is no third route, because a deleted record sends nothing and appears in no filtered listing. If you take the enumeration route, add a safety rule: a run where an unusually large share of keys has disappeared is more likely a truncated listing than a real mass deletion.
- change data capture
- ingestion
- definitions
- pipelines
The work behind this page
Builds from our portfolio that this page draws on.
AskVault
An AI internal knowledge-search platform that answers employee questions from your own docs — grounded in citations, with knowledge gaps surfaced and deflection tracked.
Productivity AIAI Lease Management
AI-powered commercial real estate lease management for multi-brand operators — automates lease data extraction, obligation tracking, and portfolio intelligence.
Real EstateRead next
- A freshness SLA is a promise about the worst case, not the averageFreshness is the age of the data behind an answer when it is served, held under a stated ceiling. Latency is how fast a run finishes, and the two can disagree by a week.definition
- Lineage: tracing one answer back to the record and the run that produced itLineage is three identifiers carried on every derived record: which record it came from, which version of the transform produced it, and which run wrote it.definition
- The golden record: which version of the truth the system is allowed to useA golden record is a per-field precedence policy, not a product and not a dataset. Buying a tool implements the rule; it never decides it.definition
- Someone updated the document and the system still serves the old textThe indexing job is rarely the culprit. A timestamp that never moved, a hash over the wrong bytes, or an edit the extractor discards all leave the pipeline correctly deciding there is nothing to do.diagnostic
- A subset of the PDFs came through as gibberish and nobody lookedA chunk of mojibake embeds happily, indexes happily and retrieves for nothing. No stage errors, so the only defence is a screen that reads the text before it is indexed.diagnostic
- An upstream field changed and the pipeline carried on regardlessA renamed source field does not raise an error. It returns nothing, coalesces to an empty string, and quietly hollows out every record ingested since — until someone plots completeness by day.diagnostic
Working on something in this space?
Tell us where you are in a sentence or two. We'll tell you honestly whether we're the right team, and what a sensible first slice of the work looks like.
Start the conversation