Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Cassandra Change Data Capture (CDC)

Change Data Capture (CDC) provides a mechanism for capturing row-level changes in Cassandra tables. When enabled, CDC creates hard links from commit log segments to a dedicated directory, allowing external systems to consume mutations as they occur.

CDC was introduced in Cassandra 3.8 and is designed for integration with streaming platforms, real-time analytics, and cross-system data synchronization.


CDC operates by leveraging the existing commit log infrastructure. Rather than modifying the write path, it creates hard links to commit log segments containing mutations for CDC-enabled tables.

Cassandra NodeCommit Log DirectoryCDC Raw DirectoryStorage EngineMemtableCommitLog-7-001.logCommitLog-7-002.logCommitLog-7-001.log(hard link)CommitLog-7-001_cdc.idxCommitLog-7-002.log(hard link)CommitLog-7-002_cdc.idxWrite RequestCDC Consumer(Debezium, Pulsar, Custom)Same inode as commit logZero-copy operationContains:- Persisted offset (integer)- "COMPLETED" when done1. Append2. Inserthard link(if CDC table)3. Processoffset info
  1. Write arrives: Mutation is written to commit log and memtable (normal write path)
  2. Segment creation: When a new commit log segment is created, if cdc_enabled=true, a hard link is created in cdc_raw_directory
  3. Index file creation: On segment fsync, a <segment_name>_cdc.idx file is created containing the byte offset of persisted data
  4. Segment completion: When the segment is fully flushed, "COMPLETED" is appended to the index file
  5. Consumer processing: External consumers read segments from cdc_raw_directory, respecting offset values for durability
  6. Cleanup: Consumers must delete processed segments; Cassandra does not automatically clean up CDC files

CDC is configured at two levels: node-wide settings in cassandra.yaml and per-table settings via CQL.

# Enable CDC functionality node-wide
cdc_enabled: true
# Directory for CDC commit log hard links
# Should be on separate spindle from data directories
# Must be on same filesystem as commitlog_directory
cdc_raw_directory: /var/lib/cassandra/cdc_raw
# Maximum space for CDC logs before writes are rejected
# Default: min(4096 MiB, 1/8 of volume space)
# Cassandra 4.1+: cdc_total_space (with size unit, e.g., 4096MiB)
# Cassandra 4.0: cdc_total_space_in_mb (integer MB)
cdc_total_space: 4096MiB
# How often to check CDC directory space usage
# Default: 250ms
# Cassandra 4.1+: cdc_free_space_check_interval (with duration, e.g., 250ms)
# Cassandra 4.0: cdc_free_space_check_interval_ms (integer milliseconds)
cdc_free_space_check_interval: 250ms

CDC Configuration by Version:

ParameterCassandra 4.0Cassandra 4.1+Default
CDC space limitcdc_total_space_in_mb (integer)cdc_total_space (e.g., 4096MiB)min(4096 MiB, 1/8 volume)
Space check intervalcdc_free_space_check_interval_ms (integer)cdc_free_space_check_interval (e.g., 250ms)250ms
ParameterDefaultDescription
cdc_enabledfalseEnables CDC node-wide. When true, writes to CDC-enabled tables are rejected if cdc_raw_directory exceeds space limit
cdc_raw_directory$CASSANDRA_HOME/data/cdc_rawDestination for commit log hard links
cdc_total_spacemin(4096MiB, 1/8 volume)Space threshold before write rejection
cdc_free_space_check_interval250msFrequency of space usage recalculation

Enable CDC on individual tables:

-- Enable CDC when creating table
CREATE TABLE events (
event_id UUID,
event_time TIMESTAMP,
event_type TEXT,
payload TEXT,
PRIMARY KEY (event_id)
) WITH cdc = true;
-- Enable CDC on existing table
ALTER TABLE events WITH cdc = true;
-- Disable CDC
ALTER TABLE events WITH cdc = false;

