Recipe
Vector clocks primer
Vector clocks let a distributed system order events without a global wall clock. Every node carries a counter per peer, increments its own on each event, and ships the full vector with every message. Compare two vectors to learn whether one event happened-before the other, or whether they are concurrent.
01.Why ordering matters
Lamport timestamps give a total order but lose causality. Vector clocks recover the partial order you actually need to reason about replicas, retries, and split-brain. If V(a) < V(b), event a causally precedes b. If neither dominates, the events are concurrent and your merge logic owns the conflict.
02.The three update rules
- On a local event at node i, increment
V[i]by one. - On send, attach the current vector to the outgoing message.
- On receive, merge element-wise with
max, then incrementV[i].
03.Reference implementation
A minimal TypeScript clock that any Meridian worker can embed. Persist the vector alongside the event log, never in shared memory.
type Vec = Record<string, number>;
export function tick(v: Vec, id: string): Vec {
return { ...v, [id]: (v[id] ?? 0) + 1 };
}
export function merge(a: Vec, b: Vec, id: string): Vec {
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
const out: Vec = {};
for (const k of keys) out[k] = Math.max(a[k] ?? 0, b[k] ?? 0);
return tick(out, id);
}
export function happensBefore(a: Vec, b: Vec): boolean {
let strict = false;
for (const k of new Set([...Object.keys(a), ...Object.keys(b)])) {
const x = a[k] ?? 0, y = b[k] ?? 0;
if (x > y) return false;
if (x < y) strict = true;
}
return strict;
}