Skip to content

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

Cassandra Repair Concepts

This page explains the fundamental concepts behind Cassandra repair operations, including how the repair process works internally, what triggers the need for repair, and the mechanisms Cassandra uses to detect and resolve data inconsistencies.

Repair is Cassandra's anti-entropy mechanism for synchronizing data across replica nodes. In a distributed system where writes may not reach all replicas (due to node failures, network partitions, or hint expiration), repair ensures that all replicas eventually contain identical data.

Node ANode BNode C
Before RepairRow 1: v1Row 1: v1Row 1: v1
Row 2: v2Row 2: v1 ⚠️ staleRow 2: v2
Row 3: v3Row 3: v3Row 3: ❌ missing
After RepairRow 1: v1Row 1: v1Row 1: v1
Row 2: v2Row 2: v2 ✓Row 2: v2
Row 3: v3Row 3: v3Row 3: v3 ✓

Data inconsistencies arise from several scenarios in distributed systems:

Write Path Failures

ScenarioDescriptionRecovery Mechanism
Node unavailable during writeWrite succeeds on available replicas onlyHinted handoff, read repair, anti-entropy repair
Network partitionReplicas in different partitions receive different writesAnti-entropy repair
Coordinator timeoutWrite acknowledged but some replicas slowRead repair, anti-entropy repair

Recovery Limitations

ScenarioDescriptionRecovery Mechanism
Hints expiredNode was down longer than hint window (default 3 hours)Anti-entropy repair only

Hint window configuration by version:

VersionParameterDefaultSyntax
4.0max_hint_window_in_ms10800000Integer (milliseconds)
4.1+max_hint_window3hDuration literal (3h, 180m)
Hinted handoff disabledHints not stored for unavailable replicasAnti-entropy repair only

Operational Events

ScenarioDescriptionRecovery Mechanism
New node bootstrapNode joins but may not have all dataStreaming + repair
Node replacementReplacement node needs data from replicasStreaming + repair

The most critical aspect of repair scheduling is the relationship with gc_grace_seconds. This parameter defines how long tombstones (deletion markers) are retained before garbage collection.

Tombstone Lifecycle and gc_grace_secondsTombstone Lifecycle and gc_grace_secondsClientNode ANode BNode CClientClientNode ANode ANode BNode BNode C(DOWN)Node C(DOWN)T=0: Delete OperationDELETE row XCreate tombstoneReplicate tombstoneNode C is downmisses DELETET=10 days: gc_grace_seconds expiresGarbage collect tombstoneGarbage collect tombstoneNode C recoversStill has original row X(no tombstone)Read RequestRead row XRow X not foundRead row XRow X exists!ZOMBIE DATADeleted row has resurrected!

Zombie data resurrection scenario:

  1. Data is deleted on Node A, creating a tombstone
  2. Tombstone replicates to Node B
  3. Node C is down and misses the delete
  4. After gc_grace_seconds, the tombstone is garbage collected from A and B
  5. Node C comes back online with the original (pre-delete) data
  6. Without the tombstone, the deleted data "resurrects" during read repair

Prevention: Run repair on all nodes within gc_grace_seconds to ensure tombstones propagate before deletion.

Simplifying Repair Operations

Ensuring repair completes within gc_grace_seconds across every node requires careful scheduling and monitoring. AxonOps Adaptive Repair handles this automatically with load-aware scheduling, failure retry, and compliance tracking.

Cassandra uses Merkle trees (hash trees) to efficiently detect differences between replicas without comparing every row.

Merkle Tree Comparison ProcessMerkle Tree Comparison ProcessReplica AReplica BReplica CReplica A(Repair Coordinator)Replica A(Repair Coordinator)Replica BReplica BReplica CReplica Cnodetool repairissued on this nodePhase 1: ValidationBuild Merkle treeby hashing partitionsRequest Merkle tree for rangeRequest Merkle tree for rangeBuild Merkle treeby hashing partitionsBuild Merkle treeby hashing partitionsReturn tree (root hash: ABC123)Return tree (root hash: XYZ789)Phase 2: ComparisonCompare root hashesA=B ≠ CDrill down into C's treeIdentify differingsubtrees recursivelyPhase 3: StreamingStream differing partitionsApply streamed dataRepair session complete

Merkle tree segments:

The token range being repaired is divided into segments, with each segment represented as a leaf node in the Merkle tree. By default, Cassandra creates approximately 32,768 (2^15) segments per repair session.

Merkle tree configuration by version:

VersionParameterDefaultDescription
4.0repair_session_max_tree_depth20Maximum depth of Merkle tree
4.1+repair_session_space16MiBMemory limit for Merkle trees (replaces depth)

Streaming granularity:

