Here's a problem every replicated datastore eventually faces: two replicas are supposed to hold the same 50GB key range, but one missed a few writes during a network blip. Which keys differ? The brute-force answer — ship everything and compare — costs 50GB of network for what might be three stale keys. The answer used by Cassandra, DynamoDB, Riak, and (in spirit) git and blockchains fits in one picture:
A real techdiagrams document — download the JSON and import it into the editor.
The structure: hashes of hashes
A Merkle tree is built bottom-up over the data:
- Leaves are hashes of data blocks (key ranges, files, transactions — the unit doesn't matter).
- Internal nodes are hashes of their children's hashes:
h01 = h(b0, b1). - The root is a single hash that summarizes everything below it.
The property that makes it magic: any change anywhere changes the root. Flip one bit in block 2 and h23 changes, so the root changes. The root is a tamper-evident, difference-evident fingerprint of the entire dataset — at the cost of one small hash.
The protocol: descend only where it differs
Anti-entropy repair between replica A and replica B becomes a conversation:
- Exchange roots. Equal? Done — the entire range is provably in sync, and that certainty cost ~32 bytes.
- Roots differ (our diagram:
9f3a…vs8c1d…). Exchange the children.h01matches — that whole subtree is now proven identical and never spoken of again.h23differs. - Recurse into the differing branch only.
b3matches,b2differs. - Sync block 2. Total network cost: a handful of hashes plus the one block that actually changed.
That's the headline: O(log n) hash exchanges to locate any difference, instead of O(n) data transfer to find it. The tree doesn't just detect divergence — it routes you to it.
Where you've already met this
- Cassandra builds Merkle trees per replica during repair and streams only mismatched ranges.
- Dynamo (the paper) used them for exactly the anti-entropy story above.
- Git is a Merkle structure — commit hashes cover trees, which cover blobs; that's why
git fetchcan figure out what you're missing so cheaply, and why history can't be silently rewritten. - Certificate Transparency and blockchains use the tamper-evidence property: you can prove one entry belongs to a dataset with a log-sized proof path.
The trade-offs nobody mentions
- Tree freshness. Hashing 50GB isn't free either — the trees themselves must be built or maintained. Cassandra pays this at repair time (expensive, occasional); other systems maintain trees incrementally on write (cheaper per repair, cost smeared over every write).
- Leaf granularity is a dial. Huge leaves → tiny tree, but a one-key difference re-syncs a huge block. Tiny leaves → surgical repair, but a deeper tree and more hashing. This is a classic tunable that deserves a load test, not a default.
- It finds that you differ, not who's right. Merkle trees locate divergence; resolving it still needs timestamps, vectors, or last-write-wins — with all the usual caveats.
See it as a system, not a data structure
The diagram above treats the tree like an architecture — because in a running system, it is one: replicas, a comparison protocol, a repair path. Import it into techdiagrams.net, attach real numbers (how often do blips happen? how big is a block?), and the "which leaf granularity?" question becomes something you can actually reason about instead of copying a default.