Case study
Design Ticketmaster Event Booking
Sell concert tickets fairly under extreme flash-sale load without overselling or double-booking seats.
Requirements
- Functional: browse events; select seats; hold during checkout; purchase; e-ticket
- Prevent double booking of same seat
- Queue users during high-demand onsales (1M users, 10K seats)
- Non-functional: hold TTL 10 min; purchase idempotent; 99.99% inventory accuracy
- Scale: 50K concurrent checkout sessions during mega onsale
Back-of-envelope Estimation
| Metric | Calculation | Result |
|---|---|---|
| Flash sale | 1M users, 10K seats | 100:1 oversubscription |
| Peak RPS | 1M enter queue in 1 min | ~17K/sec gate traffic |
| Seat hold writes | 50K concurrent × 4 seats avg | 200K held seats max |
| Inventory reads | Every seat map poll ~1/sec/user | CDN + cache critical |
API Design
| Endpoint | Description |
|---|---|
| GET /v1/events/{id}/seats | Seat map with availability (cached) |
| POST /v1/holds | { eventId, seatIds[] } → { holdId, expiresAt } |
| POST /v1/orders | { holdId, paymentToken, idempotencyKey } → order |
| GET /v1/queue/{eventId}/status | Virtual waiting room position |
Data Model
| Entity | Fields |
|---|---|
| seats | event_id, seat_id, section, row, status(available|held|sold), version |
| holds | hold_id, user_id, seat_ids[], expires_at, status |
| orders | order_id, hold_id, payment_id, status, created_at |
| waiting_room | event_id, user_id, queue_token, position — Redis |
High-level Design
User ──► Waiting Room (token bucket admit N/sec)
└──► Seat Map (CDN cached static layout + Redis availability overlay)
└──► Hold Service ──► optimistic lock seat rows (version column)
└──► Checkout ──► Payment ──► confirm seats SOLD (atomic)Deep Dive: Seat Hold with Optimistic Locking
UPDATE seats SET status='held', version=version+1 WHERE seat_id IN (...) AND status='available' AND version=@expected. If row count < requested, rollback and return 409. Hold expires via TTL job releasing seats.
Why not pessimistic lock
SELECT FOR UPDATE on 10K rows serializes the sale. Optimistic concurrency with version column scales; retry on conflict for alternate seats.
Deep Dive: Virtual Waiting Room
- Admit 500 users/sec from FIFO queue to prevent origin meltdown
- Queue token in cookie; random shuffle prevents bot front-running
- Bots: device fingerprint + proof-of-work challenge before queue entry
- Grace period if user refreshes — token tied to session
Failure Modes & Monitoring
| SLO | Target |
|---|---|
| Inventory accuracy | 100% — zero oversell |
| Hold expiry enforcement | < 30 sec after TTL |
| Queue admit rate | Stable under configured cap |
Reconciliation job compares sold seats vs payment records nightly. Alert on any seat with status=sold without matching order.