Lamport timestamps are a clever way to impose a consistent ordering on events happening across multiple machines that don’t have a perfectly synchronized clock.

Imagine you have three servers, node1, node2, and node3, all running an application. They need to know the order in which certain events occurred, like a user updating a record.

Here’s what it looks like in action. Let’s say node1 has a local clock C1, node2 has C2, and node3 has C3.

Initial State: node1: C1 = 0 node2: C2 = 0 node3: C3 = 0

Event 1: node1 sends a message.

  1. node1 increments its clock: C1 = 1.
  2. node1 attaches the timestamp 1 to the message.
  3. node1 sends the message to node2.

Event 2: node2 receives the message from node1.

  1. node2 receives the message with timestamp 1.
  2. node2 updates its clock to be the maximum of its current clock and the received timestamp, then increments: C2 = max(C2, 1) + 1 = max(0, 1) + 1 = 2.
  3. node2 processes the message.

Event 3: node2 sends a message.

  1. node2 increments its clock: C2 = 3.
  2. node2 attaches the timestamp 3 to the message.
  3. node2 sends the message to node3.

Event 4: node3 receives the message from node2.

  1. node3 receives the message with timestamp 3.
  2. node3 updates its clock: C3 = max(C3, 3) + 1 = max(0, 3) + 1 = 4.
  3. node3 processes the message.

Event 5: node3 has a local event.

  1. node3 increments its clock: C3 = 5.
  2. This local event now has timestamp 5.

Event 6: node3 sends a message.

  1. node3 increments its clock: C3 = 6.
  2. node3 attaches the timestamp 6 to the message.
  3. node3 sends the message to node1.

Event 7: node1 receives the message from node3.

  1. node1 receives the message with timestamp 6.
  2. node1 updates its clock: C1 = max(C1, 6) + 1 = max(1, 6) + 1 = 7.
  3. node1 processes the message.

Now, if you look at the timestamps associated with events:

  • node1’s send (Event 1): 1
  • node2’s receive (Event 2): 2
  • node2’s send (Event 3): 3
  • node3’s receive (Event 4): 4
  • node3’s local event (Event 5): 5
  • node3’s send (Event 6): 6
  • node1’s receive (Event 7): 7

This sequence of timestamps 1, 2, 3, 4, 5, 6, 7 represents a happened-before relationship. If event A has a lower timestamp than event B, it’s highly probable that A happened before B. Even though clocks are not synchronized, this system guarantees that if event A truly happened before event B (either concurrently on the same node, or because A caused B via a message), then the timestamp of A will be less than the timestamp of B.

The core problem this solves is establishing a consistent, global ordering of events in a distributed system where nodes cannot rely on a single, perfectly synchronized clock. Without this, you can’t definitively say which action truly preceded another when those actions occur on different machines. This is fundamental for tasks like distributed consensus, garbage collection, and maintaining consistent state across replicas.

The rules are simple:

  1. Local Event: When a node experiences a local event (not a message receive or send), it increments its own clock and assigns that value as the timestamp for the event.
  2. Sending a Message: Before sending a message, the node increments its clock, assigns the new value as the timestamp for the message, and sends it.
  3. Receiving a Message: When a node receives a message with timestamp T, it updates its own clock to max(its_current_clock, T) + 1. The +1 ensures the receive event’s timestamp is strictly greater than the send event’s timestamp.

The most surprising aspect of Lamport timestamps is that while they establish a total order on events, they don’t necessarily reflect the actual wall-clock time. Two events with timestamps 5 and 6 might have occurred very close together in real time, or event 5 could have happened significantly earlier than event 6 but involved a much longer processing delay or message latency. The ordering is causal, not temporal.

The next concept to explore is how to achieve a true synchronization of events, not just a causal ordering, which leads to Vector Clocks.

Want structured learning?

Take the full Distributed Systems course →