CRDT Lab
Three copies of the same data, each editing while it cannot reach the others, and all three ending up identical anyway. That is what a CRDT — a conflict-free replicated data type — is built to do. This is a hands-on lab and a tutorial: take replicas offline, edit all of them, then reconnect and watch the messages arrive and merge.
The network on this page is simulated: delay, reordering, duplicates and dropped messages are drawn from a seeded random source so every preset replays the same way. The merges are real. Each message carries the sender’s state serialized to JSON, and delivering it decodes those bytes and runs the Go merge function whose three merge laws are unit-tested and fuzzed, compiled to WebAssembly and running in this tab.
Loading the lab…
Presets
B and C went offline. While they were away, A added bread, B removed eggs, and C added eggs back and added tea. Nobody saw anybody else's edit.
Next: Press Reconnect all, then Deliver all. The three sets end identical, and eggs stays: C's add carries a tag B's remove never saw, so the add wins.
Data type and network
Every edit made anywhere sums to .
Replica A
Value {bread, eggs, milk}
Hash 3471a4d5
Naive
Replica B
Value {milk}
Hash c31924be
Naive
Replica C
Value {eggs, milk, tea}
Hash 0598140b
Naive
In flight
Tick 1 · sent 6 · delivered 4 · duplicates delivered 0 · out of order 0 · dropped 0
- #5 A→B, due tick 2, 101 bytes
{"adds":{"bread":{"A:3":true},"eggs":{"A:2":true},"milk":{"A:1":true}},"tombstones":{},"seq":{"A":3}} - #6 A→C, due tick 2, 101 bytes
{"adds":{"bread":{"A:3":true},"eggs":{"A:2":true},"milk":{"A:1":true}},"tombstones":{},"seq":{"A":3}}
What a CRDT is
A CRDT is a data type designed so that copies of it can be edited independently and then merged, and the merge always lands every copy on the same state. The trick is not clever conflict resolution after the fact. It is choosing a state and a merge function so that the order of merges does not matter, the grouping of merges does not matter, and merging the same thing twice changes nothing. Mathematicians call those three properties commutative, associative and idempotent. With all three, a network may delay, reorder or duplicate messages and every replica still ends up in the same place once it has heard from everyone.
Every type on this page is state-based: a replica sends its whole state, and the receiver merges it into its own. The other family, operation-based CRDTs, sends only the edit and uses smaller messages, but it needs the network to deliver every operation exactly once and in causal order — which is exactly what this page’s network refuses to promise.
Why each type merges the way it does
- G-Counter
- Each replica has its own slot and only ever raises it. Two copies of a slot can only differ because one is older, so the merge takes the larger number, slot by slot, and the value is the sum of the slots. Adding the slots on merge instead of taking the maximum is the classic mistake: it counts every duplicated message twice.
- PN-Counter
- A counter that can go down is two grow-only counters, one for increments and one for decrements. A single slot that could fall would break the rule that a larger number means a newer one.
- LWW-Register
- One value, and the latest write wins. “Latest” is a Lamport timestamp, not a clock on the wall: a replica stamps its next write one past the highest stamp it has seen, and ties are broken by replica name the same way everywhere. The cost is stated plainly: when two writes happen concurrently, one of them is discarded. Every replica discards the same one, so they agree — they agree on losing a write.
- OR-Set (add-wins)
- Every add gets a unique tag. A remove deletes only the tags its replica has seen. If one replica removes an item while another adds it again, the new add carries a tag the remover never saw, so the item survives: the add wins. The partition preset shows exactly this with eggs.
- Text (RGA)
- Each character records the character it was typed after and gets a unique id. Reading the text walks from the start, and characters typed after the same spot are ordered by id, identically on every replica. Two people typing at the same place offline get both of their words, each kept whole, in the same order everywhere.
What it costs
Convergence is paid for in metadata. The counters keep one slot per replica that ever wrote. The set keeps every add tag and, for every remove, a tombstone: the removed tag is remembered forever so that a late copy of the old add cannot bring the item back. The text keeps every deleted character as a tombstone too, because a later insert may name it as the character it was typed after. The State line in each panel counts these, and the In flight list shows each message’s size in bytes — watch both grow as you edit. Real systems prune tombstones once every replica is known to have seen them, which needs knowledge of who the replicas are; this lab never prunes.
What the lab shows, and what it does not
It shows that the replicas converge: after every message is delivered, each online replica holds the same state, and the hash in each panel proves it. It does not show that the converged state is the one your application wants. A PN-counter tracking stock will happily converge on a negative number. An add-wins set will bring back an item a person deliberately removed if someone else re-added it at the same time. A last-writer-wins register will silently drop a concurrent write. Those are rules about your business, and a CRDT does not know them: it guarantees agreement, not correctness. The network here is also simpler than a real one — three fixed replicas, no partial membership, no persistence — and the naive counter is deliberately naive, to show the failure a CRDT prevents, not as a fair picture of every non-CRDT design.
Where CRDTs are used
Collaborative editors and local-first apps use text and JSON CRDTs so that people can edit offline and sync later; the open-source libraries Automerge and Yjs are two widely used implementations. Distributed databases use them to accept writes in more than one region at once: Riak shipped counters, sets and maps as built-in data types, and Redis Enterprise uses CRDTs for its active-active replication. Presence lists — who is online in a chat room — are a common small case, because they must keep working while parts of a cluster cannot reach each other.