Why time and ordering are hard, and when to care
On one machine, "which happened first?" is answered by the clock. Across many machines there is no shared clock. Each machine's clock drifts, and syncing them leaves errors of milliseconds or more. Messages arrive late or out of order. If a system sorts events by their wall-clock timestamps, it can put a reply before the question that caused it, or silently discard a write.
Ordering events correctly gives you two things interviewers always ask about:
- Consistency: events are ordered in a way that respects cause and effect, so effects never appear before causes.
- Correct conflict handling: the system can tell the difference between "after" and "at the same time", so concurrent writes are merged rather than lost.
Care about this when:
- Several nodes write the same data and must agree on the latest version.
- You build replication, collaborative editing, distributed logs or last-write-wins stores.
- You compare timestamps from different machines for anything that matters.
Skip it, or use something simpler, when:
- One node orders everything: a single database sequence is enough.
- Timestamps are only for display or rough sorting.
Try it
Two events on different nodes must be put in order. Change how they are stamped, then change the situation.
Time and Ordering in Distributed Systems
Step 1 of 4 · Press Space to play, arrow keys to stepInitial state. Three nodes keep their own clocks and must put events in a sensible order without a shared clock.
- tick:
- 0
- causality violations:
- 0
- concurrent events ordered by mistake:
- 0
1 tick = one step of the simulation.
Things to try:
- Run Wall-clock timestamps with Ben's clock is 5 seconds behind. The reply sorts before the write it answers.
- Switch to Lamport clocks and Vector clocks with the same skew, and note that cause and effect are respected.
- Choose Ana and Cy write at the same time. Only the vector clock can tell the writes were concurrent.
What to watch:
- The 5 second skew is exaggerated so the effect is easy to see. It is illustrative, not a typical clock error.
- A vector such as [1, 1, 0] lists one counter per node, in the order Ana, Ben, Cy.
- With Lamport clocks and concurrent writes, both writes get the same number, and the simulation breaks the tie by node name. That tie-break is an arbitrary rule, which is why the conflict goes unnoticed.
- Ben's reply counter is the larger of his own and the received one, plus one. That is why it always follows the write.
Ways to order events
| Approach | How it works | Good when | Weakness |
|---|---|---|---|
| Wall-clock time | Use each machine's clock | Display, rough sorting, logs | Clocks drift and jump, so order can be wrong |
| Logical (Lamport) clock | One counter per node, increased on each event and on each received message | A total order that respects cause and effect | Cannot tell concurrent events apart |
| Vector clock | One counter per node, kept as a list | Detecting concurrent writes and conflicts | Grows with the number of nodes |
| Hybrid logical clock | Wall time plus a counter | Timestamps close to real time that still respect causality | Slightly more complex |
| Central sequencer or single leader | One node assigns the order | Simplicity, strong order | The sequencer is a bottleneck and a single point of failure |
| Bounded-error clocks (TrueTime) | Hardware clocks with a known error, and writes wait out the error | Global strong consistency | Needs special hardware |
Happens-before and concurrency
Event A happened before B if A could have caused B: they are on the same node in that order, or A is the sending of a message and B its receipt, or the relation follows through a chain. If neither happened before the other, they are concurrent: neither knew about the other.
- Lamport clocks give each event a number so that if A happened before B then A's number is smaller. The reverse is not guaranteed, so a bigger number does not prove a cause.
- Vector clocks keep one counter per node. Comparing two vectors tells you: A before B, B before A, or concurrent. That lets a system keep both versions of concurrent writes and merge them.
How the clocks work
- Lamport clock: each node keeps a counter. Before an event it adds 1. When sending, it attaches the counter. On receiving, it sets its counter to the larger of its own and the received one, then adds 1.
- Vector clock: each node keeps a list with one slot per node. A node increments its own slot for each event. On receiving a message, it takes the larger value in each slot, then increments its own.
- Compare two vectors: if every slot of A is at most B's and some slot is smaller, A happened before B. If neither is at most the other everywhere, they are concurrent.
- Resolve conflicts: for concurrent writes, keep both (and let the application or a data type such as a CRDT merge them), rather than letting a clock decide.
- Hybrid logical clock: each node keeps its wall-clock time and a small counter. On a local event or on receiving a message, it takes the largest of its own stamp and the received stamp (and its current wall time), and increases the counter when the times tie. The stamp stays close to real time, yet it never goes backwards and always follows the message it answers.
Time and ordering vs the alternatives
| Concern | Logical or vector clocks | Wall-clock last-write-wins | Single leader orders everything |
|---|---|---|---|
| Respects cause and effect | Yes | No, when clocks skew | Yes |
| Detects concurrent writes | With vector clocks | No | Not needed |
| Works with no coordination | Yes | Yes | No |
| Can lose a write silently | No, if vector clocks keep both versions. Yes, if a Lamport clock feeds last-write-wins | Yes | No |
| Metadata size | Small to growing | None | None |
| Best for | Multi-writer replication | Caches and non-critical data | Most databases |
One caution about the "Can lose a write silently" row: it is true of vector clocks that keep both versions. A Lamport clock gives concurrent writes an arbitrary order (often by node name), so if it feeds a last-write-wins rule, one concurrent write is still dropped. Only vector clocks tell you the writes were concurrent.
For critical data prefer a single writer per item. Use logical clocks when several writers are required.
Avoiding a new single point of failure
- Central sequencer: if you use one, make it a replicated service with a leader, and hand out ranges of numbers to cut calls.
- Clock sync: run time synchronisation (NTP or a cloud time service) on every node and monitor offsets. Alert on large drift.
- Metadata growth: prune old entries from vector clocks, or use a different mechanism such as version vectors per key.
You accept extra metadata on every write and the work of merging concurrent versions.
When clocks and messages misbehave
- Clocks can step. A time sync can move a clock forwards or even backwards. Code that measures a duration should use a monotonic clock, which never goes backwards, not the wall clock.
- Leap seconds. Some systems smear a leap second over a period, and others repeat a second. Either way, two machines can disagree about the time for a while.
- Messages can be delayed, duplicated or lost. A message may arrive long after it was sent, so a logical clock only orders what it has seen. Design handlers so a repeated or late message does no harm.
- A paused process still believes its old time. After a long pause (garbage collection, a stalled virtual machine) a node may act on a stale reading of the clock, which is why leases need fencing tokens.
Common mistakes
- Comparing timestamps from different machines to decide who wrote last.
- Assuming a synced clock is exact. Sync error and clock steps still exist.
- Using last-write-wins on important data. It silently drops a concurrent write.
- Assuming a bigger Lamport number means "after". It only means "not before".
- Letting vector clocks grow without bound. Pruning old entries fixes the size, but it can lose information, so two versions may later look concurrent when one really followed the other. That costs an extra merge, not a lost write.
- Ignoring leap seconds and time zones. Store times in UTC.
How to talk about time and ordering in an interview
- Say you cannot trust wall clocks across machines, and explain drift and skew.
- Name happens-before, then offer a single leader for ordering, or Lamport and vector clocks when several nodes write.
- Mention concurrent writes, how you detect them, and how you resolve them (merge, application logic or CRDTs).
- Name the trade-off: logical clocks give correct ordering with no coordination, but they do not map to real time and add metadata.