Movie Ticket Booking
Two people staring at the same seat map, both tap seat B12 at the same instant, and only one of them should walk away with a ticket. That race is the entire reason this problem shows up in interviews - everything else is bookkeeping around it.
Requirements
Functional
- A
TheaterhasShows (a movie at a specific time in a specific screen); each show has a seat map inherited from its screen's layout. - A customer selects seats for a show and books them; a booked seat cannot be selected by anyone else.
- Seats have categories (regular, premium) and each category has its own price for a show.
- A booking can be cancelled, which frees its seats.
Non-functional
- Two customers selecting the same seat at nearly the same time must never both succeed - exactly one booking should win.
- A seat a customer is actively choosing (but hasn't paid for yet) shouldn't be immediately grabbable by someone else, but it also shouldn't be locked forever if they abandon checkout.
Design
Every Seat on a Show carries its own state - AVAILABLE, LOCKED, or BOOKED - and
every transition between those states goes through one method, Seat.tryLock /
Seat.confirm / Seat.release. Show never flips a seat's state directly; it only ever
asks the seat to do it, which is what makes "two requests, one winner" enforceable in one
place instead of scattered across every caller.
- 1Alice and Bob both request B12 within the same instant.
- 2Alice's request reaches the seat's atomic lock first and flips AVAILABLE to LOCKED.
- 3Bob's request arrives a moment later.
- 4The seat is already LOCKED, so Bob's tryLock returns false - his booking attempt fails cleanly.
- 5Alice completes payment before any timeout; her lock becomes a permanent BOOKED.
Booking is created only after every requested seat has been locked successfully - if even
one seat in the request is already taken, the whole booking attempt fails and any seats it
did manage to lock are released, so a customer never ends up holding three of the four seats
they asked for.
Class diagram
Code
Design decisions
- Seats get a three-state machine (
AVAILABLE/LOCKED/BOOKED) instead of a booleanisBooked. A boolean can't represent "someone is mid-checkout on this seat right now" - without the middle state, two customers could both see a seat as free during the few seconds between selection and payment. The extra state is what a real booking flow actually needs, not gold-plating. Seat.tryLockis a compare-and-set, not a check-then-set.if (seat.isAvailable()) seat.lock()from two threads can both pass the check before either calls lock - the classic time-of-check-to-time-of-use race.tryLockdoes the check and the state flip as one atomic step (asynchronizedmethod in Java, a lock-guarded compare in Python), so only one caller ever seestruecome back.- Locking is per-seat, not per-show. A single lock around the whole show would correctly prevent double-booking, but it would also serialize every customer browsing that show, even ones picking completely different seats. Locking at the seat level is the difference between "correct" and "correct and doesn't fall over during a popular release's on-sale minute."
- What's missing for a real system: a locked seat here has no expiry - a production
system needs a TTL (a scheduled job or a lock timestamp checked on read) that reverts
LOCKEDback toAVAILABLEif the customer never completes payment, otherwise an abandoned checkout permanently removes a seat from sale.