Skip to content

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

Filesystem Selection for Cassandra

Filesystem choice significantly impacts Cassandra performance, particularly for commit log operations. This guide covers filesystem architectures, their interaction with Cassandra's I/O patterns, and recommendations based on workload characteristics.


ext4 (fourth extended filesystem) is the default Linux filesystem, evolved from ext3 with significant architectural improvements for modern storage.

ext4 Filesystem LayoutBlock Group 0SuperblockBlock Group 1Block Group NGroup DescriptorBlock BitmapInode BitmapInode TableData BlocksContains filesystem metadataReplicated across groups256 bytes per inode (default)Contains extent tree root

Block groups: ext4 divides the filesystem into fixed-size block groups (typically 128MB with 4K blocks). Each group contains:

  • Group descriptor: Metadata about the group (free blocks, free inodes, checksums)
  • Block bitmap: Tracks allocated/free blocks within the group
  • Inode bitmap: Tracks allocated/free inodes within the group
  • Inode table: Fixed-size array of inodes for files in this group
  • Data blocks: Actual file content

ext4 replaced ext3's indirect block mapping with extents, dramatically improving large file performance.

ext3: Indirect Blocksext4: ExtentsInodeDirectBlocksIndirectBlockDoubleIndirectData BlocksInodeExtent TreeRoot (4 extents)Extent IndexNodesExtent LeafNodesEach extent:- Start block- Length (up to 128MB)- Physical blockif needed
Aspectext3 (Indirect Blocks)ext4 (Extents)
1GB file mapping~256K block pointers~8 extents typical
Sequential readMultiple metadata lookupsSingle extent lookup
Metadata overheadO(n) with file sizeO(log n) with extent tree
Maximum file size2TB16TB (theoretical 1EB)

Extent structure (12 bytes each):

struct ext4_extent {
__le32 ee_block; // First logical block
__le16 ee_len; // Number of blocks (max 32768 = 128MB)
__le16 ee_start_hi; // High 16 bits of physical block
__le32 ee_start_lo; // Low 32 bits of physical block
};

ext4's journal (jbd2) provides crash consistency through write-ahead logging.

Journal TransactionDescriptorBlockMetadataBlocksData Blocks(journal mode)CommitBlockJournal located in reserved inode (inode 8)Default size: 128MBCircular buffer with transactions

Journal modes and write ordering:

ModeWrite SequenceCrash Behavior
journalData → Journal, Metadata → Journal, Commit → CheckpointData and metadata always consistent
orderedData → Disk, Metadata → Journal, Commit → CheckpointData written before metadata; no stale data exposure
writebackMetadata → Journal, Data → Disk (unordered)Possible stale data exposure after crash
Terminal window
# Check current journal mode
tune2fs -l /dev/sda1 | grep "Default mount options"
# Mount with writeback mode (higher performance, reduced consistency)
mount -o data=writeback /dev/sda1 /var/lib/cassandra/data
# Check journal size and location
dumpe2fs /dev/sda1 | grep -i journal

ext4 delays block allocation until writeback, enabling smarter placement decisions.

Write Path with Delayed Allocationwrite()Page Cache(dirty pages)AllocatorDecisionDisk WriteData buffered withoutblock allocationAllocator sees fullwrite pattern beforechoosing blocksImmediate returnAt writeback timeContiguous allocation

Benefits for Cassandra:

  • SSTable flushes benefit from contiguous allocation
  • Reduces fragmentation for sequential write workloads
  • Improves extent coalescing for large files

XFS is a high-performance 64-bit journaling filesystem designed for parallel I/O and extreme scalability.

XFS's defining feature is its division into independent allocation groups (AGs), enabling true parallel operations.

XFS FilesystemAG 0AG 1AG 2AG NSuperblockAG Free SpaceB+ TreesInode B+ TreeData ExtentsAG HeaderFree Space TreesInode TreeDataEach AG: independent locksParallel allocation possibleDefault: ~1GB per AG

Allocation group structure:

ComponentPurposeStructure
AG SuperblockAG metadata, free space summaryFixed location in each AG
AG Free ListFree block trackingTwo B+ trees (by block number, by size)
Inode B+ TreeInode allocation trackingB+ tree indexed by inode number
Free Inode B+ TreeAvailable inodesB+ tree for fast inode allocation

Parallelism implications:

Terminal window
# Check AG count and size
xfs_info /var/lib/cassandra/data
# Example output:
# meta-data=... agcount=16, agsize=65536000 blks
# Each AG can handle independent I/O operations

