All questions

Vector Clocks

Premium

Vector Clocks

A vector clock is a map from each process in a distributed system to an integer event counter, used to decide whether one event happened before another or whether the two are concurrent. Real systems reach for them when there is no shared wall clock to trust: Amazon's Dynamo, Riak, and many collaborative editors stamp every update with a vector clock so a replica can later tell "this version is strictly newer" apart from "these two versions conflict." Your job is to build one.

You implement a factory vectorClocksLamport(nodeId) that returns a live clock for one process. It exposes tick (log a local event), receive (fold in a message from another process), get (read the current counters), and compare (relate this clock to another). Two pure helpers hang off the factory for working with raw states directly.

Signature

type Clock = Record<string, number>;                     // a missing key means 0
type Relation = 'equal' | 'before' | 'after' | 'concurrent';

function vectorClocksLamport(nodeId: string): {
  tick(): Clock;                  // log a local event: bump your own counter
  receive(other: Clock): Clock;   // merge (elementwise max), then bump your own
  get(): Clock;                   // a copy of the current counters
  compare(other: Clock): Relation;
};

// Static-style helpers on the factory (pure functions over raw states):
vectorClocksLamport.merge;         // (a: Clock, b: Clock) => Clock    elementwise max
vectorClocksLamport.compareClocks; // (a: Clock, b: Clock) => Relation

Examples

const a = vectorClocksLamport('a');
const b = vectorClocksLamport('b');

a.tick();               // a logs an event -> { a: 1 }
const msg = a.tick();   // and another     -> { a: 2 }   (attach this to the message)

b.tick();               // b logs an event -> { b: 1 }
b.receive(msg);         // max({ b: 1 }, { a: 2 }), then bump b -> { a: 2, b: 2 }
vectorClocksLamport.compareClocks({ a: 2, b: 1 }, { a: 2, b: 2 });
// 'before'      every counter is at most the other, and b is strictly less

vectorClocksLamport.compareClocks({ a: 1 }, { b: 1 });
// 'concurrent'  a leads on its slot, b leads on its slot; neither dominates

Notes

  • Missing means zero. A process you have never heard from has counter 0. Store only the counters you have actually touched, and treat any absent slot as 0 when you read, merge, or compare.
  • receive is max-then-tick. First take the elementwise maximum with the incoming state, then increment your own counter — receiving a message is itself a local event, so it advances your slot just like tick does.
  • compare is a partial order. Return before when every one of this clock's counters is at most the other's and at least one is strictly less; after for the mirror image; equal when all counters match; and concurrent when each clock leads somewhere, so neither dominates.
  • States travel, not instances. receive and compare take a raw clock object — the same shape get() returns — so a clock can be serialized and sent over the wire. Do not assume the argument is another factory instance.
  • Pure and synchronous. The "clock" counts events, not seconds. There are no timers and no Date.now; every method is a plain synchronous computation.

FAQ

How is a vector clock different from a Lamport timestamp?
A Lamport timestamp is a single counter that gives every event a total order but cannot tell a causal pair from a concurrent one. A vector clock keeps one counter per process, so comparing two clocks reveals whether one event happened before the other or the two are genuinely concurrent.
How do vector clocks detect a write-write conflict?
Compare the two versions' clocks: if neither dominates the other, the result is 'concurrent', which means the updates were made without seeing each other. That is exactly a conflict the application must resolve.
What does a vector clock cost in space?
One integer per process that has ever produced an event, so O(N) per clock for N processes. Systems with churning membership bound this with dotted version vectors or by pruning entries for departed nodes.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium