Sitemap

Lamport Clocks — The System Design Series

7 min readOct 2, 2026

--

Introduction

In a distributed system, there is no single global clock.

Multiple services, servers, and processes execute independently, and their physical clocks may not be perfectly synchronized.

This creates a fundamental question:

How can we determine the order in which events happened?

Consider three services:

Service A          Service B          Service C
| | |
|--- Request ----->| |
| |--- Event ------->|
| | |

If each machine has a different physical clock, timestamps alone may not tell us the true relationship between these events.

This is where Lamport Clocks come in.

Press enter or click to view image in full size

Lamport Clocks provide a way to assign logical timestamps to events so that the ordering of causally related events can be preserved.

What Is a Lamport Clock?

A Lamport Clock, introduced by Leslie Lamport in 1978, is a logical clock used in distributed systems to establish an ordering between events.

It does not attempt to determine the actual physical time.

Instead, it answers:

Did event A happen before event B from the perspective of causality?

The key concept is the happened-before relation, commonly written as:

A → B

This means:

Event A happened before Event B

Why Do We Need Logical Clocks?

Imagine two microservices processing requests:

Service A                    Service B

Event A1 Event B1
| |
|------ Message -----------> |
| |
Event A2 Event B2

The physical timestamps might look like:

A1 = 10:00:01.100
B1 = 10:00:01.050
A2 = 10:00:01.200
B2 = 10:00:01.150

Because clocks can drift, these timestamps don’t reliably describe causality.

A logical clock solves this by maintaining an integer counter.

For example:

A1 → 1
A2 → 2
B1 → 3
B2 → 4

The numbers represent logical ordering, not real-world time.

The Happened-Before Relation

Lamport’s clock is based on the happened-before relation.

There are three important rules.

Rule 1: Events Within the Same Process

If two events occur sequentially in the same process:

A1 → A2

then:

Clock(A1) < Clock(A2)

Example:

Service A


A1 A2 A3
| | |
1 2 3

Rule 2: Sending and Receiving a Message

If one process sends a message and another process receives it:

A1 ---- Message ----> B1

then:

A1 → B1

Therefore:

Clock(A1) < Clock(B1)

The receiving process updates its logical clock based on the sender’s timestamp.

Rule 3: Transitivity

If:

A → B
B → C

then:

A → C

This gives us:

A → B → C

Therefore:

Clock(A) < Clock(B) < Clock(C)

How Lamport Clocks Work

Each process maintains its own counter.

Initially:

Clock = 0

The rules are simple.

Rule 1: Before Every Local Event

Increment the clock:

Clock = Clock + 1

Rule 2: When Sending a Message

Increment the clock and attach the timestamp:

Clock = Clock + 1

end(message, Clock)

Rule 3: When Receiving a Message

Suppose the incoming message contains timestamp T.

The receiver updates its clock:

Clock = max(Clock, T) + 1

This ensures that the receive event occurs logically after the send event.

Example

Consider two processes:

Process A                 Process B

A1 B1
| |
| |
A2 ---- Message ---------> B2
| |
A3 B3

Let’s calculate the timestamps.

Process A

Initially:

A = 0

Event A1:

A = 1

Event A2:

A = 2

A2 sends a message to B.

The message contains:

timestamp = 2

A then performs A3:

A = 3

Process B

Initially:

B = 0

B1:

B = 1

B receives the message containing:

timestamp = 2

Apply:

B = max(1, 2) + 1

Therefore:

B = 3

Then B3:

B = 4

The final ordering becomes:

A1 = 1
A2 = 2
B1 = 1
A3 = 3
B2 = 3
B3 = 4

Notice something important:

A2 → B2

and:

2 < 3

The logical clock correctly preserves the causal relationship.

Lamport Clock Algorithm

A simple implementation can look like this:

initialize clock = 0

on local event:
clock = clock + 1
on send message:
clock = clock + 1
attach clock to message
on receive message(timestamp):
clock = max(clock, timestamp) + 1

That’s essentially the entire mechanism.

The power comes from applying these rules consistently across distributed processes.

Important Property

Lamport Clocks guarantee:

If A → B
then
L(A) < L(B)

In other words:

Causal ordering is preserved by Lamport timestamps.

But there is an important limitation.

The reverse is not guaranteed.

If:

L(A) < L(B)

we cannot necessarily conclude:

A → B

A and B may be completely independent events.

Concurrent Events

Consider:

Process A                 Process B

A1 B1
| |
A2 B2

Suppose:

A2 = 2
B2 = 2

There is no message between the processes.

Therefore:

A2 ↛ B2
B2 ↛ A2

These events are concurrent.

Lamport clocks cannot directly tell us that they are concurrent.

Get Mohit Salvi’s stories in your inbox

Join Medium for free to get updates from this writer.

They only give us a consistent ordering when we need one.

Total Ordering

Sometimes distributed systems need a deterministic ordering even for concurrent events.

We can extend a Lamport timestamp with a unique process ID.

Instead of:

timestamp = 5

we use:

(timestamp, process_id)

For example:

A = (5, A)
B = (5, B)

We can compare them lexicographically:

(5, A) < (5, B)

assuming:

A < B

as process identifiers.

This creates a total order.

Lamport Clocks in Distributed Systems

Lamport Clocks are useful in many distributed-system scenarios.