For Cassandra with multiple concurrent compactions and flushes, different operations can target different AGs simultaneously without lock contention.

XFS uses B+ trees extensively for metadata management, providing O(log n) operations even at massive scale.

XFS B+ Tree (Extent Map)Root NodeInternal Node 1Internal Node 2Leaf: Extents0-1000Leaf: Extents1001-2000Leaf: Extents2001-3000Leaf: Extents3001-4000B+ tree advantages:- All data in leaves (sequential scan efficient)- High fanout (fewer levels)- Cache-friendly traversal

B+ trees in XFS:

TreePurposeLocation
Inode B+ treeMaps inode numbers to disk locationsPer-AG
Free space by blockFinds free space at specific locationPer-AG
Free space by sizeFinds free space of specific sizePer-AG
Extent mapMaps file logical blocks to physicalPer-inode (data fork)
Directory entriesMaps names to inodesPer-directory

Delayed Allocation and Speculative Preallocation

Section titled “Delayed Allocation and Speculative Preallocation”

XFS implements aggressive delayed allocation with speculative preallocation for streaming writes.

XFS Write with SpeculationApplicationwrite() callsDelayed AllocationReservationSpeculativePreallocationActualAllocationDiskPreallocates beyondcurrent write sizeReduces fragmentationfor growing filesReserve spacePredict future writesAt writeback

Speculative preallocation behavior:

File SizePreallocationRationale
< 64KB64KBSmall file optimization
64KB - 1MB2x current sizeGrowing file pattern
> 1MBFixed incrementPrevent excessive preallocation

For Cassandra SSTables that grow to hundreds of MB or GB, this results in large contiguous extents.

XFS uses a metadata-only log with unique features for high performance.

XFS Log StructureLog Record 1Log Record 2Log Record NTailHeadHeaderTransactionItemsCircular logMetadata only (no data journaling)Async writeback with log forcingOldestNewest

Log configuration impact:

Terminal window
# Standard formatting (recommended - uses optimal defaults)
mkfs.xfs -f /dev/sdb1
# Verify filesystem parameters
xfs_info /dev/sdb1

XFS automatically calculates optimal log size based on filesystem size. Manual tuning is rarely necessary for modern storage.

ZFS is a combined filesystem and volume manager with advanced features like checksumming, snapshots, and built-in RAID. While popular for general-purpose storage, it presents specific challenges for Cassandra workloads.

ZFS StackZPL(ZFS POSIX Layer)DMU(Data Management Unit)ARC(Adaptive Replacement Cache)ZIO(ZFS I/O Pipeline)VDEV LayerPhysical DisksIn-memory read cacheCompetes with JVM heapCopy-on-write semanticsNever overwrites live data

Key architectural components:

ComponentFunctionCassandra Impact
ZPLPOSIX filesystem interfaceStandard file operations
DMUObject-based data management with CoWWrite amplification
ARCAdaptive Replacement Cache (RAM)Memory competition with JVM
L2ARCSSD-based secondary cacheAdditional I/O overhead
ZILZFS Intent Log (write log)Commit log latency
SLOGSeparate ZIL deviceCan improve sync writes

Copy-on-Write (CoW) and Write Amplification

Section titled “Copy-on-Write (CoW) and Write Amplification”

ZFS never overwrites data in place. Every write creates new blocks, then updates metadata pointers.

Traditional Filesystem WriteZFS Copy-on-WriteBlock A(version 1)Block A(version 2)Block A(version 1)Block A'(version 2)Indirect BlockIndirect Block'UberblockUberblock'Write to data block requires:1. Write new data block2. Write new indirect block3. Write new uberblock3+ writes per logical writeOverwritein place

Write amplification analysis:

OperationTraditional FSZFS CoW
4KB write1 write + journal3+ writes (data + metadata chain)
SSTable writeSequential + metadataCoW tree updates
CompactionRead + writeRead + CoW writes + old block retention

For Cassandra's write-heavy workloads, this CoW overhead compounds with Cassandra's own write amplification from compaction.

The ZIL provides durability for synchronous writes, critical for Cassandra's commit log.

Sync Write PathApplicationfsync()ZIL WriteTransactionGroup (TXG)Pool WriteSequential writesCan use separate SLOG deviceBatches writes for efficiencyDefault 5-second commit intervalImmediateBatchedPeriodic (5s default)

ZIL considerations for Cassandra:

