Lesson 10 / 25
Seat Booking With Concurrency
Holding and confirming seats safely.
Hold, pay, confirm
In a movie or event booking system (a library system has a similar shape with copies instead of seats), the core risk is double booking when two users pick the same seat. Model Show -> Seat with status AVAILABLE, HELD or BOOKED, and a Booking for a user. Flow: the user selects seats, the service places a temporary hold with an expiry (for example, an assumed 5-10 minutes), the user pays, and the booking is confirmed. Holds must be atomic across all requested seats: either all are held or none. In a single process, lock per show; in a database, use a conditional update (UPDATE ... WHERE status = 'AVAILABLE') or row locks, and release expired holds with a background sweep or on read.
Concurrency and data-structure designs
These problems add concurrency and algorithmic design to object modelling.
All-or-nothing hold
Java sketch with a lock per show.
enum SeatStatus { AVAILABLE, HELD, BOOKED }
class Seat {
final String id; SeatStatus status = SeatStatus.AVAILABLE;
String heldBy; java.time.Instant holdExpiry;
Seat(String id) { this.id = id; }
}
class Show {
private final java.util.Map<String, Seat> seats = new java.util.HashMap<>();
private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock();
boolean hold(java.util.List<String> seatIds, String userId, java.time.Duration ttl) {
lock.lock();
try {
java.time.Instant now = java.time.Instant.now();
for (String id : seatIds) {
Seat s = seats.get(id);
boolean expired = s.status == SeatStatus.HELD && s.holdExpiry.isBefore(now);
if (s.status != SeatStatus.AVAILABLE && !expired) return false; // nothing changed yet
}
for (String id : seatIds) {
Seat s = seats.get(id);
s.status = SeatStatus.HELD; s.heldBy = userId; s.holdExpiry = now.plus(ttl);
}
return true;
} finally {
lock.unlock();
}
}
}
// Database equivalent per seat (all inside one transaction):
// UPDATE seat SET status = 'HELD', held_by = ?, hold_expiry = ?
// WHERE show_id = ? AND seat_id = ? AND (status = 'AVAILABLE' OR hold_expiry < now())Check then act inside the lock
Checking availability and updating status must happen in the same critical section or transaction; otherwise two users can both see a seat as free.
Quick check: Why use a temporary hold with an expiry?
- So seats are reserved during payment but released if the user abandons
- To make payment optional
- To avoid needing a database
- To let two users share a seat
Answer
So seats are reserved during payment but released if the user abandons — Holds protect the payment window without locking seats forever.