What problem does a CRDT (Conflict-free Replicated Data Type) solve, and how does a G-Counter's merge rule guarantee replicas converge without any coordination between nodes?
answer
- max, not sum, for merge
- per-node slot, sum for read
- strong eventual consistency
- no coordinator needed
- grow-only = no decrement
basics
~20 sA CRDT lets many computers update the same piece of data at the same time without talking to each other, and their copies can always be combined back into one correct answer later. A G-Counter only grows, so merging two copies means keeping the bigger count each machine reported.
solid answer
~40 sA CRDT (Conflict-free Replicated Data Type) is a data type whose replicas can be updated concurrently, independently, and offline, then merged deterministically without coordination, because the merge function is mathematically guaranteed to converge. The classic example is the G-Counter (grow-only counter): each replica keeps a per-node counter of its own increments instead of one shared integer. To read the total you sum all per-node slots; to merge two replicas you take the element-wise maximum of each node's slot. Because 'max' is commutative, associative, and idempotent, replaying merges in any order, any number of times, or dropping duplicate messages always converges to the same total - solving eventual consistency without locks, consensus, or a coordinator.
go deeper
Should describe, in plain terms, that CRDTs let replicas update independently and merge safely, and walk through the G-Counter idea (each node owns a slot, read = sum, merge = max) even if the terms 'commutative/associative/idempotent' aren't yet fluent.
Should name the merge rule precisely (element-wise max) and explain why sum would be wrong (breaks idempotency), and know that decrement needs a different structure.
Should connect the mechanism to the broader guarantee (strong eventual consistency), articulate the coordination cost it avoids, and know at least one production system that ships this natively.
Should reason about when this trade (no coordination, unbounded per-replica metadata growth) is and isn't worth it for a given system's replica count and churn pattern, and connect it to broader consistency-model choices across the architecture.
## The conflict a CRDT sidesteps In a normal distributed system, when two nodes hold copies of the same mutable value and both are updated independently while disconnected, you get a conflict: which write wins? Traditional systems solve this with: - **locks**; - **consensus protocols** (Raft, Paxos); - **last-writer-wins timestamps** that quietly discard one of the updates. A **CRDT** sidesteps the conflict entirely by designing the data type itself so that `merge` is a well-defined, order-independent operation — no matter which order updates arrive in, how many times a message is duplicated, or which subset of replicas have seen which updates yet, applying the merge function to any set of replica states always produces the same final result. This guarantee is called **strong eventual consistency**: once all replicas have seen the same set of updates (in any order), they hold identical state, with no explicit conflict resolution step and no need for replicas to be online or reachable at the same time. ## How a G-Counter works A **G-Counter** (grow-only counter) is the simplest CRDT and a good vehicle for the general idea. Instead of representing the counter as one shared integer, each replica (A, B, C) owns a private slot in a vector: `{A:0, B:0, C:0}`. 1. When A increments, it only ever bumps its own slot: `{A:1, B:0, C:0}`. 2. The counter's value is always the sum of all slots. 3. To merge two replica states you take the **element-wise maximum** of each slot, not a sum — merging `{A:3,B:1,C:0}` with `{A:2,B:1,C:4}` yields `{A:3,B:1,C:4}`. Because each node only increases its own slot and merge takes the max, no increment is ever lost (max retains the largest count either replica had recorded) and no increment is double-counted (max, not sum, so re-merging the same state twice changes nothing). ## What coordination would cost instead The alternative — shipping every increment to a single owner, or running a consensus protocol so all nodes agree on a serialized order before applying an update — costs a network round trip (or a quorum) per write, and stalls writes during a network partition. That's fine for a bank ledger, but for many practical counters (like counts, shopping carts, presence indicators) you'd rather let every replica accept writes locally, instantly, even fully offline, and reconcile later. CRDTs make that reconciliation mathematically guaranteed to succeed rather than requiring bespoke per-feature conflict logic — the appeal behind **offline-first** apps and **multi-region active-active** designs, where writes never block on other regions being reachable. ## What you give up The cost is **expressiveness and space**. - A G-Counter can only grow — it structurally cannot support decrement (that needs a **PN-Counter**, built from two G-Counters). - More generally, CRDTs only exist for operations whose combined effect can be expressed as a **commutative, associative, idempotent** merge; not every abstract data type admits such a design, and some (like a map with complex nested invariants) require careful structural tricks (unique per-op tags, tombstones) to make merges well-defined. - Complex CRDTs like sets carry per-element metadata that can dwarf the actual payload. ## How teams get it wrong 1. A team new to CRDTs commonly reaches for a plain replicated integer, lets each node add or subtract directly, and **resolves** conflicts with last-writer-wins — this silently drops updates (a concurrent increment from another node just vanishes). 2. Another mistake is merging a G-Counter-shaped vector with **sum** instead of **max**, which double-counts increments every time the same state is re-merged, breaking idempotency. 3. A third failure mode is ignoring the vector's unbounded growth — with high replica churn (autoscaling instances each getting a slot), the vector never shrinks, since a slot can't be safely removed while any other replica might still hold stale state referencing it. ## Where it ships - **Riak KV** (Basho) built native CRDT data types (counters, sets, maps, registers) directly into its replicated key-value store so applications get conflict-free merges without hand-rolling resolution logic. - **Redis Enterprise's CRDB** (Conflict-free Replicated Database) uses CRDTs for active-active geo-replication across regions. - Collaborative editors built on **Automerge** or **Yjs** use CRDT-based sequences so multiple users can type concurrently offline and merge edits deterministically when they reconnect.
- Why can't a G-Counter support decrement directly?Because merge takes the max of each replica's own slot, a decrement would look identical to 'this replica reset and hasn't caught up yet,' so max would silently discard it. Decrement requires a separate structure - a PN-Counter pairs two G-Counters, one tracking increments and one tracking decrements, and the value is (sum of increments) minus (sum of decrements).
- What happens if a merge message is delivered twice?Nothing changes - merge is idempotent by design (max of a value with itself is itself), so duplicate delivery, common in unreliable networks, is automatically safe and requires no deduplication logic.
- Does a G-Counter need vector clocks or version numbers?No - the per-node slot vector already encodes 'how much I know from each replica,' which serves the same causality-tracking purpose a vector clock would, without a separate mechanism.
Like several people independently topping up a shared water tank from their own hose, and reading the total by adding up everyone's own contribution gauge - even if two people top up 'at the same time' or the readings arrive out of order, summing each person's own gauge always gives the right total.
saying these in an interview costs you the question
- Says CRDTs need a central coordinator to merge
- Merges a G-Counter's slots with sum instead of max
- Thinks last-writer-wins is the same thing as a CRDT merge
- Can't explain why decrement needs a second counter
- Assumes CRDTs work for any data type with no design constraints