ConfigurationBehaviorRecommendation
Default ZILOn pool disksAdequate for most workloads
SLOG (separate log device)Dedicated fast device for ZILRecommended if using ZFS for commit logs
sync=disabledNo synchronous writesNever use - violates durability
Terminal window
# Check ZIL configuration
zpool status -v
# Add SLOG device (mirror recommended)
zpool add tank log mirror /dev/nvme0n1 /dev/nvme1n1
# Check ZIL statistics
zpool iostat -v tank 1

ZFS's ARC (Adaptive Replacement Cache) uses RAM for caching, competing directly with Cassandra's JVM heap and OS page cache.

Memory AllocationTotal RAM(e.g., 128GB)JVM Heap(e.g., 31GB)Off-Heap(e.g., 32GB)OS/Other(e.g., 8GB)ARC(remaining)ARC expands to fillavailable memory Default: up to 50% of RAMMust be limited for Cassandra

Memory configuration:

Terminal window
# Limit ARC size in /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=8589934592 # 8GB max
# Or dynamically
echo 8589934592 > /sys/module/zfs/parameters/zfs_arc_max
# Verify ARC usage
arc_summary # or arcstat

Memory planning for Cassandra on ZFS:

ComponentTypical AllocationNotes
JVM Heap8-31GBStandard Cassandra sizing
Off-Heap50-100% of heapMemtables, bloom filters, compression
ARC8-16GB maxMust limit explicitly
OS/kernel4-8GBPage cache, kernel structures

ZFS provides end-to-end checksumming, detecting silent data corruption (bit rot).

ZFS Data IntegrityData BlockChecksum(in parent block)Read VerificationSelf-Healing(with redundancy)Checksums: fletcher4 (default), sha256Detected corruption triggers:- Read from mirror/parity copy- Or return error if no redundancyComputed on writeValidated on readAuto-repair if redundant

Cassandra already provides checksums at the SSTable level, making ZFS checksumming redundant for data files. However, ZFS checksums protect:

  • Commit logs (Cassandra checksums optional)
  • System files
  • Any corruption in storage layer
ChallengeImpactMitigation
Write amplification2-3x more disk writesUse SSDs, accept overhead
CoW fragmentationRandom read patterns over timeRegular scrub, accept degradation
ARC memoryReduces memory for CassandraLimit ARC explicitly
Sync write latencyCommit log performanceAdd SLOG device
Snapshots retain blocksDisk space after compactionManage snapshot lifecycle
ComplexityTuning requiredExpertise needed

Despite the challenges, ZFS can be appropriate in specific scenarios:

Potentially suitable:

  • Development/test environments: Snapshots enable rapid environment cloning
  • Operational simplicity priority: Single storage stack with built-in features
  • Data integrity requirements: Regulatory or compliance needs for checksumming
  • Existing ZFS infrastructure: Team expertise and tooling already in place

Not recommended:

  • Performance-critical production: Write amplification impacts throughput
  • Cost-sensitive deployments: Requires more disk I/O capacity
  • Memory-constrained systems: ARC competition with JVM

If using ZFS, apply these optimizations:

Terminal window
# Create pool with appropriate settings
zpool create -o ashift=12 cassandra_pool mirror /dev/sda /dev/sdb
# Create dataset with Cassandra-optimized settings
zfs create -o recordsize=64K \
-o compression=lz4 \
-o atime=off \
-o xattr=sa \
-o primarycache=metadata \
cassandra_pool/data
# For commit logs (if separate dataset)
zfs create -o recordsize=8K \
-o compression=off \
-o sync=standard \
-o primarycache=metadata \
cassandra_pool/commitlog

Key tuning parameters:

ParameterData DirectoryCommit LogRationale
recordsize64K-128K8KMatch Cassandra I/O patterns
compressionlz4offData already compressed in SSTables
atimeoffoffEliminate access time updates
primarycachemetadatametadataLet Cassandra manage data caching
syncstandardstandardMaintain durability
logbiasthroughputlatencyOptimize for workload type
Terminal window
# Limit ARC (critical for Cassandra)
echo "options zfs zfs_arc_max=8589934592" >> /etc/modprobe.d/zfs.conf
# Tune transaction group timing
echo "options zfs zfs_txg_timeout=5" >> /etc/modprobe.d/zfs.conf
# Apply changes (requires reboot or module reload)

ext3 lacks architectural features critical for Cassandra performance.

Fundamental limitations:

Aspectext3 BehaviorImpact on Cassandra
Block mappingIndirect blocks (up to triple)O(n) metadata overhead for large SSTables
AllocationImmediate allocationNo optimization for write patterns
Maximum extentN/A (block-based)Every block tracked individually
Directory scalingLinear search (without htree)Slow with many SSTables
JournalFull data journaling common2x write amplification

