Clocks, timestamps and ordering events

Distributed systems Senior Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

Node a writes, sends to b; b writes, then receives. What does each node record?

Python 3.13
from dataclasses import dataclass, field


@dataclass
class Node:
    name: str
    clock: int = 0
    seen: list[str] = field(default_factory=list)

    def local(self, what: str) -> int:
        self.clock += 1
        self.seen.append(f"{what}@{self.clock}")
        return self.clock

    def send(self) -> int:
        return self.local("send")

    def receive(self, ts: int) -> None:
        self.clock = max(self.clock, ts)
        self.local("recv")


a, b = Node("a"), Node("b")
a.local("write x")
t = a.send()
b.local("write y")
b.receive(t)
print(a.seen, b.seen)

Options
Hint 1 of 1

receive: clock = max(local, received) + 1

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A ['write x@1', 'send@2'] ['write y@1', 'recv@3']

Correct. b's clock jumps to max(1, 2) and then increments, so the receive is ordered after the send.

A Lamport clock ticks on every local event. On receive it first jumps to max(own, message) and then ticks, so b's receive gets 3 and comes after a's send at 2.

Why not the others?
  • B ['write x@1', 'send@2'] ['write y@1', 'recv@2']. Receive takes the max and then adds one.
  • C ['write x@1', 'send@2'] ['write y@2', 'recv@3']. b's first local event is its first tick.
  • D ['write x@1', 'send@1'] ['write y@1', 'recv@2']. Sending is an event and increments a's clock.

30-second answer

Server clocks drift and get stepped by NTP, so a write that happened later can carry an earlier timestamp and be silently discarded. Wall-clock timestamps can't tell you which event caused which. Logical clocks can: a Lamport clock gives an order consistent with causality, and version vectors detect when two writes were concurrent so you can merge them or ask the user. For one database, let it order writes, for example with a version column.

Practise saying it · 35 s. No recording, just a timer

Why not order by timestamps?

Say your answer out loud, then check which of these points you covered:

  • Clocks drift and jump
  • LWW silently drops writes
  • Lamport: causal order
  • Version vectors detect concurrency
  • Monotonic clock for durations

Deep dive

Use a monotonic clock (time.monotonic()) for measuring durations; wall time can jump backwards.

Last-writer-wins is acceptable only when losing a concurrent update is fine.

Google Spanner uses TrueTime: bounded clock uncertainty plus waiting out the bound before commit.

Hybrid logical clocks combine wall time with a logical counter (used by CockroachDB).

Common mistakes

  • Measuring a timeout with time.time().
  • Ordering distributed events by server timestamp.
  • Assuming NTP keeps servers within a millisecond.

Senior take & follow-ups

Explain drift and LWW data loss, then logical clocks and version vectors.

Follow-up questions

Think of your answer first, then open the hint.

  1. What does a Lamport clock not tell you?

    Show hint

    Whether two events were concurrent; you need vector clocks for that.

  2. Why is a database sequence better than timestamps for order?

    Show hint

    One writer assigns it, so it's a true order for that database.

Suggest a fix if something here is wrong or out of date.