Load Data Incrementally
Incremental loading makes each pipeline run in the C3 Agentic AI Platform read only the data that changed since the previous run. Runs stay fast, and the load on your source system stays low as the data grows. A source system holds the connection to an external store, and a source collection names the files, table, or topic that a pipeline reads from it.
Each source family (file, SQL, API, or streaming) tracks change its own way. File and streaming collections load incrementally without setup. SQL and API collections need change data capture (CDC): a stored checkpoint that records the last position loaded. The next run then reads only what comes after that position. You can set up CDC for a SQL collection, reset its checkpoint to reload its data, and reload a small table in full on every run. You can also speed up file listings with a native change stream and replace the default read with your own logic.
In Data Fusion, you turn on CDC for a SQL source collection on the canvas. For those steps, see Enable Change Data Capture for a SQL Source via UI.
Prerequisites
Make sure you have:
- A working SQL pipeline: a source system, a source collection, and a transform to a target entity Type. The pipeline also needs a source Type that describes one row of the table. Ingest a Snowflake Table Programmatically builds such a pipeline.
- Access to the C3 AI Console of your application. You run every snippet on this page in the Console.
The examples use a SQL source system named ordersDb, a source Type named OrderSqlSource, and a collection named ordersDbColl. The file source example uses a Google Drive collection named driveContactsColl. Replace these names with your own.
How each source family tracks change
Each source family tracks change differently, and only SQL and API collections need configuration:
| Source family | How it tracks change | You configure |
|---|---|---|
| File | After a run processes a file, the platform moves it to the collection's archive folder, and later runs skip it | Nothing. Optionally, set cdcOrder to $native for providers with a change stream |
| SQL | A CDC checkpoint records the highest value loaded from a field you name | cdcOrder on the collection |
| API | A CDC checkpoint passed to read logic you write | cdcSourcesOverride and cdcOrder on the collection, and staging with a stage file source collection |
| Streaming | The platform processes each message as it arrives on the topic | Nothing |
The checkpoint is a SourceCollection.Cdc.Checkpoint record, one per collection. Every source collection has the cdcOrder, fullSync, and cdcSourcesOverride fields that this page describes, but only SQL collections act on fullSync.
Configure change data capture for a SQL source
To set up CDC, name the field to track, run the collection, and confirm the checkpoint:
Name the field that defines "newer than last time" when you create the collection. The field's values must only ever increase (the field must be monotonic), such as an auto-incrementing key, a modification timestamp, or a load batch number:
JavaScriptSqlSourceCollection.make({ name: 'ordersDbColl', source: OrderSqlSource, sourceSystem: SqlSourceSystem.forName('ordersDb'), cdcOrder: 'id' }).upsert();cdcOrdernames the field on the source Type, not the database column; the source Type maps each field to its column.cdcOrdertakes a sort order. A field name alone, such as'id', means ascending order, which is the default: each run reads the rows whose value is greater than the checkpoint.Run the collection. The first
process()call reads every row and records the highestcdcOrdervalue it loaded:JavaScriptSqlSourceCollection.forName('ordersDbColl').process(DataIntegSpec.make({}));After the run completes, read the checkpoint:
JavaScriptSqlSourceCollection.forName('ordersDbColl').cdcCheckpoint().value; // { "id": "2" }The value is a map keyed by the
cdcOrderfield name. In this example, the table holds rows withidvalues 1 and 2, so the checkpoint stores 2. Before the first run completes,cdcCheckpoint()returns null.Run
process()again on an unchanged table to confirm that CDC works. The run loads no rows, creates no new SourceStatus records (SourceStatus records the outcome of each run), and leaves the checkpoint at the same value.
From then on, each run reads only rows past the checkpoint and advances it. An empty run means the source did not change. If you expected new rows, confirm that they have a higher cdcOrder value than the checkpoint.
If you change cdcOrder on a collection that does not stage its data, the platform keeps the old checkpoint. Reset the checkpoint after you change cdcOrder, as described in Reset the checkpoint to reload.
A source Type (an entity Type that mixes External) supports one SqlSourceCollection per application. A second collection on the same source Type makes processing fail for both. The error reads More than one SqlSourceCollection found for external type .... To change how a collection loads, update its fields instead of creating a second collection.
Reset the checkpoint to reload
Reset the checkpoint when you need to re-read data it already passed. For example, reset the checkpoint after you fix a transform bug, when you backfill a target you truncated, or when a chunk fails after the checkpoint advanced.
You cannot rewind the checkpoint to a value you choose. The supported reset is Reset Run State in Data Fusion. Reset Run State clears the checkpoint, so the next run reads every row in the table.
In Data Fusion, select Execute pipeline on the SQL collection node.
In the Execute Pipeline dialog, turn on Reset Run State, then select Execute.
Reset Run State clears more than this collection's checkpoint. Reset Run State also clears all queued work and all run status records in the application, for every collection. Use Reset Run State only when no other pipeline in the application has work in progress.
The run re-reads every row in the table, and the checkpoint advances again. By default, the target merges each reloaded row by its ID: a matching row receives the new non-empty field values. Reloaded rows update their existing copies only when the transform sets a stable ID on each target row. A transform that generates a new ID on every run creates new rows instead.
Reload everything on every run with fullSync
Set fullSync on a small table whose rows change in place, such as a reference or dimension table. Checkpoint tracking misses in-place updates: an updated row keeps its old cdcOrder value, so later runs skip it. With fullSync on, every run re-reads the entire table, and the collection ignores cdcOrder and the checkpoint:
SqlSourceCollection.forName('ordersDbColl')
.withFullSync(true)
.upsert();Keep cdcOrder set when you turn on fullSync, as in this example, unless the collection stages its data into files first.
Only SQL collections act on fullSync, and a limit applies. The platform refuses to fully sync a table larger than maxFullSyncRowCount, a field on the collection with a default of 100,000 rows. The platform counts the source rows each time the collection processes. A collection whose table exceeds the limit therefore saves without an error, and then every run fails with an error like this one:
SqlSourceCollection ordersDbColl cannot enable fullSync: source type OrderSqlSource
has 250000 rows, exceeding the configured maxFullSyncRowCount of 100000.To fully sync a larger table, raise maxFullSyncRowCount on the collection. A full re-read of a large table on every scheduled run costs time and load on the source, so the limit blocks full re-reads of large tables by default.
Use native change streams for file sources
Set cdcOrder to $native on a file collection so that each sync lists only the files that changed. Without $native, file collections still skip files they already processed, but every sync lists the full inbox. On a large drive, the full listing takes most of the sync time.
The $native value works with file systems that expose a native change stream: Google Drive (changes.list) and OneDrive or SharePoint (the Microsoft Graph delta API):
FileSourceCollection.forName('driveContactsColl')
.withCdcOrder('$native')
.upsert();With $native set, each sync stores the provider's delta token, a marker of the last change the provider reported, in the collection's checkpoint. For these collections, the checkpoint holds that token instead of a field value. The next sync asks the provider for only the files that changed since that token.
You can set $native on any file collection. File systems without a change stream, including Amazon S3, Google Cloud Storage, and Azure Blob Storage, ignore it and return a full listing. For those file systems, the platform creates no checkpoint. The pipeline behaves as before, without the faster listing. A file written right before a sync can appear one sync later; it loads on the next run.
If the provider rejects the stored token as invalid, the sync does not fail. The sync falls back to a full listing, the same listing a collection without $native makes.
The token advances only after every batch of the sync succeeds. If a batch fails, the token stays at its old value, and the next sync replays the same changes. A sync that finds no changes advances the token at once. To confirm that the stream is in use, read the token after each sync and check that it changes:
FileSourceCollection.forName('driveContactsColl').cdcCheckpoint().value.deltaToken;Change streams are eventually consistent. A sync that runs right after a write can find no changes, and the file then loads on a later sync. For Google Drive, schedule syncs at least a minute or two apart instead of back to back.
The change stream also reports files that were deleted at the source. The platform does not remove the records that earlier runs loaded from those files. To act on deletions, run your own cleanup logic after the pipeline. For the pattern, see Run BatchJobs Before and After a Pipeline.
Write custom checkpoint logic with cdcSourcesOverride
Write your own read logic when the source has no field whose values only increase. For example, the source is an API that pages by cursor, or a table keyed on business identifiers. The cdcSourcesOverride field on the collection holds a Lambda that receives the current checkpoint and returns a stream of source records to load. When the field is set, the collection uses it instead of the default read on every process() run, manual or scheduled.
Your lambda decides what to read from the checkpoint. After the run, the platform advances the checkpoint to the highest cdcOrder value among the records your lambda returned. Set cdcOrder on the collection and populate that field on every record. Records with no value in the field do not move the checkpoint, and the checkpoint never moves backward.
An ApiSourceCollection uses cdcSourcesOverride for CDC: its lambda field reads the data, and the override reads only the changed data. An API collection that sets the override stages the records it reads, so it also needs a stage file source collection.
No incremental mechanism detects rows deleted at the source. A checkpoint sees only rows that are new or past its position, so a row deleted at the source stays in your target. A fullSync run does not help: a full sync re-reads and upserts every source row, but it does not remove target records whose rows were deleted. To reflect deletes, have the source mark rows as deleted, and filter on that flag in the transform. You can also run a cleanup job after the load that removes target records the source no longer has. For the pattern, see Run BatchJobs Before and After a Pipeline.
Fix common problems
Each row starts from what you see:
| Symptom | Cause | Fix |
|---|---|---|
| A run loads nothing and creates no status records | The checkpoint is already at the newest row | If the source did not change, no action is needed. If the source did change, confirm that the new rows have a higher cdcOrder value than the checkpoint, or reset the checkpoint to reload |
| Updated rows never load again | The rows kept their old cdcOrder value, so the checkpoint skips them | Use a modification timestamp as cdcOrder, or fullSync for small tables |
Every run fails with cannot enable fullSync ... exceeding the configured maxFullSyncRowCount | The source table outgrew the limit; the platform checks the row count each time the collection processes | Raise maxFullSyncRowCount, or switch back to checkpoint CDC |
Processing fails with More than one SqlSourceCollection found for external type | Two collections use the same source Type | Keep one collection per source Type and update its fields instead |
$native is set, but syncs are no faster | The file system has no native change stream, so it returns a full listing | Expected on Amazon S3, Google Cloud Storage, and Azure Blob Storage; $native speeds up only providers with a change stream |
| Rows deleted at the source still appear in the target | No CDC mechanism captures deletes, and fullSync only upserts | Use soft-delete flags at the source and filter on them in the transform, or remove stale target records after the load |