Files in cdc_raw_directory are hard links to commit log segments. They share the same inode as the original commit log file, meaning:

  • Hard links initially consume no additional disk space for the data itself
  • Reading from CDC directory reads the same data as the commit log
  • Modifications to the original segment are visible through the hard link

Disk Space Impact

While hard links share data with the original commit log segment, they prevent segment reuse. Cassandra cannot recycle commit log segments that have CDC hard links until consumers delete the hard links. This effectively increases disk utilization over time if consumers fall behind—space is held until both the original segment is flushed and the CDC hard link is deleted.

cdc_raw_directory/
├── CommitLog-7-1702987654321.log # Hard link to commit log segment
├── CommitLog-7-1702987654321_cdc.idx # Index file with offset
├── CommitLog-7-1702987654322.log
├── CommitLog-7-1702987654322_cdc.idx
└── ...

Each commit log segment in the CDC directory has a corresponding index file:

# While segment is being written
1234567
# After segment is complete
1234567
COMPLETED
StateIndex File ContentConsumer Action
In progressInteger offset onlyRead up to offset bytes only
CompleteOffset + "COMPLETED"Safe to read entire segment and delete

Durability Guarantee

Consumers must only parse data up to the offset specified in the index file. Data beyond that offset may not be durable and could be lost on crash recovery.


When cdc_raw_directory exceeds the configured space limit, Cassandra applies backpressure by rejecting writes to CDC-enabled tables.

Write mutation arrivesTable has cdc=true?yesnocdc_raw_directory < cdc_total_space_in_mb?yesnoAccept writeAppend to commit logCreate hard link in cdc_rawReject write"CDC is full. Writes toCDC-enabled tables arerejected until spaceis freed."Throw WriteTimeoutExceptionAccept writeNormal write path

Cassandra does not expose dedicated JMX metrics for CDC space usage. The CDC size tracking is internal to CommitLogSegmentManagerCDC. Monitor CDC space through filesystem monitoring:

