Change Data Capture (CDC)¶
Change Data Capture tracks mutations (creates, updates, deletes) as they happen, giving you a replayable event log per entity. Use it for undo/redo, audit trails, syncing changes to a remote server, or replication.
Enabling CDC¶
CDC is opt-in to avoid overhead on the mutation hot path. There are two levels of control:
- Database-wide default: set at construction time or toggled at runtime.
- Per-session override: each session can opt in or out regardless of the database default.
Rust¶
use grafeo_engine::{Config, GrafeoDB};
// Enable CDC for all sessions via config
let db = GrafeoDB::with_config(Config::in_memory().with_cdc())?;
// Or toggle at runtime (affects future sessions only)
db.set_cdc_enabled(true);
// Per-session override
let tracked = db.session_with_cdc(true); // CDC on for this session
let untracked = db.session_with_cdc(false); // CDC off for this session
let default = db.session(); // follows database default
Python¶
from grafeo import GrafeoDB
# Enable at construction
db = GrafeoDB(cdc=True)
# Or toggle at runtime
db.enable_cdc()
db.disable_cdc()
# Check current state
print(db.cdc_enabled) # True / False
Node.js / TypeScript¶
import { GrafeoDB } from '@grafeo-db/node';
const db = GrafeoDB.create();
db.enableCdc();
console.log(db.isCdcEnabled); // true
db.disableCdc();
C¶
#include "grafeo.h"
GrafeoDatabase* db = grafeo_open_memory();
grafeo_set_cdc_enabled(db, true);
bool enabled = grafeo_is_cdc_enabled(db);
Querying change history¶
Once CDC is enabled, every mutation records a ChangeEvent with:
| Field | Description |
|---|---|
entity_id | Node or edge ID |
kind | Create, Update, or Delete |
epoch | Commit epoch (monotonically increasing) |
timestamp | HLC timestamp (hybrid logical clock) |
before | Property snapshot before the change (if any) |
after | Property snapshot after the change (if any) |
labels | Labels at create time (nodes only) |
Per-entity history¶
// Full history for a node
let events = db.history(node_id)?;
// History since a specific epoch
let recent = db.history_since(node_id, EpochId::new(42))?;
Range queries¶
// All changes across all entities in an epoch range
let changes = db.changes_between(EpochId::new(10), EpochId::new(50))?;
Python¶
history = db.node_history(node_id)
history = db.edge_history(edge_id)
history = db.node_history_since(node_id, since_epoch=42)
changes = db.changes_between(start_epoch=10, end_epoch=50)
Node.js¶
const history = await db.nodeHistory(nodeId);
const history = await db.edgeHistory(edgeId);
const history = await db.nodeHistorySince(nodeId, 42);
const changes = await db.changesBetween(10, 50);
Transaction semantics¶
CDC events are buffered during a transaction:
- On commit, buffered events are flushed to the CDC log with the commit epoch assigned.
- On rollback, buffered events are discarded.
- On rollback to savepoint, events after the savepoint are truncated.
This means the CDC log only contains events from successfully committed transactions.
Performance considerations¶
When CDC is disabled (the default), there is zero overhead on the mutation path. No HLC timestamps are generated, no events are buffered, and no store wrapping occurs.
When enabled, each mutation incurs:
- One HLC timestamp generation (
SystemTime::now()syscall + atomic CAS) - One event buffer push (mutex-protected
Vec) - Property snapshot allocations for before/after state
For bulk data loading or benchmarks, disable CDC or use session_with_cdc(false) to avoid this overhead.
Feature flag¶
CDC requires the cdc feature flag at compile time. The lpg, ai and enterprise profiles include it. The edge (WASM) profile does not.