Anti-entropy: locate disagreement before choosing a winner
Build comparable Merkle summaries and distinguish detecting divergent replicas from defining their reconciliation semantics.
On this page
The question for today
What you’ll understand
- Specify canonical leaf encoding and matching tree partitions.
- Estimate digest traffic in a single-divergence toy tree.
- Separate integrity, difference detection, and conflict resolution.
Two replicas can accept useful work independently and later need to discover where their states differ. Comparing every stored byte is expensive when almost everything agrees. Anti-entropy periodically repairs disagreement, while a Merkle tree provides hierarchical fingerprints that narrow the search. Neither mechanism tells you which concurrent value the application should prefer. Start with a clear definition of equality, then design efficient detection and a separate, explicit reconciliation rule.
Comparable hashes require comparable inputs
A leaf digest must cover the information whose equality matters: typically a key, value, and relevant version or deletion metadata. Serialize that information unambiguously. Concatenating unframed strings makes pairs such as (ab,c) and (a,bc) indistinguishable before hashing. Length prefixes, explicit types, and a canonical encoding avoid that particular ambiguity. Agree on field order, number representation, Unicode handling, and schema version; semantically identical objects with different byte encodings otherwise appear different to a byte-oriented hash.
Internal nodes summarize ordered child digests over identical key ranges or tree partitions. Domain separation distinguishes a leaf encoding from an internal-node encoding, and fixed-length child digests remove another framing ambiguity. Both replicas must use the same hash function, encoding, partition boundaries, and tree version. A root mismatch caused by different schemas is not evidence that a customer record changed. Conversely, a matching cryptographic digest is a collision-resistance-based equality check, not a logical impossibility of unequal content.
Descend only into differing ranges
If roots disagree, compare child digests. Equal children let the repair skip their covered data, while unequal children require further descent or direct record comparison. For a balanced binary tree with one changed leaf, only the ancestor path must be explored. Multiple scattered changes can force much wider traversal. Tree construction and maintenance also cost CPU, reads, and storage, so digest exchange is only one part of the repair budget. A hot range can keep changing during comparison and require snapshot or version handling.
A digest should summarize a consistent state boundary if the repair protocol assumes one. Recomputing half a tree while updates race through the other half can produce misleading comparisons unless the implementation accounts for that concurrency. Once differing records are identified, transfer enough data and metadata to run the actual merge policy. Copying a larger timestamp without understanding its meaning can lose a legitimate concurrent update. Anti-entropy transport can be correct while the chosen conflict semantics are still wrong for the product.
Repair is a continuing operational responsibility
A successful repair round does not mean future replicas can never diverge. Schedule repeated coverage, measure repair lag and bytes, and preserve deletions long enough that old copies cannot resurrect values. Decide how a new replica establishes a baseline and how a returning replica proves its history is compatible. Authentication protects who may participate; hashes alone do not turn an untrusted peer into an authorized source. If malicious behavior is in scope, integrity and membership require additional protocol guarantees.
Use the function lab only to inspect the cost of a naive all-pairs comparison alternative. Reset, set coefficient a to 1 and position x to 0.5, and predict y=0.25 before Step. Change x to 1 at the retained time and predict y=1. The fourfold change illustrates quadratic comparison growth under normalized units. It does not build a Merkle tree, compute digests, or reconcile replicas. The lesson’s explicit tree trace supplies the actual anti-entropy reasoning.
Work it through
One changed leaf in an eight-record tree
Two comparable perfect binary trees cover eight records, with exactly one changed leaf. Each digest is 32 bytes. Roots are already known to differ; count the subsequent child-digest payload.
Eight leaves give three internal descent levels along the changed leaf’s path. At each visited differing internal node, request the two child digests and skip the child whose digest agrees.
Three levels times two digests gives six digests, or 192 bytes. This identifies the differing leaf under the stated assumptions. Exchanging all eight leaf digests would instead cost 256 bytes.
A 500-byte changed record then adds 500 bytes of payload, making 692 bytes for these two counted categories. Real messages also need root exchange, range identifiers, version context, and framing; several changed leaves would alter the traversal.
Hierarchical comparison saves digest work in this particular sparse-change example; the record’s conflict still needs its own merge rule.
The living notebook
Explore the model
Interactive lab · function
Count a naive comparison cost
Reset, set a=1 and x=0.5, predict y=0.25, then Step. Set x=1 and predict y=1. This fixed quadratic illustrates a normalized all-pairs cost, not Merkle hashing, tree traversal, or replica repair.
- y at chosen x
- 0.608
- Tangent slope · 2ax
- 1.35
- Signed area · ax³/3
- 0.182
This lab uses only y = ax². Playback traces the curve within the fixed integration interval from 0 to x; it does not estimate numerical error or change the chosen point. Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forAn efficient comparison structure can reduce work, but the quadratic lab does not implement that structure.
Pause & reason
Check your understanding
Your turn
Try it yourself
A perfect tree has 16 leaves, one differing leaf, and 32-byte digests. Roots already differ. How many child-digest bytes does the same descent count, and what does root equality assume?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Count binary descent levels and two digests per visited internal node.
Compare with the solution
There are four levels, so 2×4×32=256 child-digest bytes, excluding other traffic. Matching roots support equality only under compatible encodings, matching covered state, and the hash’s collision-resistance assumption; they do not prove authorization or choose a conflict winner.
Keep the idea
Take it with you
- Canonical bytes and matching partitions make digest comparisons meaningful.
- Merkle trees locate disagreement rather than define product semantics.
- Repair needs continuing coverage and correct deletion/version handling.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.