Time, Clocks, and Causality

LESSON

Distributed Systems Foundations

006 25 min beginner

Time, Clocks, and Causality

By the end of this lesson, you will be able to...

  • explain why a later timestamp does not prove that one event saw another.

  • trace causal order through local actions, sent messages, and received messages.

  • choose when wall time, monotonic time, or causal metadata is the right evidence.

Idea in one sentence: Distributed systems need more than "what time was it?" because many decisions depend on what information had reached an actor before it acted.

Core Insight

A team uses a private chat room called #launch.

At 10:00, an admin removes Mateo from the room. At 10:01, Mateo's phone sends a message:

"Ship it when the tests finish?"

The server receives the removal first and the message second. The message also has a client timestamp of 09:59 because Mateo's phone clock is slow.

What should the system conclude?

The naive answer is to sort by timestamp. If the message timestamp is before the removal, accept it. If it is after the removal, reject it.

That sounds simple, but it answers the wrong question. The important question is not only "what time did the phone display?" The important question is:

Had Mateo's phone seen the removal before it sent the message?

If the phone was offline, the answer may be no. The message may be concurrent with the removal: not simultaneous, but unordered by evidence.

That does not automatically decide the product policy. A secure chat may reject the message anyway because the server's current membership says Mateo is no longer allowed. But it does change the explanation. The rejection is a permission policy, not proof that Mateo intentionally sent after seeing the removal.

That distinction is the heart of distributed time. A timestamp tells you what a clock said. Causality tells you what information could have influenced a decision.

Plain meaning:

One event is causally after another when information from the first event could have reached the actor that performed the second event.

In this scenario:

Mateo's message is causally after the removal only if his phone received evidence of the removal before sending the message.

Technical name:

The relation is called happens-before. It is the ordering created by local sequence and message flow.

The Naive Idea: Sort Events By Time

Many systems start with a column like this:

event_id | actor        | action          | timestamp
---------+--------------+-----------------+----------
E1       | admin web    | remove Mateo    | 10:00:00
E2       | Mateo phone  | send message    | 09:59:40

Then they write a rule:

earlier timestamp first
later timestamp last

This is tempting because timestamps are familiar. Logs have them. Databases store them. Humans ask questions like "what happened around 10?" A timestamp gives the system a total order.

The problem is that this total order may be fiction.

Wall clocks can drift. They can be corrected forward or backward. A client can send local time, not server time. A server can record when it received an event, not when the user created it.

Even with perfectly synchronized clocks, timestamp order still would not prove influence. If the admin removed Mateo at 10:00 in Madrid and Mateo's phone sent a message at 10:01 while disconnected on a train, the later timestamp does not mean the phone knew about the removal.

So the first repair is not "buy better clocks." Better clocks help with many jobs. They do not answer every ordering question.

Three Kinds Of Time Evidence

Use different clocks for different questions.

A wall clock answers:

What human time did this machine believe it was?

Wall clocks are useful for logs, certificates, token expiration, billing windows, retention policies, and support investigations.

A monotonic clock answers:

How much time elapsed on this one machine?

Monotonic clocks are useful for local durations: timeouts, retry budgets, latency measurements, and "how long have I been waiting?" They should not jump backward when the wall clock is corrected.

Causal evidence answers:

What had this actor already seen before it acted?

It comes from local order and messages. It is the evidence a system needs when replacement, permission, merge, or conflict handling depends on what information crossed a boundary.

For the chat room, the three questions lead to different facts:

wall clock:
  admin event says 10:00
  phone event says 09:59

monotonic clock:
  chat server waited 800 ms for membership state
  phone retry loop waited 5 seconds before reconnecting

causal evidence:
  phone had not received the removal event before sending
  server had received the removal before deciding whether to accept

These facts answer different questions.

Happens-Before

The useful causal rule is small.

An event happens-before another event when one of these is true:

If neither event happens-before the other, the events are concurrent.

Concurrent does not mean "same millisecond." It means:

the system has no evidence that either event saw or influenced the other

Look at a causal chain:

admin web:
  A1 click "remove Mateo"
  A2 send removal command -------->

membership service:
                            M1 receive command
                            M2 store member version 18
                            M3 publish removal -------->

chat server:
                                                   C1 receive removal
                                                   C2 reject future Mateo messages

Here, A1 happens-before C2. The order is not proven by timestamps. It is proven by the message path:

A1 -> A2 -> M1 -> M2 -> M3 -> C1 -> C2

Now compare Mateo's offline phone:

Mateo phone:
  P1 compose message
  P2 send message when network returns -------->

membership service:
  M2 store member version 18
  M3 publish removal -------------------------->

chat server:
  C1 receive removal
  C3 receive Mateo message

The server can decide using its policy because it has both pieces of evidence. But the phone's send event is not automatically causally after the removal. There was no path from M2 to P2.

Check: If Mateo's phone received "you were removed from #launch" and then sent the message, would the send still be concurrent with the removal?

Think first, then reveal.

Answer: No. Receiving the removal creates a message path from the removal to the later send. The send is now causally after the removal because the phone had the removal in view before acting.

A Worked Trace

Let's make the evidence visible.

The system has four actors:

admin web
membership service
chat server
Mateo phone

The membership service assigns a version to room membership. The chat server uses that version when deciding whether to accept a message.

