On this page
Anti-Entropy and Data Repair
Read repair only fixes the data that is read, so cold data may remain inconsistent for a long time. Anti-entropy uses Merkle trees to systematically compare and repair replicas in the background—gossip-driven incremental comparison eventually drives all replicas toward convergence.
Overview
The previous section explained how read repair and hinted handoff "fix" data incidentally during read and write paths. These are passive repairs—they only fix the specific key that was read, leaving large amounts of cold data potentially inconsistent for a long time. Anti-entropy is an active, systematic repair process: the background continuously compares replicas, identifies all inconsistencies, and fixes them, eventually driving all replicas to consistency (convergence). The engine for this section is the Merkle tree—a tree structure that efficiently compares the differences between two datasets.
Why Read Repair Is Not Enough
Read repair has three blind spots:
- Only fixes the data that is read: If cold data hasn't been read for a month, no one addresses the inconsistency for a month.
- Only fixes individual keys: It doesn't detect cases where "an entire replica is lagging behind."
- Doesn't handle node re-joins: When a node recovers after being down for a week, it has missed a large number of writes, and hinted handoffs may have expired—requiring a systematic full reconciliation.
Anti-entropy addresses these three blind spots.
Merkle Tree: The Engine for Comparing Differences
How do you efficiently find which keys are inconsistent between data (which could be several GB of key-value sets) on two nodes? Comparing them one by one is too slow. Merkle trees reduce the comparison complexity to ~O(log n):
Construction method:
- Divide all keys on the local node into several buckets (leaves of the Merkle tree) based on ranges.
- Calculate a hash for each bucket (an aggregated hash of all key-values within that bucket).
- Parent node hash = hash(concatenation of child node hashes).
- The root hash identifies the entire dataset.
To compare data between two nodes: exchange root hashes → if they match, the data is consistent; if they differ, recursively compare child node hashes → locate the inconsistent bucket → perform one-by-one comparison and repair only for the keys within that bucket. Two nodes only need to exchange hashes, not the actual data; the actual data is transferred only after the inconsistent bucket is identified.
Engineering Details
- Dynamic Construction: Merkle trees are not persistent—they are built from the current data only when a comparison is needed, and discarded afterward. Cassandra's
nodetool rebuildtriggers this construction. - Bucket Granularity: If buckets are too fine, the tree becomes too large and construction is slow; if too coarse, localization is inaccurate, leading to excessive repairs. Cassandra defaults to 15 levels.
- Construction Cost: Building requires scanning all data to calculate hashes, which is heavy for large datasets—therefore, anti-entropy is low-frequency background (e.g., once a day), not real-time.
Two Anti-Entropy Modes
Gossip Anti-Entropy: The Dynamo Approach
The Gossip Protocol is used not only for member discovery but also for anti-entropy. Each node periodically randomly selects a peer:
- Both parties exchange their respective Merkle tree root hashes.
- If root hashes differ → recursively compare to find inconsistent buckets.
- For inconsistent keys, use version vectors (or last write timestamps) to decide which update wins—it's not simply "push what I have to you if you don't," because both sides may have updated the same key (conflicts are discussed in Conflict Resolution).
- Push the winning version to the other party.
Randomly selecting peers for gossip anti-entropy has favorable mathematical properties: among N nodes, if each time a random peer is selected, inconsistent data is repaired with high probability within O(log N) rounds—this is the core conclusion of Demers' 1987 "Epidemic Algorithms."
Full Repair: Node Re-joining
When a node re-joins after being down, it has missed a large number of writes, and hinted handoffs may have expired, requiring a one-time full reconciliation:
- The new node fully pulls the data for the token ranges it is responsible for from other replicas (or incrementally: only pulls changes that occurred during the downtime—if a changelog exists).
- Use Merkle trees for verification during the pull (to confirm the pulled data is complete and correct).
- After pulling, build local indexes and Bloom filters.
nodetool rebuild in Cassandra, riak-admin transfers in Riak, and HDFS DataNode block reports all follow this pattern.
Interaction Between Consistency and Repair
A counter-intuitive point: anti-entropy pays off the debt of "eventual consistency". If a system uses strong consistency (e.g., Raft), anti-entropy is not needed during normal operation—Raft's log replication already ensures that committed entries are consistent across the majority. Raft only needs "repair" in the following cases:
- Snapshot Transmission: A newly joined follower is too far behind, and the leader has already truncated its log → the leader sends a snapshot (a full state machine snapshot), which is essentially a full repair.
- Disk Corruption: Local data on a single node is corrupted → re-pull the full data from other nodes (similar to "node re-joining" in anti-entropy).
Therefore, the engineering form of anti-entropy depends on the consistency model: the more a replication strategy leans toward eventual consistency, the more important anti-entropy becomes, requiring engineering implementations of Merkle trees + gossip; the more it leans toward strong consistency, the more anti-entropy is replaced by "consensus protocol log replication + snapshots"—but the need to "re-pull full data" remains, only the trigger conditions and engineering implementations differ.
Repair Scheduling and Throttling
Anti-entropy is not cost-free: building Merkle trees involves scanning all data (CPU + IO), and transferring hashes and actual data consumes network bandwidth. Control is needed:
- Throttling: Limit the throughput consumed by repairs. Cassandra's
compaction_throughput_mb_per_secalso affects streaming during repair. - Scheduling: Avoid business peak hours; or trigger full repairs during low-traffic periods, while only performing read repairs during peaks.
- Incremental First: If the system has a changelog (a list of recently changed keys), repair this part first, then perform full verification.
Trade-offs and Failure Modes
- Using read repair as anti-entropy: Cold data will never be repaired → periodic full anti-entropy is required.
- Merkle tree construction overwhelms IO: Node CPU/disk spikes during anti-entropy, affecting normal reads/writes → throttling + low-traffic scheduling.
- Full repair saturates the network: Pulling several GB of data fills the bandwidth → streaming throttling + parallelism control.
- Incorrect version conflict resolution: Using "last write wins" during anti-entropy overwrites concurrent updates → use version vectors instead of timestamps (see Conflict Resolution).
References
- Papers: "Dynamo" (anti-entropy via Merkle tree, 2007), "Epidemic Algorithms for Replicated Database Maintenance" (Demers 1987)
Keywords: anti-entropy, Merkle tree, hash tree, incremental comparison, gossip repair, read repair, hinted handoff, replica migration, consistent hashing rebalance, full vs incremental repair, repair scheduling, repair throttling, version vector, epidemic algorithm