Back to the map

Module 13 · Distributed fundamentals · Lesson 10 of 18

Vector clocks

One counter per participant, carried together, so that comparing two timestamps tells you exactly whether one event caused the other or they were concurrent. The rules.

DeepNice-to-knowSenior 12 minVideoPro Updated Nov 2026

The video walkthrough is part of Pro.

See what Pro includes
Video walkthrough · 2:17 · narrated, with captions

Why it matters

A user edits their profile on their phone while offline and on their laptop; both sync. Did one edit supersede the other, or did they happen independently? Lamport timestamps cannot say. Vector clocks can: they capture exactly what each replica had seen when it wrote, so the system can tell "this version descends from that one, keep the newer" apart from "these two are concurrent, surface both or merge". This is the mechanism under Dynamo, Riak and causal-consistency systems, and the standard follow-up after Lamport in interviews.

The idea

A vector clock is an array with one integer per process (or replica). Each process keeps its own vector.

  1. 1

    Local event

    Increment your own slot.
  2. 2

    Send

    Increment your own slot and attach the whole vector to the message.
  3. 3

    Receive

    Take the element-wise maximum of your vector and the received one, then increment your own slot.

Comparison: V(A) is less than V(B) if every element of V(A) is less than or equal to the corresponding element of V(B) and at least one is strictly less. Then A happened before B. If neither vector is less than the other, A and B are concurrent. Unlike Lamport clocks, this is an if-and-only-if: vector clocks capture causality exactly.

R1R2R3[1,0,0][1,1,0][1,2,0][0,0,1][1,2,2][1,0,0] < [1,2,0]: causal · [1,2,0] vs [0,0,1]: concurrent
Each replica tracks what it has seen from every replica. Two versions with incomparable vectors are concurrent: a real conflict.

Version vectors in replicated stores

In a Dynamo-style key-value store, each stored value carries a version vector with one slot per replica (or per client) that has updated the key. A client reads the value and its vector, modifies it, and writes back with that vector as context. The replica compares: if the incoming vector descends from the stored one, it is a newer version and replaces it; if the stored one descends from the incoming, the write is stale and is ignored or rejected; if they are concurrent, the store keeps both as siblings and returns them on the next read, asking the client to reconcile (merge the shopping carts, pick the latest by application rule, or ask the user). That is how Amazon's cart never loses an item: concurrent adds become siblings that merge by union.

Comparison resultMeaningStore's action
Incoming > storedClient saw the stored version and built on itReplace
Incoming < storedClient wrote based on an older version than what is storedReject or ignore as stale
EqualSame versionNo-op
IncomparableConcurrent writes, neither saw the otherKeep siblings; reconcile on read

Growth and dots

A vector has one slot per participant, so with many clients writing a key the vector grows. Dynamo pruned old entries past a size limit, accepting rare false concurrency. Dotted version vectors track the replica that coordinated each write (a "dot") and keep vectors sized by the number of replicas rather than the number of clients, which is what Riak uses. Causal-consistency systems that must track dependencies across many keys use compressed or coarse-grained vectors for the same reason.

Vector clocks versus the alternatives

Last-write-wins by timestamp is simplest and loses concurrent writes. Lamport timestamps give an order but hide concurrency. Vector clocks detect concurrency precisely at the cost of metadata size and a reconciliation step the application must implement. CRDTs, later in this module, remove the reconciliation step by making merges automatic for specific data types, often using vector-clock-like metadata underneath.

Vector clocks · Backplane