When a mismatch is detected, the entire segment is streamed—not individual rows. This means a single differing row causes the entire segment to be transferred:

Table Size / NodeSegmentsMin Stream Unit (1 segment)
100 GB32,768~3 MB
500 GB32,768~15 MB
1 TB32,768~30 MB

For example, with a 500 GB table, if one row is inconsistent, the entire ~15 MB segment containing that row must be streamed. If inconsistencies are spread across many segments, streaming volumes increase proportionally.

Merkle tree comparison and streaming:

Merkle Tree Comparison Between Two ReplicasMerkle Tree Comparison Between Two ReplicasReplica A (Repair Coordinator)Replica BRoot: 0xABC123Hash: 0x111Hash: 0x222Seg 1Seg 2Seg 3Seg 4Root: 0xDEF456Hash: 0x111Hash: 0x333Seg 1Seg 2Seg 3Seg 4Green = matching hashPink = mismatching hash Root hashes differ → drill downLeft subtrees match → skipRight subtrees differ → check segmentsSegment 3 differs → stream dataStreamSegment 3

The comparison process:

  1. Compare root hashes—if they match, replicas are identical (no streaming needed)
  2. If root hashes differ, compare child hashes recursively
  3. Drill down only into subtrees with mismatching hashes
  4. At the leaf level, stream the entire segment for any mismatching hash
Repair Session LifecycleRepair Session LifecycleInitiatedPreparingValidatingComparingStreamingCompletingFailedMost resource-intensive phase:- Reads all data in range- Computes hashes- Memory for tree storageNetwork-intensive phase:- Transfers differing data- Triggers compactionnodetool repairAcquire repairsession IDBuild Merkletrees on replicasTrees receivedfrom all replicasDifferencesidentifiedAll datastreamedSuccessTimeout ornode failureComparisonerrorStreamfailureError reported

Full repair is the original repair mechanism in Cassandra. It compares all data in the specified token ranges across all replicas, regardless of whether the data has been previously repaired.

How it works:

  1. The repair coordinator builds a Merkle tree from all SSTables in the repair range
  2. Each replica builds its own Merkle tree from all its SSTables
  3. Trees are compared to identify differences
  4. Differing data is streamed between replicas to synchronize
Full Repair: All SSTables Included in ComparisonFull Repair: All SSTables Included in ComparisonReplica A (Repair Coordinator)Replica BReplica CSSTable 1SSTable 2SSTable 3SSTable 4SSTable 1SSTable 2SSTable 3SSTable 1SSTable 2SSTable 3Full repair ignores repaired/unrepaired status.All SSTables are validated and synchronized.Repair ALL SSTablesRepair ALL SSTables

Advantages:

  • Simple and reliable - no complex state tracking
  • Guarantees complete consistency check across all data
  • No risk of repaired/unrepaired state corruption
  • Works correctly regardless of previous repair history
  • Required after certain failure scenarios

Disadvantages:

  • Re-validates already-consistent data unnecessarily
  • Longer duration as data volume grows
  • Higher resource consumption (CPU, memory, network, disk I/O)

When to use full repair:

  • After node replacement or rebuild
  • After recovering from data corruption
  • When incremental repair state is suspect or corrupted
  • Before major version upgrades
  • As periodic validation (e.g., monthly) alongside incremental repairs

Incremental repair was introduced in Cassandra 2.1 via CASSANDRA-5351 to address the scalability limitations of full repair. It tracks which SSTables have been previously repaired and only validates new (unrepaired) data.

History and evolution:

Incremental repair had a troubled history in early versions. While the concept was sound, the implementation suffered from numerous bugs that could lead to data inconsistency, silent corruption of the repaired/unrepaired state, and operational challenges. Many operators avoided incremental repair entirely in versions prior to 4.0, preferring the slower but more reliable full repair.

VersionStatusNotes
2.1IntroducedInitial implementation; significant bugs and edge cases
2.2 - 3.xProblematicOngoing fixes but still unreliable for production use; many operators avoided it
4.0+Production readyMajor rework; became default behavior; full repair requires -full flag

Recommendation: For clusters running Cassandra 4.0 or later, incremental repair is the recommended approach for routine maintenance. For earlier versions, evaluate carefully and consider using full repair if stability is a concern.

How it works:

  1. Each SSTable has a repairedAt metadata field (0 = unrepaired, timestamp = repaired)
  2. During incremental repair, only SSTables with repairedAt = 0 are included in Merkle tree generation
  3. After successful repair, participating SSTables are marked with a repairedAt timestamp
  4. Subsequent repairs skip already-repaired SSTables
  5. Anti-compaction separates repaired and unrepaired data when SSTables contain both
