Logical Clocks (Scalar / Vector / Matrix) + Physical Time Sync
Time Without A Clock
The most surprising thing about distributed systems is that there's no shared "now". Each node has its own quartz crystal that ticks at a slightly different rate. Two events happen — was X before Y, after Y, or simultaneously? The honest answer is: we can't tell without a discipline.
Leslie Lamport's 1978 paper "Time, Clocks, and the Ordering of Events in a Distributed System" gave us that discipline. The trick: forget physical time. Define causality instead, then build clocks that respect it.
The Happened-Before Relation
Lamport's has three rules:
1. Same-process order: if and are in the same process and occurs first, then . 2. Send precedes receive: . 3. Transitive: .
Two events that are NOT related by (in either direction) are concurrent. Two ships passing in the night; neither caused the other.
Notice what's NOT in this definition: anything physical. We've defined a *partial order* on events purely from local sequencing + communication. Causality is now a graph property, not a wall-clock property.
Logical Clocks
A logical clock is a function that assigns timestamps to events such that . That's called consistency (or monotonicity). The stronger property — strong consistency — requires the converse too: .
This converse is what separates scalar clocks from vector/matrix clocks. Memorise this distinction; it's the most-asked exam trap on logical clocks.
Scalar (Lamport) Clocks
Each process keeps a single integer .
- R1 (before any event): (usually ).
- R2 (on receive of message with timestamp ): , then R1, then deliver.
To get a *total* order from this partial one, tie-break with — timestamp first, then process ID, lex order.
Consistency, yes; strong consistency, no. Forward direction holds: if via a chain of sends/recvs/local events, the clock values strictly increase along the chain. But the converse FAILS: runs to , runs independently to . We have but the events are concurrent — no causal relationship. Scalar clocks lose that information.
Vector Clocks
Each keeps a vector of length (one entry per process).
- R1 (before any event): .
- R2 (on receive with vector ): , then R1, then deliver.
Comparison: iff componentwise AND . Otherwise — concurrent.
Strong consistency: . The -th component of 's vector clock equals the number of events at causally preceding the current event. Vector clocks capture exactly the partial order of causality.
The cost: storage per process, bytes per message. In a cluster of 10,000 nodes, that's heavy.
Singhal-Kshemkalyani Optimisation
Most of the time most components don't change. Send only the *changed* entries since last send to that recipient. Each maintains = when last sent to , and = when was last updated. On send to , include only where .
Requires FIFO channels — the recipient reconstructs the full vector incrementally; later partial-vectors arriving before earlier ones would corrupt that reconstruction.
Matrix Clocks
Each keeps an matrix.
- = 's own vector clock.
- = 's knowledge of 's knowledge of 's clock.
That's *second-order knowledge*: "what does I know about what J knows?" Useful when you need to safely discard information that's universally known.
Killer application — obsolete-message GC. Once , every process has been observed to have seen 's clock pass — so messages with timestamps are universally delivered and can be discarded from buffers. Used in replicated databases.
When To Use Which
| Clock | Storage | Consistency | Use | |---|---|---|---| | Scalar | 1 int | Consistent (forward only) | Total ordering with tie-break | | Vector | n ints | Strongly consistent | Causal order, concurrency detection | | Matrix | n² ints | Strong + 2nd-order knowledge | Obsolete-message GC, replicated DBs |
Physical Time Sync — Cristian's, Berkeley, NTP
Even with logical clocks, sometimes you need real time. Build timestamps. Security tokens. Coordinated timeouts. The four classical algorithms:
Cristian's algorithm. Client polls a single time server. RTT = . Estimated time = . Uses UTC. Single point of failure.
Berkeley algorithm. Master polls all slaves, averages their times (after correcting transit delay and discarding outliers), sends each slave its delta. No UTC — only internal agreement among nodes. Useful for LAN clusters that don't need world time.
Decentralised averaging. At fixed times, every machine broadcasts. Each averages received times. Fully decentralised; no master.
NTP — the production answer. Hierarchical strata: stratum 0 (atomic clocks, GPS) → stratum 1 (servers directly connected) → stratum 2+ (secondary). Clients exchange four timestamps with servers and compute offset + delay. Accuracy: ms WAN, sub-ms LAN. Cannot achieve perfect sync — stratum delay accumulates; different nodes may use different NTP servers.
Why Still Need Logical Clocks?
NTP gives only approximate synchronisation. Distributed mutex, snapshots, deadlock detection — these need *causal* correctness, not numeric closeness. A 1 ms clock skew between two nodes is invisible to NTP but breaks safety properties of algorithms that rely on "happened before". Logical clocks capture causality exactly, regardless of physical drift.
What You Walk In Carrying
The DS model (processors, channels, three event types). Lamport's — three rules + concurrency definition. Logical clock definition + consistency vs strong consistency. Scalar fails the converse; vector and matrix satisfy both. Scalar R1 (++own, R2 (max + R1). Vector R1 (++own), R2 (componentwise max + R1). Comparison: iff componentwise + strict. Singhal-Kshemkalyani LS / LU + FIFO requirement. Matrix interpretation as second-order knowledge + GC application. Cristian's RTT/2 + UTC. Berkeley average + no UTC. NTP stratum hierarchy + accuracy.