State-based CRDTs: merge information with a join
Use a grow-only counter to derive convergent state exchange and distinguish monotone metadata from the visible query value.
On this page
The question for today
What you’ll understand
- Check commutativity, associativity, and idempotence for a merge.
- Compute a componentwise-max counter state without double counting.
- State the eventual-delivery and monotone-update conditions behind convergence.
A state-based conflict-free replicated data type sends states that can be merged in any order, including repeated delivery. The central tool is a join: the least state that contains the information of both inputs under a chosen partial order. This does not mean every application value grows numerically. It means the internal information order is designed so accepted history is not accidentally forgotten when replicas exchange states.
A merge is more than a convenient function
A join-semilattice supplies a partial order and a least upper bound for every pair of states. Its join is commutative, associative, and idempotent. Commutativity tolerates reversed arrival order, associativity tolerates different grouping, and idempotence tolerates repeated delivery of the same state. These laws explain why sending a whole state twice need not count its contents twice. Choosing addition as the merge for already-accumulated totals usually fails this requirement: merging the same state twice increases the total again.
Local updates must also be inflationary in the chosen state order, and communication must eventually carry the relevant updates to correct replicas. Algebra cannot repair a message that is lost forever without any later state containing its information. Similarly, merely labeling an arbitrary JSON merge as a CRDT does not establish a partial order or its laws. State the payload, update, merge, query, and delivery assumptions, then test the laws on histories as well as individual sample values.
Give every increment one accountable owner
For a fixed-membership grow-only counter, store one nonnegative component per replica. Replica i increments only its own component. Merge takes componentwise maxima, while the visible counter is the sum. A transferred component is an accumulated count from that actor, not another increment to add on arrival. This representation lets a receiver learn a newer prefix without counting the older prefix twice. Stable replica identities and nondecreasing counters remain essential; restarting from zero under the same identity would break the accounting.
To support decrements, one standard construction keeps separate grow-only positive and negative vectors and subtracts their sums when queried. The visible value can decrease even though each internal component only increases. This illustrates why monotonicity must name the state order. It does not automatically make a bounded counter safe: two disconnected replicas could each spend the last available unit. Bounds require an appropriate design such as escrow rights or coordination, not a wish that convergent arithmetic enforces all numeric constraints.
Separate convergence tests from semantic tests
Generate histories with duplicate state exchange, delayed stale states, concurrent increments, and different merge groupings. Compare both internal states and observable queries after all information arrives. Then test the product’s actual requirements: reset behavior, actor retirement, permissions, and bounds are not answered by the three merge laws alone. Delta-state techniques can reduce transmitted payload, but their deltas and propagation protocol must preserve the same information requirements. Sending an arbitrary numeric difference is not enough to inherit the original state proof.
Use the modular lab to construct a counterexample to naive numeric monotonicity. Reset, set m=8 and a=3, predict residues 0,3,6,1, then Step through three transitions. The visible residue decreases from six to one even though another addition occurred. This finite additive orbit is not a G-counter or a join implementation. Its purpose is to force you to name the order: ordinary numeric order on residues does not describe accumulating knowledge, while the CRDT’s componentwise metadata order does.
Work it through
Merge cumulative actor counts
Replicas hold v=(3,0,2) and w=(1,4,2) for actors A, B, C. Both states obey single-writer component rules. Merge, repeat the same merge, and query the total.
Take componentwise maxima: max(3,1)=3, max(0,4)=4, and max(2,2)=2. The joined payload is (3,4,2).
Query its total as 3+4+2=9. Adding the two old query values, five and seven, would produce 12 and count overlapping A and C prefixes more than once.
Merge w again: max((3,4,2),(1,4,2)) remains (3,4,2), so the total stays nine. If A then performs one fresh increment, its component becomes four and the new total is ten.
The payload records each actor’s accumulated contribution. Maximum combines prefixes; addition is reserved for the query across distinct owners.
The living notebook
Explore the model
Interactive lab · modular
Name the order before claiming monotonicity
Reset, set m=8 and a=3, predict residues 0→3→6→1, then Step three times. This additive orbit is not a CRDT join or counter; ordinary numeric order on its residues is not an information order.
- Residue r0
- 0
- Orbit period · m/gcd(m,a)
- 12
- Distinct residues visited
- 1 of 12
Addition modulo m starts at zero: rₙ = na mod m. Playback advances two additions per second. The orbit has period m/gcd(m,a), including period one for a = 0. This is not a cryptographic security test or a primality algorithm. 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 forA monotonicity claim is meaningful only after specifying what state and order it concerns.
Pause & reason
Check your understanding
Your turn
Try it yourself
Merge G-counter payloads (2,5) and (4,3). Then actor B increments once. Compute both totals and name the identity assumption.
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Join by matching actor component, then modify only B’s component.
Compare with the solution
The join is (4,5), with total nine. B’s new increment gives (4,6), total ten. Each component must have a stable single writer that does not reuse old counters after losing state; otherwise the same numbers can no longer faithfully summarize actor contributions.
Keep the idea
Take it with you
- State merge needs a defined information order and join laws.
- Inflationary updates and eventual dissemination are separate requirements.
- A convergent counter does not automatically enforce spending bounds or resets.
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.