Skip to content

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

Cassandra Commit Log

The commit log is Cassandra's write-ahead log (WAL), providing durability for all mutations. Every write is appended to the commit log before being acknowledged, ensuring data can be recovered after a crash.

For an overview of how the commit log fits into the write path, see Write Path.


The commit log serves a single purpose: crash recovery. It is not used for reads—all read operations go through memtables and SSTables.

GuaranteeDescription
DurabilityAcknowledged writes survive node restart
OrderingMutations are replayed in segment order; correctness relies on mutation timestamps
AtomicityIndividual mutations are atomic (all-or-nothing)

Commit Log vs Replication

The commit log provides single-node durability. For cluster-wide durability, Cassandra relies on replication. With RF=3 and QUORUM writes, data survives even if one node loses its commit log before flushing.


The commit log is organized into segments—fixed-size files that are allocated, filled, and eventually deleted.

Commit Log Segment LifecycleCommit Log Segment LifecycleSegment PoolActive SegmentsCleanupAvailableSegmentAvailableSegmentAllocatingSegment(current)ActiveSegment(awaiting flush)ActiveSegment(awaiting flush)CleanSegment(all tables flushed)DeletedMutations appended hereSwitches when size limit reachedSafe to delete:all dirty intervalshave been flushedallocationsegment full(> 32MB)all referencedmemtables flushed
StateDescription
AvailableAllocated, empty, queued for use
AllocatingCurrently receiving mutations
ActiveFull (reached commitlog_segment_size_in_mb), awaiting memtable flush
CleanAll referenced data flushed to SSTables; segment is deleted

The CommitLogSegmentManager maintains a pool of available segments to ensure a new segment is always ready when the current one fills. Segments are allocated on demand—they are not pre-allocated to their full size on disk.

Memory-mapped segments (default): The segment file is created and memory-mapped. The file grows as mutations are appended, up to commitlog_segment_size_in_mb.

Compressed/encrypted segments: Mutations are buffered in memory and written in blocks. The on-disk size depends on compression ratio.

Historical: Pre-allocation and Recycling (removed in 2.2)

Prior to Cassandra 2.2, segments were pre-allocated to their full size (128MB default) and recycled after use. This was removed to reduce page cache pressure and simplify the code. Modern Cassandra deletes segments when clean rather than recycling them.

Each segment maintains a map of which table mutations it contains and their position ranges:

Segment: CommitLog-7-1702345678901.log
Dirty Intervals:
┌─────────────────┬────────────────────┐
│ Table ID │ Position Range │
├─────────────────┼────────────────────┤
│ users │ [1024, 15360] │
│ orders │ [2048, 31744] │
│ events │ [8192, 28672] │
└─────────────────┴────────────────────┘

When a memtable flushes, it reports its commit log position range. The segment marks those intervals as clean. Once all intervals are clean, the segment is deleted.


Each segment file contains a header followed by sync blocks of serialized mutations.

┌─────────────────────────────────────────────────────────────┐
│ HEADER │
├──────────┬───────────┬─────────────┬────────────┬───────────┤
│ Version │ Segment │ Params Len │ Params │ Header │
│ (4 bytes)│ ID (8) │ (2 bytes) │ (JSON) │ CRC (4) │
└──────────┴───────────┴─────────────┴────────────┴───────────┘
┌─────────────────────────────────────────────────────────────┐
│ SYNC BLOCK 1 │
├─────────────────────────────────────────────────────────────┤
│ Sync Marker: [next block offset (4) | marker CRC (4)] │
├─────────────────────────────────────────────────────────────┤
│ Mutation 1: [size (4) | size CRC (4) | data | data CRC (4)] │
│ Mutation 2: [size (4) | size CRC (4) | data | data CRC (4)] │
│ ... │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ SYNC BLOCK 2 │
│ ... │
└─────────────────────────────────────────────────────────────┘
FieldSizeDescription
Version4 bytesCommit log format version
Segment ID8 bytesUnique segment identifier
Parameters length2 bytesLength of JSON parameters (unsigned short)
ParametersVariableJSON: compression/encryption settings
Header CRC4 bytesCRC32 checksum of header

