If physical clocks can't be trusted to order events across machines, the fix is to stop asking "when" and start asking "could this event have caused that one" — which turns out to be answerable exactly, using nothing but the messages already being sent.
3 min read
Lesson 2 established that physical clocks can't reliably order events on different machines. Logical clocks sidestep the problem entirely by not trying to measure time at all — instead, they track causality: did event A happen-before event B, could B have been influenced by A, or are they concurrent (neither could have influenced the other, because no message connects them)?
A Lamport timestamp is a counter each process (machine) maintains independently, following two rules:
max(your counter, received counter) + 1.If Lamport(A) < Lamport(B), it's possible A happened-before B — but the reverse guarantee doesn't hold: a smaller timestamp doesn't prove causality, and two genuinely unrelated (concurrent) events can end up with either ordering. This is a real limitation: Lamport timestamps give a total order convenient for tie-breaking, but that order doesn't necessarily reflect true causality.
A vector clock fixes this by giving each process an entire array of counters — one slot per process in the system — instead of a single number. Each process increments only its own slot on a local event, and merges in the maximum of every slot when receiving a message (the same idea as Lamport, applied per-process instead of to one shared counter).
Process 1's vector: [2, 0, 1] (2 local events, 0 known from P2, 1 known from P3)
Process 2's vector: [1, 3, 0]
Comparing two vector clocks tells you exactly one of three things: one happened-before the other (every slot in A is ≤ the corresponding slot in B, and at least one is strictly less), the reverse, or the two events are genuinely concurrent — and unlike Lamport timestamps, this comparison is precise: concurrency detected by a vector clock really is concurrency, not just an artifact of the tie-breaking scheme.
A vector clock needs one slot per participating process, so its size grows with the number of processes in the system — a genuine scalability concern for systems with a very large or frequently-changing number of nodes. This is a direct instance of a pattern that shows up constantly in distributed systems: Lamport timestamps trade precision for a small, fixed size; vector clocks trade a larger, growing size for exact causal precision. Neither is strictly better — the right choice depends on whether the system actually needs to detect concurrent, conflicting writes (where vector clocks earn their cost) or just needs some consistent ordering for tie-breaking (where Lamport timestamps are enough).
Amazon's Dynamo (and systems inspired by it, like early versions of Riak) use vector clocks specifically to detect when two clients wrote to the same key concurrently, without either write knowing about the other — surfacing that conflict to the application (or a resolution policy) instead of silently picking one write via a physical timestamp and quietly losing the other, which lesson 2 showed can pick the wrong one.
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What does it mean for two events to be 'concurrent' in the logical-clock sense?
2. Why can't a smaller Lamport timestamp reliably prove that one event caused another?
3. What real cost do vector clocks pay for their more precise causality detection compared to Lamport timestamps?
Distributed Systems Fundamentals