Ticketmaster / BookMyShow#
Problem statement (interviewer prompt)
Design Ticketmaster / BookMyShow: users browse events, see a seat map, hold a seat for ~7 minutes while paying, then receive a ticket. Guarantee no double-booking even during 500k-concurrent-user drops for hot shows. Add anti-bot defences.
flowchart LR
U([User])
CAT[Event Catalog]
SEAT[Seat Map Service]
HOLD[Seat Hold<br/>TTL lock]
PAY[Payment]
TKT[Ticket Issue]
U --> CAT
U --> SEAT
SEAT --> HOLD
HOLD --> PAY --> TKT
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class U client;
class CAT,SEAT,HOLD,PAY,TKT service;
flowchart TB
subgraph Clients
WEB
MOB
end
subgraph Edge
CDN
LB
WAF[WAF + Bot filter]
QUEUE[[Virtual Queue / Waiting Room]]
end
subgraph Discover
EVT[Events Catalog]
SRCH[Search]
EVDB[(Events DB)]
end
subgraph Seat[Seat selection]
MAP[Seat Map renderer]
SEATSVC[Seat Service]
SEATDB[(Seats per show<br/>strong consistency)]
HOLD[Hold Service<br/>TTL 7-10 min]
LOCK[Redis SETNX EX lock per seat]
end
subgraph Buy
CKT[Checkout]
PAYSVC[Payment]
PG((Payment Provider))
IDEM[Idempotency keys]
SAGA([Booking saga])
end
subgraph Issue
TKT[Ticket Issuer]
QR[QR / barcode]
EMAIL[Email / SMS]
WALLET[Apple/Google Wallet]
end
subgraph Anti[Anti-abuse]
CAPTCHA
BOT[Bot scoring]
LIMIT([Per-user limits])
BLACKLIST
end
subgraph After
REFUND[Refund / cancel]
RESALE[Resale / Transfer]
SCAN([Venue scanner])
end
Clients --> CDN --> LB --> WAF --> QUEUE --> Discover
Discover --> Seat --> HOLD --> LOCK
HOLD --> Buy --> Issue
Anti --- Clients
After --- Issue
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class LIMIT client;
class WAF edge;
class EVT,SRCH,MAP,SEATSVC,HOLD,CKT,PAYSVC,IDEM,TKT,QR,EMAIL,WALLET,BOT,REFUND,RESALE service;
class EVDB,SEATDB datastore;
class LOCK cache;
class QUEUE queue;
class SAGA,SCAN compute;
class PG external;
Seat reservation correctness#
- Each seat is a unique row in
seats(show_id, seat_id, state)with state machine:available → held → sold. - Hold = atomic CAS update to
heldwithheld_until = now + TTL. - Background sweeper releases expired holds.
- Selling = CAS
held → soldonly if owner_id matches and payment captured.
Waiting room#
- High-demand events route users to a virtual queue (Cloudflare-style).
- Random fair-share lottery; users enter the buy flow in waves.
- Prevents thundering herd on seat APIs.
Anti-bot#
- Heavy captcha + behavioral fingerprinting.
- Per-account / per-IP / per-payment-method limits.
- Account age and history matter for hot drops.
Glossary & fundamentals#
Concepts referenced in this design. Each row links to its canonical page; the tag column shows whether it is a high-level (HLD) or low-level (LLD) concept.
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
CDN | edge caching for static assets | cdn |
HLD |
CAP / PACELC | C vs A under partition; L vs C otherwise | cap-pacelc |
HLD |
Distributed transactions | 2PC, TCC, sagas, outbox/inbox | distributed-transactions |
HLD |
Idempotency & retries | safe re-execution, backoff + jitter | idempotency-retries |
HLD |
Search internals | inverted index, BM25, embeddings, ANN | search-internals |
LLD |
State machines | FSM, HSM, transitions, guards | state-machines |
LLD |
REST API design | verbs, statuses, pagination, errors | rest-api-design |
LLD |
Behavioural patterns | Strategy, Observer, State, Command, Chain | behavioral-patterns |
LLD |
Concurrency primitives | mutex, semaphore, RW lock, atomic, CAS | concurrency-primitives |
Quick reference#
Functional#
- Browse events, view seat map, hold, pay, get ticket.
- Wait-list, refund, resale.
- Anti-bot, fair queueing for hot drops.
Non-functional#
- Strict no-double-booking.
- Survive flash crowds (10-100× normal traffic) for marquee events.
- p99 hold response < 1 s under load.
Capacity#
- Big drop: 500k+ concurrent users in queue.
- Seat catalog: 100k+ seats for a stadium.
Schema#
events(id, venue_id, time, status)shows(id, event_id, hall, sale_start)seats(show_id, seat_id, state, held_by, held_until, version)bookings(id, user_id, show_id, seat_ids[], payment_id, status, idempotency_key)
Trade-offs#
- Strong consistency on seats non-negotiable; pay the latency.
- TTL holds prevent permanent leaks but require sweepers.
- Virtual queue is mandatory for big drops or service melts down.
- Resale complicates ticket identity → unique IDs, key rotation per transfer.
Refs#
- Ticketmaster engineering "Verified Fan" posts.
- BookMyShow architecture talks.
- ByteByteGo "Design ticketing", Alex Xu Vol 2.
FAQ#
How does Ticketmaster prevent two users from buying the same seat?#
Each seat has a row in a strongly consistent store. A short TTL hold is taken at click time using compare and set, and the hold is converted to a sold state only after payment succeeds.
How does Ticketmaster handle 500k concurrent users for a hot drop?#
A waiting room throttles entry, randomizing or queueing users into the checkout funnel. Pre-warmed caches absorb reads while the seat hold service runs on dedicated capacity.
What happens if a user's payment fails during seat hold?#
The seat hold expires automatically via a TTL or an explicit release on payment failure. The seat returns to the pool and another user can claim it via the same hold then pay flow.
How does Ticketmaster fight bot ticket scalpers?#
Defenses include captcha, device fingerprinting, behavioral signals, rate limits per account and IP, and verified fan programs. Suspected bot traffic is shadow banned or sent to slow queues.
How is the seat map rendered for large venues?#
The seat map is precomputed as static SVG or vector tiles by section. Real time availability is overlaid via a separate websocket or polled JSON channel keyed by section.