Migration path:

Terminal window
# Check if filesystem is ext3
tune2fs -l /dev/sda1 | grep "Filesystem features"
# ext3: has_journal (no extents)
# ext4: has_journal extents
# Convert ext3 to ext4 (offline)
tune2fs -O extents,uninit_bg,dir_index /dev/sda1
e2fsck -fD /dev/sda1
Featureext4XFSZFS
Block groups / AGsFixed 128MB groupsConfigurable AGs (~1GB)Variable record size
Parallel allocationLimitedExcellent (per-AG)Good (per-vdev)
Extent sizeMax 128MBMax 8EBVariable blocks
Write behaviorIn-placeIn-placeCopy-on-write
JournalData + metadata modesMetadata onlyIntent log (ZIL)
Delayed allocationYesYes (aggressive)Yes (TXG batching)
Built-in checksumsMetadata onlyMetadata onlyAll data + metadata
SnapshotsNoNoYes (instant, CoW)
Online shrinkYesNoNo
Maximum file size16TB8EB16EB
Maximum filesystem1EB8EB256 ZB
Memory overheadMinimalMinimalARC cache (significant)
Write amplificationLowLowHigh (2-3x)
Cassandra suitabilityExcellentExcellentLimited

Understanding Cassandra's I/O behavior is essential for filesystem selection.

The commit log provides durability by persisting writes before acknowledgment.

Commit LogMemory-Mapped(Uncompressed)FileChannel(Compressed)Write RequestMemtableClient ACKMappedByteBufferOS manages flushingFilesystem-sensitiveExplicit I/O callsByteBuffer writesLess filesystem-sensitive

I/O characteristics:

ModeImplementationFilesystem Interaction
UncompressedMappedByteBufferMemory-mapped files; OS kernel manages page flushing; highly sensitive to filesystem I/O handling
CompressedFileChannel + ByteBufferExplicit write calls; more predictable I/O pattern; less filesystem-dependent

The difference in implementation explains why filesystem choice significantly impacts uncompressed commit log performance but has minimal effect on compressed workloads.

SSTable operations have different characteristics:

OperationI/O PatternFilesystem Impact
Memtable flushLarge sequential writesExtent allocation efficiency
CompactionSequential read + sequential writeSustained throughput
Read pathRandom reads (with caching)Block allocation locality

Data directory workloads benefit from XFS's large file handling and parallel I/O capabilities.


Benchmark studies reveal significant performance differences for uncompressed commit logs, particularly on certain Linux distributions.

Observed behavior (CentOS/RHEL):

FilesystemThroughputMean LatencyNotes
ext4~21k ops/sec~2msSuperior MappedByteBuffer handling
XFS~14k ops/sec~4msHigher latency with memory-mapped I/O

This represents approximately 50% throughput reduction and 2x latency increase for XFS with uncompressed commit logs on CentOS systems.

Root cause analysis:

The performance difference stems from how each filesystem handles memory-mapped file synchronization:

  1. ext4: More efficient write coalescing and page cache management for mmap operations
  2. XFS: Allocation group locking and extent management add overhead for frequent small mmap flushes

Distribution variance:

Distributionext4 vs XFS Gap
CentOS/RHELSignificant (50%+ throughput difference)
UbuntuMinimal (kernel I/O path differences)

The kernel version and distribution-specific patches affect filesystem behavior. Ubuntu's kernel includes different I/O scheduling and writeback tuning that reduces the performance gap.

Enabling commit log compression eliminates the filesystem performance gap:

Configurationext4XFS
Uncompressed~21k ops/sec~14k ops/sec
Compressed~24k ops/sec~24k ops/sec

Compression switches from MappedByteBuffer to FileChannel I/O, which both filesystems handle similarly.

cassandra.yaml
commitlog_compression:
- class_name: LZ4Compressor

For data directories, XFS generally provides advantages:

Aspectext4XFS
Large SSTable writesGoodBetter (optimized extent allocation)
Parallel compactionGoodBetter (allocation groups)
Many small filesBetterGood
File deletionFasterSlower (extent cleanup)

Use CaseRecommendedAlternativeAvoid
Data directoriesXFSext4ZFS (write amplification)
Commit logs (uncompressed)ext4XFS (with commitlog compression enabled)ZFS
Commit logs (compressed)XFS or ext4-ZFS
Mixed (single mount)XFS (with commitlog compression)ext4ZFS
Dev/test with snapshotsZFSXFS/ext4 + LVM-

