Back to Engineering Notes

Preventing Seat Overbooking with Distributed Locks

July 2026·10 min read

Selling the same seat twice is one of the fastest ways to lose user trust.

In a carpool platform, multiple passengers may discover the same route at exactly the same time. If only one seat remains, every booking request will initially believe that seat is available.

Without proper coordination, several requests can complete successfully before the database reflects the new seat count.

This is a classic race condition.

Preventing it became one of the most critical parts of the booking system.


The Problem

Imagine two passengers pressing Book Seat within a few milliseconds of each other.

Both requests arrive at the server almost simultaneously.

Both read that one seat is available.

Both continue through validation.

If each request updates the database independently, both may succeed.

The platform now has more confirmed passengers than physical seats.

The problem isn't incorrect business logic.

It's that both requests observe the same state before either has finished modifying it.


Why Database Checks Alone Aren't Enough

A common approach is to check the remaining seat count before creating the booking.

Unfortunately, that check isn't atomic.

Between reading the seat count and updating it, another request may complete first.

Simply wrapping the operation in application code doesn't eliminate this race.

The booking process needs a mechanism that ensures only one request can modify seat availability at a time.


Locking the Booking Process

Before attempting to reserve a seat, every booking request acquires a distributed lock using Redis.

The lock is created with SET NX EX, ensuring only one request can hold ownership for a particular carpool.

Any competing request immediately knows that another booking operation is already in progress.

Rather than risking inconsistent state, it simply retries later.

Passenger Request
Acquire Redis Lock
Validate Booking
Reserve Seat
Release Lock

The lock doesn't reserve the seat itself.

It simply guarantees that only one reservation workflow executes at a time.

Decision 01
Serialize seat reservations with a distributed lock

Concurrency problems are much easier to solve when only one reservation workflow is allowed to modify shared state at any given moment.

Redis provides an efficient distributed locking mechanism without introducing additional database contention.


A Second Layer of Protection

Locks reduce concurrency, but they aren't perfect.

Network delays, process crashes, or lock expiration can still create rare edge cases.

For that reason, the booking system doesn't rely on locking alone.

Seat availability is also maintained using an atomic Redis counter.

Each successful reservation performs a DECR operation.

If the counter ever becomes negative, the reservation is immediately rolled back with a corresponding INCR, and the request fails.

This creates a second safety barrier even if something unexpected happens during the reservation workflow.

The result is a system where both coordination and seat allocation are independently protected.


Why Two Layers?

Using only a distributed lock leaves the system dependent on lock correctness.

Using only an atomic counter allows unnecessary concurrent work before discovering that no seats remain.

Combining both approaches provides stronger guarantees.

The lock minimizes contention by allowing only one reservation workflow to execute.

The atomic counter guarantees that the seat inventory itself can never become inconsistent.

Decision 02
Protect correctness with layered concurrency controls

Distributed locks coordinate requests.

Atomic operations protect shared state.

Using both mechanisms together provides stronger guarantees than relying on either technique independently.


Tradeoffs

Distributed locks introduce a small amount of latency because requests must wait for exclusive access.

However, booking seats is a correctness-critical operation.

A slight increase in response time is a far better outcome than confirming reservations that cannot actually be fulfilled.

This approach also keeps the reservation workflow deterministic, making production issues significantly easier to diagnose.


Lessons Learned

Concurrency bugs rarely appear during development.

They appear when real users interact with the system simultaneously.

Designing the reservation workflow around distributed coordination and atomic state changes ensured that every confirmed booking represented a real, available seat.

Correctness always came before throughput.

For systems managing limited inventory—whether seats, tickets, hotel rooms, or product stock—that tradeoff is almost always worthwhile.

Distributed SystemsRedisConcurrencyBackend