1. Distributed Event Ordering

They can help establish a consistent order of events across multiple machines.

Service A → Event 10

Service B → Event 15
Service C → Event 21

A system can use these logical timestamps when processing events.

2. Distributed Databases

Logical ordering can be useful when coordinating operations across distributed database nodes.

For example:

Write A
↓
Replication
↓
Write B

Logical timestamps can help represent the ordering between operations.

3. Distributed Transactions

Distributed transactions involve operations across multiple services.

Lamport timestamps can help represent causal relationships between transaction events.

Transaction Started
↓
Service A Updated
↓
Service B Updated
↓
Transaction Completed

4. Message Queues

Message-based architectures often process events asynchronously.

For example:

Producer
|
v
Kafka
|
+---- Consumer A
|
+---- Consumer B

Logical timestamps can help reason about event relationships and processing order.

5. Distributed Debugging

Debugging distributed systems is difficult because logs are generated independently.

Instead of looking only at:

2026-10-02 10:00:01

we can associate events with logical timestamps:

Service A → Event 12
Service B → Event 13
Service C → Event 14

This can make causal relationships easier to reconstruct.

Lamport Clocks vs Physical Clocks

Physical Clock

Represents:
Actual wall-clock time
Example:
10:30:45.123

Lamport Clock
Represents:
Logical ordering
Example:
42

Physical clocks answer:

When did something happen?

Lamport clocks answer:

In what logical order did events occur?

Lamport Clocks vs Vector Clocks

Lamport Clocks and Vector Clocks solve related problems, but they provide different information.

Lamport Clock

Single number
Example:
42

A vector clock stores multiple logical counters:

[A: 5, B: 3, C: 7]

Vector clocks can determine whether two events are concurrent.

For example:

A = [3, 2]
B = [2, 3]

Neither vector dominates the other.

Therefore:

A || B

meaning they are concurrent.

Lamport clocks cannot provide this same concurrency information from timestamps alone.

Advantages

Lamport Clocks are popular because they are simple.

1. Easy to Implement

Only one integer needs to be maintained.

2. Low Overhead

Messages only need to carry a single timestamp.

3. Preserves Causality

If:

A → B

then:

L(A) < L(B)

4. Useful for Distributed Ordering

They provide a consistent logical ordering across distributed processes.

Limitations

Lamport Clocks are not perfect.

1. Cannot Detect Concurrency

If:

L(A) < L(B)

we cannot conclude that:

A → B

2. Does Not Represent Real Time

A timestamp of:

100

does not mean the event occurred 100 seconds after the system started.

It is only a logical value.

3. Does Not Capture Complete Causality

For systems where understanding concurrent operations is critical, Vector Clocks or other causal-tracking mechanisms may be more appropriate.

Real-World Example

Imagine an e-commerce system.

A customer places an order:

Order Service
|
v
Payment Service
|
v
Inventory Service
|
v
Shipping Service

Events might be:

Order Created       → 10
Payment Completed → 11
Inventory Reserved → 12
Shipment Created → 13

If another service receives an event and generates a new event, its logical timestamp is updated accordingly.

This gives the system a way to reason about:

Order Created
↓
Payment Completed
↓
Inventory Reserved
↓
Shipment Created

even when these services are running on completely different machines.

The Core Idea

The most important thing to remember is:

Lamport Clock ≠ Real Clock

It is a mechanism for reasoning about logical time.

The fundamental guarantee is:

A → B

↓

L(A) < L(B)

But:

L(A) < L(B)

does not necessarily mean:

A → B

That distinction is critical when designing distributed systems.

Lamport Clocks in One Diagram

Distributed System


Process A Process B
| |
| A1 = 1 | B1 = 1
| |
| A2 = 2 |
| |
|------ (2) ---------> |
| | B2 = 3
| |
| A3 = 3 | B3 = 4
| |
A2 → B2
2 < 3

The message carries the sender’s logical timestamp.

The receiver updates its clock using:

max(local_clock, received_timestamp) + 1

Interview Questions

What is a Lamport Clock?

A logical clock used in distributed systems to establish a causal ordering of events.

Does a Lamport Clock represent real time?

No. It represents logical ordering rather than physical time.

What property does a Lamport Clock guarantee?

If:

A → B

then:

L(A) < L(B)

Can Lamport Clocks detect concurrent events?

No. They cannot reliably determine concurrency from timestamps alone.

What is the difference between Lamport and Vector Clocks?

Lamport clocks use a single counter, while vector clocks maintain a vector of counters and can provide information about concurrency.

Why do we use max(local, received) + 1?

To ensure that the receiving event logically occurs after both the receiver’s previous event and the event represented by the incoming message.

Final Takeaway

Distributed systems don’t have a naturally shared notion of time.

Machines can have different physical clocks, network delays can reorder messages, and events can happen concurrently.

Lamport Clocks provide a simple solution by replacing physical time with logical time.

The core rule is:

If A happened before B,

then:
Lamport(A) < Lamport(B)

This simple idea became one of the foundations of reasoning about distributed systems.

And while Lamport Clocks cannot tell us everything about causality, they provide an elegant and lightweight mechanism for establishing logical event ordering.

--

--

Mohit Salvi
Mohit Salvi

Written by Mohit Salvi

Tech enthusiast building, learning, and growing one project at a time.