Terminal window
# Check cdc_raw directory size
du -sh /var/lib/cassandra/cdc_raw/
# Count pending CDC segments
ls -1 /var/lib/cassandra/cdc_raw/*.log 2>/dev/null | wc -l
# Check for completed segments (ready for processing)
grep -l "COMPLETED" /var/lib/cassandra/cdc_raw/*_cdc.idx 2>/dev/null | wc -l
Monitoring ApproachDescription
Filesystem monitoringTrack cdc_raw_directory size with node_exporter or similar
Segment countCount .log files in CDC directory
Completion rateTrack ratio of segments with "COMPLETED" in .idx files
Log monitoringWatch for "CDC is full" rejection messages
ConditionAction
cdc_raw size > 50% of cdc_total_space_in_mbWarning: Consumer may be falling behind
cdc_raw size > 75% of cdc_total_space_in_mbCritical: Consumer is falling behind
cdc_raw size > 90% of cdc_total_space_in_mbEmergency: Write rejection imminent
"CDC is full" in logsWrites to CDC-enabled tables are being rejected

CDC consumers must:

  1. Poll for new segments: Watch cdc_raw_directory for new files
  2. Read index files: Parse _cdc.idx to determine safe read offset
  3. Parse commit log format: Decode mutations from binary commit log format
  4. Handle duplicates: Same mutation may appear in multiple segments (replicas)
  5. Delete processed files: Remove segments after successful processing

Consumer Required

Do not enable CDC without an active consumption process. Without consumers deleting processed files, cdc_raw_directory fills up and writes to CDC-enabled tables are rejected.

ToolDescriptionDeduplicationUse Case
DebeziumOpen-source CDC platformNo - consumers must deduplicateKafka integration, schema evolution
DataStax CDC for PulsarOfficial connectorYes - MD5 digest cacheApache Pulsar streaming
Custom consumerDirect commit log parsingMust implementSpecialized requirements

Debezium provides a Cassandra connector that consumes CDC data and produces change events to Kafka:

Cassandra ClusterDebezium Agents(one per node)KafkaNode 1Node 2Node 3CDC ConsumerKafka Producercassandra.keyspace.tableDownstream Consumers(must deduplicate)Duplicates expected due toreplication factor.Consumers must deduplicate.CDC filesCDC filesCDC filesmutations

Key Debezium characteristics:

  • Runs agent on each Cassandra node
  • Monitors cdc_raw_directory for new segments
  • Produces Kafka messages with row change data
  • Does not deduplicate: Due to replication factor, the same mutation appears on multiple nodes; downstream consumers must handle deduplication
  • Default behavior waits for completion: By default, Debezium waits for the _cdc.idx file to be marked "COMPLETED" before processing, adding latency

Real-Time Processing Trade-off

The default Debezium behavior (waiting for segment completion) adds latency but guarantees durability. Organizations requiring near real-time CDC (sub-second latency) have modified Debezium to process commit logs continuously as data arrives, before the completion marker. This trades durability guarantees for lower latency—on crash, some events may be reprocessed or lost.


RequirementReason
Same filesystem for commitlog and cdc_rawHard links cannot span filesystems
Separate spindle for cdc_raw (recommended)Reduces I/O contention with commit log
Sufficient spaceConsumers must keep up to avoid write rejection

CDC has minimal performance impact on normal operations:

OperationImpact
Write latencyNegligible (hard link creation is metadata-only)
Commit log fsyncSlight increase (index file creation)
Disk spaceHard links share inodes but prevent segment reuse until consumers delete files
CPUNone on Cassandra side; consumers bear parsing cost
Cassandra VersionCDC Support
< 3.8Not available
3.8 - 3.11Basic CDC
4.0+Enhanced CDC with improved metrics
5.0+CDC improvements for large partitions

Mixed-Version Clusters

Do not enable CDC on mixed-version clusters. Upgrade all nodes to the same version before enabling CDC.


Capture mutations as events for downstream processing:

CREATE TABLE orders (
order_id UUID,
customer_id UUID,
order_time TIMESTAMP,
status TEXT,
total DECIMAL,
PRIMARY KEY (order_id)
) WITH cdc = true;

Each insert or update produces a change event containing the full row state.

Invalidate caches when source data changes:

ApplicationCassandra(cdc=true)Redis CacheCDC ConsumerwritereadCDC eventsinvalidate

Replicate changes to external systems:

SourceTargetMethod
CassandraElasticsearchCDC → Kafka → ES Connector
CassandraData LakeCDC → Kafka → S3 Sink
CassandraAnalytics DBCDC → Kafka → JDBC Sink

Capture all changes for compliance:

CREATE TABLE user_profiles (
user_id UUID PRIMARY KEY,
email TEXT,
phone TEXT,
preferences MAP<TEXT, TEXT>
) WITH cdc = true;

CDC events provide a complete audit trail of all profile changes.


Due to Cassandra's replication, the same mutation is captured on multiple nodes. For RF=3, each write produces up to 3 CDC events. Consumers must deduplicate.

StrategyDescriptionTrade-off
MD5 digest cacheHash mutation content, cache seen digestsMemory-bound; loses state on restart
Kafka exactly-onceUse Kafka transactions with idempotent producersAdds latency; requires Kafka configuration
Downstream dedupDeduplicate in stream processor (Flink, Spark)Adds processing layer; more infrastructure
Database constraintUse upserts/idempotent writes to targetTarget must support; may miss deletes
Single-node consumptionConsume from one node onlyLoses CDC if that node fails

For high-scale environments (tens of thousands of events per second), dedicated deduplication infrastructure is typically required. Solutions like Apache Flink or custom stateful processors provide the scale and state retention needed for reliable deduplication.


LimitationDescription
No built-in parserConsumers must parse binary commit log format
No automatic cleanupConsumers must delete processed files
Space-based backpressureWrite rejection when CDC directory fills
Replica duplicationSame mutation captured on all replicas; consumers must deduplicate
No filteringAll mutations to CDC-enabled tables are captured
Schema changesConsumers must handle schema evolution
Latency vs durabilityReal-time processing requires trading off durability guarantees