About Us Contact Us Write for Us Advertise
Home > System Design > High-Level Design > Design Ticketmaster / BookMyShow — System Design
System Design › High-Level Design

Design Ticketmaster / BookMyShow — System Design

Design a ticket booking system where the core challenge is concurrency: no double-booking under a flash-sale stampede. The reservation pattern, pessimistic vs optimistic locking, strong consistency, and virtual waiting rooms.

Shiv Pandey
Shiv Pandey
Oct 14, 2026 | 44 views
Design Ticketmaster / BookMyShow — System Design

🔴 High-Level Design · Super-senior · the concurrency deep dive

"Design Ticketmaster / BookMyShow" — a system to book seats for concerts, movies, and matches. On the surface it's a CRUD app. But it hides one of the hardest problems in system design: when 50,000 people try to grab the same 100 seats in the same second, how do you sell each seat to exactly one person? That's a concurrency problem, and handling it is what this interview is about. Let's design it.

The whole design in one line: let users browse events, then reserve a seat with a short-lived lock so no one else can take it while they pay — using strong consistency and locking so a seat is never double-booked, even under a flash-sale stampede.

Step 1 — Requirements

  • Functional: browse events and available seats; book specific seats; pay; get a confirmation. Seats must never be double-sold.
  • Non-functional: strong consistency for seats (correctness is non-negotiable — one seat, one owner), handle extreme spikes (a popular show sells out in seconds), and stay available. This is a read-heavy browse workload with a fiercely write-contended booking moment.

Step 2 — The heart of it: no double-booking

🔴 The whole design revolves around one invariant: a seat is sold to exactly one person. The naive "check if available, then mark booked" has a fatal race condition — two users both read "available" and both book. You need a proper concurrency-control mechanism. There are two classic approaches:

Approach How Best when
Pessimistic locking Lock the seat row (SELECT … FOR UPDATE) before booking; others wait/fail High contention (popular shows) — no wasted work
Optimistic locking Don't lock; at commit, check a version/status didn't change — retry if it did Low contention — cheaper, more concurrent

Either way, the seat update is an ACID transaction with strong consistency — this is exactly the case where you do not use eventual consistency. Naming pessimistic-vs-optimistic locking is the core of a strong answer.

Step 3 — The reservation pattern (locks + a timer)

🔴 Booking isn't instant — the user needs time to pay. So you don't go straight from "available" to "booked"; there's a middle state: reserved.

AVAILABLEanyone can pick RESERVEDlocked ~10 min BOOKEDpaid, final select seat pay ✓ timer expires / pay fails → released

When you select a seat it becomes reserved for a few minutes (a lock with a TTL). Others see it as unavailable. If you pay in time → booked. If the timer expires or payment fails → it's released back to available. This reservation-with-expiry is the elegant pattern that gives users time to pay without letting a seat be held forever — and it prevents inventory from being locked up by abandoned carts.

Step 4 — Surviving the flash-sale stampede

🔴 When tickets for a huge show drop, millions hit "buy" simultaneously — a thundering herd that can melt the database with lock contention. Two senior mitigations:

  • Virtual waiting room / queue: put incoming users in a queue and admit them to the booking page in controlled batches. This smooths the spike into a rate the booking system can actually handle — exactly what real ticketing sites do ("You're number 12,000 in line").
  • Cache the read path: browsing events/seat maps is read-heavy, so serve it from a cache/CDN. Only the actual reserve/book action hits the strongly-consistent database. Separating the hot read path from the contended write path is key.

Step 5 — Architecture & data

events:  event_id, name, venue, time
seats:   seat_id, event_id, status(AVAILABLE|RESERVED|BOOKED),
         reserved_until, user_id, version   ← version for optimistic lock
bookings: booking_id, user_id, seat_ids, payment_status

Services: a Booking Service (owns the reserve→book transaction with locking), a Payment Service (see payment design — idempotent!), and browse served from cache. A background job releases expired reservations. The seat inventory lives in a relational DB precisely because it needs ACID transactions and strong consistency.

The interview-ready walkthrough

"The whole problem is no double-booking under high concurrency. I use a reservation pattern: selecting a seat moves it AVAILABLE → RESERVED (a lock with a ~10-minute TTL) → BOOKED on payment, releasing it if the timer expires. The reserve step is an ACID transaction with locking — pessimistic (SELECT FOR UPDATE) for hot shows, optimistic for quieter ones — on a strongly-consistent DB. For flash sales I add a virtual waiting-room queue to throttle the stampede and serve the browse path from cache/CDN, so only real booking actions hit the contended DB. Payment is idempotent." That leads with concurrency control — exactly the crux the question is testing."

What to read next

← Design Dropbox · On to Low-Level Design →

Related Articles

Design Dropbox / Google Drive (File Sync) — System Design
System Design › High-Level Design

Design Dropbox / Google Drive (File Sync) — System Design

Design a Payment System (Stripe) — System Design
System Design › High-Level Design

Design a Payment System (Stripe) — System Design

Design Uber / Lyft (Ride-Hailing) — System Design
System Design › High-Level Design

Design Uber / Lyft (Ride-Hailing) — System Design

Design YouTube / Netflix (Video Streaming) — System Design
System Design › High-Level Design

Design YouTube / Netflix (Video Streaming) — System Design