System Design Arena

Design Ticketmaster

Event ticketing under extreme contention: browse events, hold seats, pay — without double-selling seat 14B when a superstar tour goes on sale.

Functional RequirementsNon-Functional RequirementsAPI Endpoints

Case Study

Ticketmaster

Same conversation, diagrams, and wrap-up you expect—now framed with clearer scaffolding and iconography.

Prompt

Design a ticketing service like Ticketmaster where users browse events, select specific seats, and purchase them — including a Taylor-Swift-scale on-sale.

Interview snapshot

  • • Topic: Ticketmaster
  • • Expected depth: 45 - 60 minutes
  • • Focus areas: APIs, scale estimation, resilient architecture
  • • Wrap-up: risk, monitoring, disaster recovery

Key takeaways

  • Browse events and interactive seat maps.
  • Zero double-sells — correctness beats availability on the purchase path.
  • GET /events/{id}/seats → seat map + advisory availability bitmap.

🎙️ Interview mode

Practice this like a real interview

Don't read the answer first. Work the framework against the bare prompt, then compare against what the interviewer expected at each step.

🎯 Start guided practice (free)

Scorecard

What the interviewer is evaluating

Requirements8/10
Scale estimation7/10
API design6/10
Architecture8/10
Trade-offs9/10
Failure handling8/10

Staff-level signal

Does the candidate recognize this as a CONTENTION problem (10M people, 50K seats) and reach for holds + a waiting room instead of "more servers"?

🧠 Staff engineer judgment

Autoscaling cannot fix contention on 50K seat rows. When the resource itself is scarce, admission control (a waiting room) beats capacity every time.

Calibration

The common (bad) answer

User → LB → App servers (autoscaled ×100) → Seats table
SELECT … WHERE available=true; UPDATE …

❌ Why this scores poorly: The check-then-update race double-sells seats the moment two requests interleave — and autoscaling multiplies the racers, making it worse.

✓ What a strong answer adds

  • Atomic compare-and-set per seat: available → held(session, TTL) in one operation.
  • Holds with TTL and an explicit state machine: available → held → sold.
  • A virtual waiting room that admits users at the rate the hold path can absorb.
  • Advisory (cached) availability for browsing; transactional truth only at hold time.
  • Idempotent payment completion keyed by holdId.

Video walkthrough

System Design Interview: Design Ticketmaster

Hello Interview · ex-Meta staff engineer · 58 min

Seat reservation under contention: locking, holds and the thundering-herd problem.

Work the prompt yourself first — guided practice grades your own answer step by step. Watching someone else design it feels like progress and teaches you far less.

Interview flow

Dialogue timeline

Interviewer

What are we building?

Candidate

Event browsing and seat maps, temporary seat holds while a user checks out, and purchase. The invariant that beats everything else: a seat is never sold twice.

Interviewer

What's the scale and shape of the load?

Candidate

Browsing is ordinary read traffic. The interesting shape is the on-sale spike: 10M users contending for 50K seats in minutes. It's a contention problem wearing a scale costume.

Interviewer

How do you keep browsing fast during a spike?

Candidate

Event and seat-map data is read-heavy and cache-friendly — CDN and Redis with seat availability as a slightly-stale bitmap. Availability shown during browse is advisory; truth lives only in the hold/purchase path.

Interviewer

Walk me through selecting a seat.

Candidate

Selecting creates a hold: an atomic conditional write on the seat row — 'available → held by session X, expires in 10 min'. Postgres row-level locking or Redis with Lua both work; the key is one atomic compare-and-set, one owner.

Interviewer

Two users click seat 14B in the same millisecond.

Candidate

One CAS wins, one loses and instantly sees 'seat taken — here are neighbors'. The loser must fail fast; a queued or slow failure is what melts systems and users' trust.

Interviewer

Holds expire how?

Candidate

TTL on the hold; expiry returns the seat to the pool. Payment completion converts hold→sold in a transaction that verifies the hold still belongs to that session. Crash between payment and conversion is reconciled by an idempotent payment-callback replay.

Interviewer

The on-sale starts and 10M people arrive. What protects the seat store?

Candidate

A virtual waiting room: admit users in randomized batches with signed tokens, sized to what the hold path can absorb. Everyone else sees a queue position. It converts a stampede into a steady stream — fairness plus survival.

Interviewer

Why not just autoscale instead of a queue?

Candidate

The bottleneck is per-seat contention, not compute — 10M CAS attempts on 50K rows serialize regardless of how many app servers you add. Autoscaling multiplies the mob; the waiting room shrinks it.

Scoping

Requirements & trade-offs

Functional Requirements

  • Browse events and interactive seat maps.
  • Hold specific seats for ~10 minutes while checking out.
  • Purchase converts hold to owned ticket; holds expire back to the pool.
  • Out of scope: resale marketplace, dynamic pricing, fraud.

Non-Functional Requirements

  • Zero double-sells — correctness beats availability on the purchase path.
  • Survive 100× normal traffic during on-sales without collapsing browse.
  • Hold acquisition responds in <500ms — win or lose fast.
  • Fairness: on-sale access order shouldn't reward better bots.

Blueprint

Architecture modules

Module 1

API Endpoints

  • GET /events/{id}/seats → seat map + advisory availability bitmap.
  • POST /holds {eventId, seatIds[]} → {holdId, expiresAt} or 409 with alternatives.
  • POST /purchase {holdId, paymentToken} — idempotent by holdId.
  • Waiting room issues signed admission tokens required by /holds during on-sales.

Module 2

Back-of-the-Envelope

  • 50K seats, 10M interested → 200:1 oversubscription; most users must never reach the seat store.
  • Hold path sized at ~5K holds/sec → admit ~5K users/sec from the queue.
  • Seat state is tiny (50K rows/event) — contention, not volume, is the enemy.
  • Browse spike ~1M RPS handled almost entirely by CDN + cache.

Module 3

System Diagram Notes

  • CDN → Browse services (cached seat bitmaps) — fully separate from the transactional path.
  • Waiting room (queue + token issuer) gates the Hold service.
  • Hold service → Seat store (partitioned by event; atomic CAS per seat).
  • Payment service converts holds; callbacks are idempotent.

Module 4

Design Playbook

  • Name the invariant first: no seat sold twice — it justifies every later choice.
  • Split advisory availability (cacheable) from transactional truth (locked).
  • Holds with TTL are the heart; show the state machine available→held→sold.
  • Introduce the waiting room as a contention valve, not a scaling hack.
  • Explain why the loser of a seat race must fail in milliseconds.