Curiosity
A horizontal timeline showing two runs: Full load and Delta only, with cursor markers and a note about cursor advancement.

Incremental sync

A full reload every run is expensive. Use a cursor to read only what changed.

var cursor = await state.LoadCursorAsync() ?? DateTimeOffset.MinValue;

await foreach (var row in source.ReadModifiedSinceAsync(cursor))
{
    // TryAdd · Link · RestrictAccess ...
    if (++count % 500 == 0) await graph.CommitPendingAsync();
}

await graph.CommitPendingAsync();
await state.SaveCursorAsync(DateTimeOffset.UtcNow);  // ← only after a successful commit

Sync strategies:

Strategy Use when
Full refresh Small datasets; source has no timestamps
Watermark incremental Source has a reliable updated_at — most common
Change feed Source has a native event stream (Kafka, CDC, webhooks)
Reconciliation pass Needed to catch deletes and data drift

Deletes: the graph doesn't auto-remove nodes when source records disappear. Handle with:

  • A soft-delete flag on the source
  • A periodic reconciliation scan (graph.RemoveNode(uid) for missing records)
  • Delete events from a source webhook

Ingestion pipelines