Incremental Repair: Only Unrepaired SSTables ComparedIncremental Repair: Only Unrepaired SSTables ComparedReplica A (Repair Coordinator)Replica BReplica CSSTable 1(repaired)SKIPPEDSSTable 2(repaired)SKIPPEDSSTable 3(unrepaired)INCLUDEDSSTable 4(unrepaired)INCLUDEDSSTable 1(repaired)SKIPPEDSSTable 2(repaired)SKIPPEDSSTable 3(unrepaired)INCLUDEDSSTable 1(repaired)SKIPPEDSSTable 2(unrepaired)INCLUDEDSSTable 3(unrepaired)INCLUDEDAfter incremental repair:- Unrepaired SSTables marked as repaired- Next incremental skips already-repaired data- Smaller Merkle trees = faster validationRepair UNREPAIRED SSTables onlyRepair UNREPAIRED SSTables only

Advantages:

  • Faster execution - only validates new data since last repair
  • Lower resource consumption for routine maintenance
  • Scales better with large datasets
  • Enables more frequent repair cycles
  • Reduces repair window, making it easier to complete within gc_grace_seconds

Disadvantages:

  • Anti-compaction overhead after repair completion (see below)
  • More complex operational model to understand and troubleshoot

Anti-compaction considerations:

After incremental repair completes, Cassandra runs anti-compaction to split SSTables that contain both repaired and unrepaired data. This process:

  • Reads the SSTable and writes two new SSTables (one repaired, one unrepaired)
  • Consumes disk I/O and temporary disk space (up to 2x the SSTable size during the split)
  • Adds to compaction pending tasks
  • Can delay the start of normal compaction work

Operational guidance:

  • Monitor CompactionManager pending tasks during and after repair
  • Ensure sufficient disk headroom (anti-compaction temporarily increases disk usage)
  • On I/O-constrained systems, consider scheduling repairs during low-traffic periods
  • The nodetool compactionstats command shows anti-compaction progress

When to use incremental repair:

  • Routine scheduled maintenance (default choice for Cassandra 4.0+)
  • Clusters with large data volumes where full repair is impractical
  • When repair must complete within tight time windows

AspectFull RepairIncremental Repair
ScopeAll data in rangeOnly unrepaired SSTables
SSTable markingDoes not modify SSTable metadataMarks SSTables with repairedAt timestamp
DurationLonger (proportional to total data)Shorter (proportional to new data)
Resource usageHigherLower for routine runs
ComplexitySimpleRequires state tracking
Use caseRecovery, validation, periodic full checkRegular maintenance
Default (4.0+)Must specify -full flagDefault behavior

Incremental repair tracks repair state at the SSTable level:

SSTable Repair State TrackingSSTable Repair State TrackingNew SSTableCreatedFlushed to Disk(unrepaired)repairedAt = 0After IncrementalRepair (repaired)repairedAt = timestampCompacted withother repairedRepaired SSTables:- Have repairedAt timestamp- Excluded from futureincremental repairs- Compacted separatelyfrom unrepairedUnrepaired SSTables:- repairedAt = 0- Included in nextincremental repairflush/compactionincrementalrepaircompaction

Cassandra partitions data across nodes using a token ring. Each node is responsible for specific token ranges.

Token Ring and Primary Ranges (RF=3)Token Ring and Primary Ranges (RF=3)Node ATokens: 0-25Node BTokens: 25-50Node CTokens: 50-75Node DTokens: 75-100Primary: 0-25Replicas: 75-100, 50-75Primary: 25-50Replicas: 0-25, 75-100Primary: 50-75Replicas: 25-50, 0-25Primary: 75-100Replicas: 50-75, 25-50clockwise

The -pr flag limits repair to only the primary token ranges owned by the node:

Primary Range Repair (-pr) BehaviorPrimary Range Repair (-pr) BehaviorWithout -pr (Full Range)With -pr (Primary Only)Range 0-25(primary)Range 75-100(replica)Range 50-75(replica)Range 0-25(primary)Range 75-100(skipped)Range 50-75(skipped)Repairs ALL ranges thisnode holds, causingredundant workRepairs ONLY primaryrange. Other nodesrepair their primaries.

Recommendation: Always use -pr for routine maintenance. Running -pr on each node in sequence ensures every range is repaired exactly once.

Behavior Without Keyspace or Table Specification

Section titled “Behavior Without Keyspace or Table Specification”

When running repair without specifying tables, Cassandra iterates through all tables in the keyspace:

Repair Iteration BehaviorRepair Iteration BehaviorKeyspace specified?noyesIterates throughall user keyspacesGet all non-system keyspacesUse specified keyspaceTables specified?noyesFor each table:1. Build Merkle trees2. Compare with replicas3. Stream differences Tables repaired sequentiallyunless -j specifiedGet all tables in keyspaceRepair next tableWait for completionyesMore tables?noRepair specified tables only

