Ticket booking
Design a system for booking seats at events. Seats are specific and numbered. Popular events sell out in under a minute, with far more people trying than there are seats. Nobody may ever end up with a seat somebody else also has.
Where’s the slack?
Browsing: minutes. Holding a seat: minutes. The commit: zero.Almost everything here has generous slack. One operation has none at all, and that operation is the problem.
Browsing has minutes of slack. The event listing, the seat map and the price can all be cached hard and served stale. They must be, because at on-sale time a hundred thousand people load that page in ten seconds. A seat map that is five seconds out of date is fine; someone clicks a seat that was just taken and is told so at the next step.
The hold has minutes of slack by design. You give the user 10 minutes to enter card details, and that window is itself a decision about how much inventory you are willing to make unavailable to other people.
The commit has zero slack and zero tolerance for being wrong. At the instant a seat is assigned, the decision must be exact, serialised, and durable. No eventual consistency, no cache, no approximation. If two commits succeed for one seat, you have sold something twice and no amount of speed makes up for it.
Everything after (the confirmation email, the ticket PDF, the analytics, the reconciliation with the venue) has minutes or hours of slack and belongs on a queue.
So: The slack profile splits the system cleanly in two: a massively cacheable read tier that can be stale, and a small, strictly serialised commit path that cannot. The skill is keeping the second one small. Weak designs let the strict path grow.
Work it through
Five stages, gated. You write your answer for each stage before its model answer unlocks. That is the core of the method.
Reference material for this problem. The diagrams below are here to check your own against once you have drawn it, not to read first.
The lifecycle
The numbers
Only 50,000 writes will ever succeed, because there are only 50,000 seats. So the hard part is not write throughput. It is rejecting the ~90% of attempts that must fail, cheaply, before they reach the database.
The architecture
The mechanism
The decision this turns on
- —One statement: set held where still available. Atomic, no gap between check and write.
- —The database is already a single point of serialisation — use it.
- —No lease to expire, so no scenario where two holders both believe they won.
- —Write ceiling per partition, which the seat count guarantees you never reach.
- —Familiar pattern, and the right tool when there is no shared serialisation point.
- —Adds a component that can fail, on the most correctness-critical path you have.
- —A paused process can lose its lease without noticing — two holders, one seat.
- —Needs fencing tokens to be safe, which is more machinery than the alternative.
Follow-ups you should expect
Many interviews are decided in the follow-ups, not the first diagram.
Same shape, other products
A fixed number of things and more people who want them than there are things.
If two people can end up with the same one, you have failed, no matter how fast the system is. Caching the availability number is the classic wrong turn.
Others with this shape: Ticket booking · Hotel booking · Flash sale · Seat reservation
How it is really built
Write your own answer first. Then read how teams who run this at scale describe it. Their constraints were real, and the write-ups often say what their choices cost.
- PostgreSQL — Transaction Isolation ↗The isolation levels that decide whether you can sell the same seat twice.