Commitlog Compression

"Commitlog compression" refers to Cassandra's built-in compression configured in cassandra.yaml, not filesystem-level compression. XFS does not support filesystem compression. Enabling commitlog compression switches Cassandra from MappedByteBuffer to FileChannel I/O, which performs equally well on both ext4 and XFS.

Preferred configuration (separate mounts):

/dev/nvme0n1p1 /var/lib/cassandra/data xfs defaults,noatime 0 2
/dev/nvme1n1p1 /var/lib/cassandra/commitlog ext4 defaults,noatime 0 2

With commit log compression enabled in cassandra.yaml.

ZFS in Production

ZFS is not recommended for production Cassandra deployments due to:

  • Write amplification: CoW semantics cause 2-3x write overhead
  • Memory contention: ARC competes with JVM heap
  • Compaction interaction: CoW + Cassandra compaction compounds write amplification

If organizational requirements mandate ZFS, see the ZFS Tuning section for optimization guidance.

Alternative (single filesystem):

If using a single filesystem for both data and commit logs:

  1. Use XFS for overall performance
  2. Enable commit log compression to avoid MappedByteBuffer performance penalty
# cassandra.yaml - required when using XFS for commit logs
commitlog_compression:
- class_name: LZ4Compressor
DistributionData DirectoryCommit Log
RHEL/CentOS/RockyXFSext4, or XFS with commitlog compression
Ubuntu/DebianXFSEither (minimal difference)
Amazon LinuxXFSext4, or XFS with commitlog compression

/etc/fstab
/dev/sdb1 /var/lib/cassandra/data xfs defaults,noatime,nodiratime,allocsize=512m 0 2
OptionPurpose
noatimeDisable access time updates; reduces write overhead
nodiratimeDisable directory access time updates
allocsize=512mLarger allocation size for streaming writes (optional)
Terminal window
# /etc/fstab - balanced configuration
/dev/sdc1 /var/lib/cassandra/commitlog ext4 defaults,noatime,nodiratime 0 2
# /etc/fstab - maximum performance (reduced durability)
/dev/sdc1 /var/lib/cassandra/commitlog ext4 defaults,noatime,nodiratime,data=writeback,barrier=0 0 2
OptionPurposeTrade-off
noatimeDisable access time updatesNone
nodiratimeDisable directory access time updatesNone
data=writebackJournal metadata onlyReduced durability on crash
barrier=0Disable write barriersReduced durability; requires battery-backed cache

Write Barriers

Disabling write barriers (barrier=0) should only be used with battery-backed RAID controllers or when commit log durability is handled at another layer. Data corruption is possible on power failure without hardware write caching protection.

Terminal window
# Check mounted filesystem options
mount | grep cassandra
# Check XFS allocation groups
xfs_info /var/lib/cassandra/data
# Check ext4 features
tune2fs -l /dev/sdc1 | grep -E "features|Default mount"

Terminal window
# Standard formatting (recommended - uses optimal defaults)
mkfs.xfs -f /dev/sdb1
# Verify filesystem parameters
xfs_info /dev/sdb1

XFS automatically calculates optimal allocation group count and log size based on device characteristics. Manual tuning is rarely necessary for modern NVMe/SSD storage.

Terminal window
# Standard formatting
mkfs.ext4 /dev/sdc1
# Optimized (larger inode size, disable journaling for commit log if using replication)
mkfs.ext4 -I 512 /dev/sdc1

Terminal window
# Real-time I/O monitoring
iostat -xz 1
# Key metrics to watch
# - await: Average I/O wait time (ms)
# - %util: Device utilization
# - avgqu-sz: Average queue depth
Terminal window
# XFS fragmentation
xfs_db -c frag -r /dev/sdb1
# ext4 fragmentation
e4defrag -c /var/lib/cassandra/commitlog
# Check for extent fragmentation in large files
filefrag /var/lib/cassandra/data/keyspace/table-*/nb-*-big-Data.db

RecommendationRationale
Use XFS for data directoriesOptimized for large files, parallel I/O, delayed allocation
Use ext4 for commit logs OR enable compressionext4 has better MappedByteBuffer handling
Avoid ZFS for productionWrite amplification and memory contention impact performance
Always use noatimeEliminates unnecessary write overhead
Enable Cassandra commitlog compression if using XFSNeutralizes MappedByteBuffer performance difference
Match filesystem choice to distributionRHEL-based systems show larger ext4/XFS gaps
If ZFS required, limit ARC and tune recordsizeMinimize performance impact with proper configuration