Each sync block begins with a marker indicating the offset to the next block:

FieldSizeDescription
Next offset4 bytesFile position of next sync block
Marker CRC4 bytesCRC32 of the offset value
FieldSizeDescription
Size4 bytesSerialized mutation size
Size CRC4 bytesCRC32 of size field
DataVariableSerialized mutation bytes
Data CRC4 bytesCRC32 of mutation data

The dual CRC design (size + data) allows detection of both truncation and corruption during replay.


The sync mode controls when data is flushed from OS page cache to persistent storage.

commitlog_sync: periodic
commitlog_sync_period_in_ms: 10000

Mutations are written to the page cache and acknowledged immediately. A background thread calls fsync() every N milliseconds.

ClientClientCassandraCassandraPage CachePage CacheDiskDiskClientClientCassandraCassandraPage CachePage CacheDiskDiskWrite mutationwrite() to page cacheAcknowledge (fast)10 seconds laterfsync()Flush to diskConfirmedData in page cache only.Power loss = data loss.After fsync, data survivespower failure.

Trade-off: Lowest latency, but up to commitlog_sync_period_in_ms of data can be lost on power failure (mitigated by replication).

commitlog_sync: batch
commitlog_sync_batch_window_in_ms: 2

Mutations are batched for up to N milliseconds, then flushed together with a single fsync().

Client 1Client 1Client 2Client 2CassandraCassandraDiskDiskClient 1Client 1Client 2Client 2CassandraCassandraDiskDiskWrite AQueued, waitingWrite BQueued, waiting2ms window expiresfsync() [A + B]ConfirmedAcknowledge AAcknowledge B

Trade-off: Higher latency (waits for batch window), but stronger single-node durability.

commitlog_sync: group
commitlog_sync_group_window_in_ms: 1000

Similar to batch, but with a larger default window. Mutations are grouped and synced together.

ModeDefault WindowLatencyDurability
periodic10,000 msLowestWeakest (single-node)
group1,000 msLowModerate
batch2 msHigherStrongest

The segment type determines how data is written to disk.

# No special configuration - this is the default

Uses memory-mapped I/O. The segment is mapped into virtual memory, and writes go directly to the mapped region. The OS handles flushing based on sync mode.

Characteristics:

  • Simplest implementation
  • Relies on OS page cache management
  • No compression overhead
commitlog_compression:
- class_name: LZ4Compressor
parameters: {}

Mutations are compressed in memory before writing to disk.

AlgorithmClass NameRatioCPU
LZ4LZ4Compressor~2-3xVery low
SnappySnappyCompressor~2xLow
DeflateDeflateCompressor~4-5xHigh

Characteristics:

  • Reduces disk I/O
  • Smaller commit log footprint
  • Slight CPU overhead
  • Compression happens in fixed-size buffers before writing
transparent_data_encryption_options:
enabled: true
chunk_length_kb: 64
cipher: AES/CBC/PKCS5Padding
key_alias: testing:1
key_provider:
- class_name: org.apache.cassandra.security.JKSKeyProvider
parameters:
- keystore: conf/.keystore
keystore_password: changeit
store_type: JCEKS
key_password: changeit

Data is written in configurable-size blocks. Each block is compressed (if enabled) then encrypted.

Block Structure (Encrypted):

┌───────────────────────────────────────────┐
│ Total Block Length (unencrypted, 4 bytes) │
│ Encrypted Data Length (unencrypted, 4) │
│ Encrypted Data (variable) │
│ └── Contains: compressed mutation data │
└───────────────────────────────────────────┘

The length fields are unencrypted to allow reading block boundaries without decryption.