Important considerations:

  • Tables are repaired sequentially by default
  • Use -j <threads> to repair multiple tables in parallel
  • Large tables dominate repair duration
  • Consider repairing critical tables separately

After incremental repair, Cassandra performs anti-compaction to separate repaired and unrepaired data:

Anti-Compaction ProcessAnti-Compaction ProcessBefore Anti-CompactionSSTable A (mixed data)After Anti-CompactionSSTable A-repairedSSTable A-unrepairedRepaired partitionsUnrepaired partitionsRepaired onlyUnrepaired onlyMarked with repairedAttimestamp. Excluded fromfuture incremental repairs.Split byrepair status

Anti-compaction ensures clean separation between repaired and unrepaired data, enabling efficient future incremental repairs.

While standard repairs reconcile user table data across replicas, Paxos repairs specifically reconcile the Paxos state used by lightweight transactions (LWTs). LWTs are statements that include IF conditions (such as INSERT ... IF NOT EXISTS or UPDATE ... IF column = value), which provide linearizable consistency guarantees.

Paxos repairs maintain LWT linearizability and correctness, especially across topology changes such as bootstrap, decommission, replace, and move operations.

Paxos repairs are only relevant for keyspaces that use LWTs. For keyspaces that never use LWTs, Paxos state does not affect correctness, and operators MAY safely skip Paxos repairs for those keyspaces.

Cassandra 4.1+ provides two distinct Paxos repair mechanisms:

  1. Background Paxos repair — runs automatically every 5 minutes (configurable). Completes uncommitted Paxos transactions but does NOT advance the Paxos repair low bound or enable garbage collection of system.paxos data.
  2. Coordinated Paxos repair — runs via nodetool repair --paxos-only or as part of regular nodetool repair. Completes uncommitted transactions AND advances the low bound in system.paxos_repair_history, enabling garbage collection when using paxos_state_purging: repaired.

For clusters using paxos_state_purging: repaired, operators MUST run regular coordinated Paxos repairs. The automatic background repair alone is not sufficient. See Understanding the Two Paxos Repair Mechanisms in the Repair Strategies guide for the full distinction.

In Cassandra 4.1 and later, a Paxos repair gate runs before certain topology changes complete (for example, node bootstrap). This gate ensures that Paxos state is consistent across all replicas for the affected token ranges before the topology change finalizes.

If Paxos repair cannot complete for the affected ranges and keyspaces—for example, because nodes are overloaded, have very large partitions, or some replicas are unavailable—the topology change MUST fail to avoid violating LWT correctness guarantees.

Operators MAY encounter errors such as PaxosCleanupException with message CANCELLED when overloaded replicas cannot finish Paxos cleanup within the allowed time. This typically indicates that the cluster is under too much load or that specific partitions are too large for Paxos cleanup to complete successfully.

Paxos Repair Gate During Topology ChangePaxos Repair Gate During Topology ChangeJoining NodeCoordinatorExisting ReplicasJoining NodeJoining NodeCoordinatorCoordinatorExisting ReplicasExisting ReplicasRequest bootstrapIdentify affectedtoken rangesPaxos Repair GateInitiate Paxos cleanupfor affected rangesEach replica must completePaxos cleanup for LWT keyspacesalt[All replicas complete cleanup]Cleanup completeProceed with bootstrapStream dataBootstrap complete[Cleanup fails (timeout, overload, etc.)]PaxosCleanupException(CANCELLED)Bootstrap FAILEDTopology change blockedto preserve LWT correctness

Cassandra 4.1+ introduces Paxos v2, an updated Paxos implementation for lightweight transactions. Paxos v2 provides several improvements:

  • Reduced network round-trips for LWT reads and writes
  • Improved behavior under contention when multiple clients compete for the same partition
  • Works in conjunction with regular Paxos repairs and Paxos state purging

Paxos v2 is selected via the paxos_variant setting in cassandra.yaml (values: v1 or v2).

paxos_variant and paxos_state_purging are independent settings — neither requires the other. However, the recommended production configuration for LWT-heavy clusters is paxos_variant: v2 combined with paxos_state_purging: repaired, which together enable the commit consistency optimization.

To safely take full advantage of Paxos v2 with repaired purging, operators MUST ensure:

  1. Regular coordinated Paxos repairs are running (via nodetool repair --paxos-only schedule or regular nodetool repair)
  2. Paxos state purging is configured appropriately (see Paxos-related cassandra.yaml configuration in the Repair Strategies guide)

Detailed configuration options and upgrade guidance are covered in the Repair Strategies documentation.