Starting state:

room #launch membership version 17:
  Mateo is a member

Mateo phone has seen:
  membership version 17

chat server has seen:
  membership version 17

Now the admin removes Mateo:

membership service:
  input:      remove Mateo from #launch
  transition: version 17 -> version 18
  output:     removal event for version 18

Mateo's phone is offline at that moment. It still has local evidence from version 17:

Mateo phone:
  seen membership version: 17
  action: sends "Ship it when the tests finish?"
  attached evidence: message based on version 17

The chat server receives both events. Here are two possible arrival orders.

Path A: removal arrives first

chat server state:
  before: version 17, Mateo allowed
  receive removal version 18
  after: version 18, Mateo removed
  receive Mateo message based on version 17
  decision: reject or hold for moderation
Path B: message arrives first

chat server state:
  before: version 17, Mateo allowed
  receive Mateo message based on version 17
  decision: accept, or mark as pending until membership catches up
  receive removal version 18
  after: version 18, Mateo removed

The naive timestamp rule tries to decide by comparing 10:00 and 09:59. The causal view asks a richer question:

What membership version had each actor seen?

That gives the system enough information to choose an honest policy.

For a strict private room, the policy might be:

Accept a message only if the chat server has not seen a removal for that user.
If the message is based on an older membership version, reject it or hold it.

For a less strict collaboration tool, the policy might be:

If the sender had not seen the removal yet, keep the message but mark it as sent before membership caught up.

The same causal evidence can support different product rules. Causality does not replace policy. It prevents the policy from pretending a timestamp proved more than it did.

We have moved from:

largest timestamp wins

to:

decide using the evidence each actor had seen

That is the important mental shift.

Logical Clocks And Version Vectors

A logical clock records ordering evidence instead of physical time.

The simplest logical clock is a counter. If all important events pass through one ordered process, each event can get the next number:

17 -> 18 -> 19

That works when one stream owns the order. Distributed systems often have more than one actor, so one counter may hide concurrency. A version vector records what each actor has seen.

In a tiny two-actor example:

membership event:
  {membership: 18, phone: 0}

phone message while offline:
  {membership: 17, phone: 1}

The phone message is behind the membership service on membership, but ahead on phone. Each side has something the other did not include. That is the shape of concurrency.

Now change one thing. The phone receives version 18 before sending:

phone receives removal:
  seen {membership: 18, phone: 0}

phone sends message:
  {membership: 18, phone: 1}

This new message includes the removal history. It is causally after the removal.

The comparison rule is:

Version vectors cost storage, complicate APIs, and can grow when there are many actors. But they make an invisible question inspectable:

does this event include the history of that event?

Check: Compare {membership: 18, phone: 0} with {membership: 17, phone: 1}. Is either one causally after the other?

Think first, then reveal.

Answer: No. The first is ahead on membership history. The second is ahead on phone history. Each has evidence the other lacks, so they are concurrent.

Trade-offs And Limits

The trade-off is that causal evidence gives better decisions, but it makes the system heavier.

It improves:

It costs:

It can still fail when the policy is unclear. A system can correctly detect concurrency and still make a bad product choice. The causal metadata tells the truth about order. It does not decide the business rule.

It also does not remove the need for physical time. Wall clocks still matter for expiration, leases, certificates, billing, retention, and incident timelines. Monotonic clocks still matter for timeouts and latency measurement.

You can see the boundary when teams argue about words like "latest," "stale," or "after." If the real question is "what had this actor seen?", a timestamp-only design is probably too weak.

A useful design test is to ask what harm comes from being wrong. If the harm is a confusing log line, wall time may be enough. If the harm is deleting work, accepting an old permission, or explaining a false audit trail, the system needs stronger ordering evidence.

Common Confusions

Confusion: Concurrent means simultaneous

Why it is tempting:

The word sounds like two things happened at exactly the same time.

Better model:

Concurrent means unordered by causal evidence. Two events can be minutes apart and still be concurrent if no information path connected them before they occurred.

Confusion: Better clock synchronization solves causality

Why it is tempting:

If all clocks are close, timestamp order feels trustworthy.

Better model:

Synchronized clocks help logs and deadlines. They do not prove what a participant had seen. A disconnected phone can act after a removal in physical time without knowing about the removal.

Practice

Choose one workflow:

profile photo update
shopping cart item addition
feature flag activation
password reset
room membership change

Fill in this review:

actors:
state being changed:
event A:
event B:
what each actor had seen before acting:
causal evidence needed:
safe policy if the events are concurrent:
safe policy if one event happens-before the other:
where wall time is still useful:

Model answer for password reset:

actors:
  user browser, auth service, email service

state being changed:
  active password reset token

event A:
  user requests a new reset token

event B:
  user clicks an older reset link

what each actor had seen before acting:
  the browser may only have the old email;
  the auth service may have issued a newer token

wall-clock fact that may be misleading:
  the old email can be opened after the new token was issued

causal evidence needed:
  which token generation the click refers to

safe policy:
  accept only the currently active token;
  invalidate older generations after a newer token is issued

where wall time is still useful:
  token expiration and support investigation

Resources

Key Takeaways

PREVIOUS Consensus, Quorums, and Coordination NEXT CAP, PACELC, and Partition-Time Behavior