Dev Skills

Clocks, ordering and why timestamps lie

Two machines never agree on the time, so never let time decide who wins.

1

What problem does logical clocks solve?

Every machine has a clock, and no two of them agree. They drift apart by milliseconds normally, and by much more when something goes wrong. NTP usually corrects small errors gradually, but it fixes a large one by stepping the clock, so a clock can even move backwards.

This matters because people constantly use timestamps to decide things: which write is newer, which event happened first, whether a lease expired. Each of those decisions is wrong some of the time, and nothing reports it.

2

What logical clocks costs you

What it costs you
Doing it properly costs you simplicity. Logical clocks give you correct ordering but no relation to real time, so you cannot use them to answer "how long ago". Version vectors grow with the number of writers. And ordering everything through one place is correct and puts a bottleneck in the middle of your system. Meanwhile the wrong-but-easy answer, wall clock timestamps, works fine until it drops somebody's data with no error.

Every concept in here states a cost. A technique with no price is a technique you have not understood yet — and naming the cost out loud is most of what separates a senior answer from a junior one.

3

When to reach for logical clocks, and when not to

Use it when

  • Deciding which of two concurrent writes wins.
  • Ordering events that arrive from different machines.
  • Anything with a lease, a lock expiry or a token lifetime.
  • Debugging across services, where you are lining up logs from several machines.

Do not, when

  • Ordering events inside a single process or a single partition. There the sequence is already unambiguous, and a counter is enough.
  • Displaying a time to a human. There a wall clock is right, because being 50 ms off does not matter.
4

How logical clocks actually works

Server Aclock 12:00:00.000Server Bclock 11:59:59.920write "red"stamped .000write "blue"stamped 11:59:59.920Storekeeps "red""blue" happened later, and the user watches their change vanish
Server B is 80 ms behind. The second write, made later in real life, carries an earlier timestamp, so last-write-wins keeps the older value and silently discards the newer one.

First, the size of the problem. A well-run fleet with NTP keeps clocks within a few milliseconds of each other most of the time. A badly configured or briefly disconnected machine can be seconds out. And when NTP steps the clock to correct a large error, the same machine can read 12:00:00.100 and then, a moment later, 12:00:00.050. Any code that assumes time only moves forward has a bug it will hit rarely and struggle to reproduce.

So: last-write-wins is a decision to lose data, not a conflict resolution strategy. It is legitimate when the value is disposable (a presence status, a cached counter), and you should say that when you choose it. It is not legitimate for anything a user typed. If two people edit a document and one edit vanishes because a server's clock was 80 ms behind, you have not resolved a conflict. You have hidden one.

Logical clocks fix ordering without needing real time. A Lamport clock is a counter each machine keeps. Bump it on every event and send it with every message. When a message arrives, set your counter to one more than the larger of yours and the received value. That guarantees that if A caused B, then A has a lower number than B. It cannot tell you the reverse. A lower number does not prove causation, so you cannot use it to detect that two writes were concurrent.

Version vectors can. Each writer keeps its own counter, and the value carried around is the set of all of them. Comparing two versions gives you three answers, not two: this one is newer, that one is newer, or neither: they are concurrent and in conflict. That third answer is the useful one, because it hands the conflict to something that can resolve it: application logic, a merge rule, or the user. The cost is that the vector grows with the number of writers, so you need a way to prune retired ones.

Hybrid logical clocks are the practical compromise used by several distributed databases, CockroachDB among them. They combine a physical timestamp with a logical counter, so the value stays close to real time and means something when you read it, while still guaranteeing correct causal order even when the physical clocks disagree. If someone asks how you would order events across regions, this is the answer to name.

The other answer is to stop needing global order. Most systems do not need to order everything, only the things that touch each other. Ordering per key, per conversation, per account is usually enough, and it is much cheaper: route all writes for one key through one place, and the sequence is unambiguous with no clocks involved at all. Partitioning by key already gives you that. Reach for that before reaching for vector clocks.

Two practical rules. Never compare timestamps from different machines to decide a winner. And never trust a client-supplied timestamp for anything that matters, because that clock is on a phone whose owner can set it to any value they like.

Common trap
  • Last-write-wins on user content, described as if it resolved the conflict rather than discarded one side.
  • Assuming time only moves forward. NTP can step clocks backwards.
  • Trusting a timestamp sent by a client.
  • Reaching for vector clocks when partitioning by key would have removed the problem entirely.
Say this
"I would not order these by wall clock, because server clocks never agree exactly and the loser is discarded silently. All writes for one conversation go through one partition, so the order is just arrival order. Where I do need cross-machine ordering I would use a hybrid logical clock, so the value still looks like real time but the causal order is correct."
5

The logical clocks follow-up an interviewer will ask

"You store a last-updated timestamp and use it to resolve conflicts. What goes wrong?"

What a strong answer sounds like

Data disappears with no error, and nobody can reproduce it. The concrete failure: two servers accept a write to the same key, one server's clock is 80 milliseconds behind, and the write that happened second carries the earlier timestamp. Last-write-wins keeps the older value and drops the newer one, so a user watches their edit apply and then revert on refresh. There is no error, no log line, nothing to alert on. It is worse in a multi-leader or multi-region setup, where the two writes are naturally on different machines. What I would do instead depends on the data. If the value is disposable, like a presence indicator, last-write-wins is fine and I would say out loud that I am choosing to lose the odd update. If it is user content, I want to detect the conflict rather than pick a winner, so I would carry a version: a version vector, or a version number with a compare-and-set write so the loser is told to retry rather than silently overwritten. Best of all, if the shape allows it, I remove the conflict: route all writes for that key through one partition, so there is a single order and no clock is involved.

6

Your turn

60-second self-check

Why is "order these events by their timestamp" unreliable across machines, and what is the cheapest way to avoid needing it?

Stuck? Take one hint at a time0 of 3 used
0 / 25 characters
7

Learn it from the source

This page gets you interview-ready. These take you to where the idea is actually specified or measured — read them when you want the real depth.

Where you will use this