On startup, Cassandra replays commit log segments to recover mutations that weren't flushed to SSTables.

Scan commitlog_directorySort segments by IDRead segment headerValidate header CRCHeader valid?yesnoRead sync markerValidate marker CRCMarker valid?yesnoRead mutation size + CRCSize CRC valid?yesnoRead mutation data + CRCData CRC valid?yesnoCheck if mutation's tablewas flushed after this positionNeeds replay?yesnoApply mutation to memtableSkip (already in SSTable)Log corruption, skip mutationLog corruption, skip to next blockyesMore mutations in block?Log corruption, skip segmentyesMore sync blocks?Skip corrupted segmentDelete replayed segmentyesMore segments?Replay complete

Not all mutations in a segment need replay. Each SSTable records the commit log position at flush time. During replay:

  1. Read mutation's commit log position
  2. Check if the mutation's table has an SSTable flushed after that position
  3. If yes, skip (data already durable in SSTable)
  4. If no, apply mutation to memtable
Corruption TypeDetectionRecovery
Header corruptionHeader CRC mismatchSkip entire segment
Sync marker corruptionMarker CRC mismatchSkip to next segment
Size field corruptionSize CRC mismatchSkip to next sync block
Data corruptionData CRC mismatchSkip mutation, continue
TruncationUnexpected EOFStop replay at truncation point

The CRC-based design allows partial recovery—corruption in one mutation doesn't prevent replaying subsequent valid mutations.


ParameterDefaultDescription
commitlog_directory$CASSANDRA_HOME/data/commitlogCommit log location
commitlog_segment_size_in_mb32Max segment size before switching
commitlog_total_space_in_mb8192Max total commit log space
ParameterDefaultDescription
commitlog_syncperiodicSync mode: periodic, batch, or group
commitlog_sync_period_in_ms10000Periodic sync interval
commitlog_sync_batch_window_in_ms2Batch mode window
commitlog_sync_group_window_in_ms1000Group mode window
ParameterDefaultDescription
commitlog_compressionnoneCompression algorithm configuration
ParameterDefaultDescription
transparent_data_encryption_options.enabledfalseEnable encryption
transparent_data_encryption_options.chunk_length_kb64Encryption block size
transparent_data_encryption_options.cipherAES/CBC/PKCS5PaddingCipher algorithm

RecommendationRationale
Dedicated disk/volumeIsolate commit log I/O from data I/O
Fast storage (NVMe/SSD)Commit log is write-intensive
Battery-backed cacheAllows safer periodic sync with durability
Separate from data_file_directoriesPrevents commit log from competing with compaction
Terminal window
# Check commit log size
du -sh /var/lib/cassandra/commitlog/
# Count segments
ls -1 /var/lib/cassandra/commitlog/*.log | wc -l
# Monitor via JMX
nodetool sjk mx -b "org.apache.cassandra.metrics:type=CommitLog,name=TotalCommitLogSize" -f Value
nodetool sjk mx -b "org.apache.cassandra.metrics:type=CommitLog,name=PendingTasks" -f Value
MetricDescriptionAlert Threshold
TotalCommitLogSizeCurrent size of all segments> 75% of commitlog_total_space_in_mb
PendingTasksMutations awaiting syncSustained high values
WaitingOnCommitTime waiting for fsync> 100ms average
WaitingOnSegmentAllocationTime waiting for new segmentShould be ~0
SymptomPossible CauseResolution
High WaitingOnCommitSlow disk, high loadFaster storage, tune sync mode
Growing commit logMemtables not flushingCheck memtable_flush_writers, disk space
Slow startupLarge commit log replayMore frequent flushing, check flush triggers
Segment allocation delaysDisk full or slowFree space, faster storage

VersionCassandraChanges
63.0 - 3.11Introduced with storage engine rewrite
74.0+Current format, improved checksums

See Segment Allocation for details on segment recycling removal in 2.2.