Conflict-free replicated data types. Increment, write, add, remove — partition the network and watch them converge.
Counter: 0
Select a CRDT type and interact with the nodes. Click an edge to partition or heal.
How G-Counters Work
A G-Counter (grow-only counter) is one of the simplest CRDTs — conflict-free replicated data types. Each node in the system maintains a vector of counts, one slot per node. When a node wants to increment, it only bumps its own slot. To read the counter value, you sum all slots.
The merge operation takes the pointwise maximum of two vectors. This is what makes it conflict-free: no matter what order merges happen in, or how many times you merge, you always converge to the same result. Mathematically, merge forms a join-semilattice — it's commutative, associative, and idempotent.
Try partitioning edges to split the network. Each side keeps incrementing independently. When you heal the partition, the nodes sync back up — no conflicts, no lost increments. That's the whole point of CRDTs.
How PN-Counters Work
A PN-Counter supports both increment and decrement by composing two G-Counters: a P (positive) counter for increments and an N (negative) counter for decrements. The net value is the sum of P minus the sum of N.
You can't just subtract from a G-Counter — that would break the "grow-only" property that makes merge work. Instead, you track positives and negatives separately. Each one merges independently using pointwise max, and the final value is derived from their difference.
This is a common pattern in CRDTs: build complex types by composing simpler ones. The PN-Counter inherits all the convergence guarantees of the G-Counter, while adding the ability to decrement.
How LWW-Registers Work
A Last-Writer-Wins Register (LWW-Register) is the simplest way to replicate a mutable value. Each write is tagged with a timestamp. When merging, the value with the highest timestamp wins. Ties are broken by node ID.
The appeal is simplicity — it works for any data type and the merge rule is trivial. But it has a fundamental weakness: it depends on synchronized clocks. If node C's clock is skewed ahead, its writes will always "win" even if they happened earlier in real time.
Try adjusting the clock skew for node C. Write a value on another node, then write on C — even though C wrote "later" in wall-clock time, its inflated timestamp makes it win the merge. This is exactly the problem that drives people toward more sophisticated CRDTs.
How OR-Sets Work
An Observed-Remove Set (OR-Set) solves the "add vs remove" conflict that plagues simpler set CRDTs. In a naive set, if one node adds an element while another concurrently removes it, you have a conflict. The OR-Set resolves this with add-wins semantics: a concurrent add always beats a concurrent remove.
The trick is unique tags. Every add operation creates a new unique tag (like "A-3") for the element. A remove operation only deletes the specific tags it has observed. So if node A adds "apple" (tagged "A-1") and node B concurrently removes "apple" (removing tag "A-1"), but node C also adds "apple" (tagged "C-2"), the merge keeps "apple" because tag "C-2" was never removed.
This is more powerful than a G-Set (add-only) or 2P-Set (where removed elements can never be re-added). The OR-Set allows arbitrary add and remove operations while guaranteeing convergence.