Part 10 · 1 chapters · ~13 min
CRDTs and Client Sync
The client as a replica: why last-writer-wins and locks fail offline, the commutative, associative and idempotent merge that guarantees convergence, counters, observed-remove sets and registers, sequence CRDTs for collaborative text, practical sync with local logs and relays, and the invariants that still need a single authority.
19
Merging without asking anyone
code
// two small CRDTs: a G-counter and an observed-remove set
type GCounter = Record<string, number>;
const inc = (c: GCounter, me: string, n = 1): GCounter => ({ ...c, [me]: (c[me] ?? 0) + n });
const value = (c: GCounter) => Object.values(c).reduce((a, b) => a + b, 0);
const mergeG = (a: GCounter, b: GCounter): GCounter => {
const out = { ...a };
for (const [k, v] of Object.entries(b)) out[k] = Math.max(out[k] ?? 0, v); // commutative, idempotent
return out;
};
type ORSet<T> = { adds: Map<string, T>; removed: Set<string> }; // tag → element; removed tags
const add = <T,>(s: ORSet<T>, x: T): ORSet<T> => ({ ...s, adds: new Map(s.adds).set(crypto.randomUUID(), x) });
const remove = <T,>(s: ORSet<T>, x: T): ORSet<T> => // removes only the tags it has seen
({ ...s, removed: new Set([...s.removed, ...[...s.adds].filter(([, v]) => v === x).map(([t]) => t)]) });
const mergeS = <T,>(a: ORSet<T>, b: ORSet<T>): ORSet<T> =>
({ adds: new Map([...a.adds, ...b.adds]), removed: new Set([...a.removed, ...b.removed]) });
const elements = <T,>(s: ORSet<T>) => new Set([...s.adds].filter(([t]) => !s.removed.has(t)).map(([, v]) => v));| data | fits a CRDT? | why |
|---|---|---|
| shared shopping list, notes, drafts | yes | merging both edits is what users want |
| reactions, view counts | yes (counters) | order never matters |
| a form being filled offline | often a simple LWW per field | one user, rare true conflicts |
| account balance, seat booking, unique usernames | no | an invariant across replicas needs a single authority or consensus |
the frontend link
KinniJije as a PWA, and any offline queue you build, are this chapter in practice. Name which data merges freely and which must wait for the server, and design the UI for the second kind: "saved on this phone, will send when online".
THE CLIENT AS A REPLICA: CRDTS AND SYNC
offline edits, merges without coordination, and the sync protocols behind collaborative apps
swipe the figure sideways, or tap expand for full screen
1/6
the problem
The problem: two devices edit the same note offline. Last-writer-wins by timestamp silently drops one edit, and clocks on phones are wrong (part 1). Locking needs connectivity. We want both edits kept, and every replica to converge on the same result.