Ticketmaster Booking System Design
A high-contention ticketing platform is a system-design problem in extremis: 14 million people racing for ~50,000 seats to a single Taylor Swift concert in a 60-second window. Every architectural decision pivots on the question “how do we maintain seat-level inventory correctness, fairness, and bot resistance while serving traffic that exceeds normal load by four orders of magnitude in a planned spike?” The classical interview prompt — “Design Ticketmaster” — is taken seriously precisely because most candidates default to “use Redis” without grappling with the depth of issues: per-seat (not per-event) inventory, soft-hold leases with TTLs, virtual waiting rooms enforcing First-In-First-Out (FIFO) order, anti-bot defenses, dynamic pricing controversies, and the legal/PR tail (the November 2022 Eras Tour collapse generated a U.S. Senate hearing). This note develops the design end-to-end. StubHub and the secondary marketplace are treated as a downstream extension rather than a peer system.
1. Requirements
Functional Requirements
- Browse events. A user browses concerts/sports/theater events filtered by city, date, performer, genre.
- View venue map and seats. For a chosen event, the user sees a venue map (e.g., Madison Square Garden’s seating chart) with seats color-coded by section/price/availability. Seat availability is real-time.
- Select seats. The user clicks one or more available seats. The system places a soft hold on those seats, locking them out from other users for a short window (typically 5–10 minutes) while the user enters payment.
- Checkout. The user enters payment, the system charges, the seats become permanently theirs, and a digital ticket is issued.
- Receive ticket. Tickets delivered as email + mobile-app barcodes; modern barcodes rotate every 15 seconds (Ticketmaster’s “SafeTix”) to defeat screenshot resale.
- Virtual waiting room. For high-demand events, users land in a queue before the buying experience opens; FIFO entry order is preserved; queue position visible to user.
- Anti-bot defenses. CAPTCHAs, behavioral analysis (mouse movement, request timing), device fingerprinting, login requirement, account-history checks (“Verified Fan” pre-registration).
- Dynamic pricing for hot events. Optionally, prices fluctuate based on demand (Ticketmaster “Platinum” tier) — algorithmically priced to capture surplus.
- Resale marketplace. Tickets bought on Ticketmaster can be resold via StubHub or Ticketmaster’s own resale; original buyer’s identity is unlinked from the ticket on transfer.
- Refunds and cancellations. When events are canceled, automated mass refund. When events are rescheduled, ticket validity transfers; user can opt to refund.
Non-Functional Requirements
- Extreme write contention on hot events. A single popular concert may attract millions of concurrent buyers for tens of thousands of seats. Per-seat lock duration must be short, contention must be bounded.
- Fairness. Within a single onsale, two users who arrive within 1 ms of each other should have nearly equal probability of getting tickets — but a user who arrived 30 seconds earlier should reliably go first. This is the FIFO/queue requirement.
- Latency on the queue path. While in the queue, refresh latency 1–5 seconds is fine; the moment a user is released from the queue, the buying experience must be fast (sub-second seat-map fetch, sub-second hold, sub-second checkout).
- Availability. ≥ 99.9% normally; for an onsale event, the goal is “no degraded service during the announced window” — but in practice, Ticketmaster has degraded multiple times under such loads.
- Anti-fraud. Bots must not capture inventory at scale. Reseller arbitrage is a major business and PR concern.
- Regulatory compliance. New York, New Jersey, and several other states have anti-scalper laws; the EU has consumer-protection rules; resale platforms have separate legal regimes.
2. Capacity Estimation
For a single mega-event onsale (Taylor Swift Eras Tour scenario):
- Pre-onsale registered users (Verified Fan): ~3.5 million registered for the Eras Tour Verified Fan presale; ~1.5 million were invited to buy and ~2 million waitlisted. On presale day ~14 million people (fans plus bots) hit the site against that ~1.5 million provisioned capacity — the root of the meltdown (controversy record, 2022).
- Available tickets (across all dates and venues): ~2.4 million for the Eras Tour. Per single show: ~50K–80K seats.
- Onsale window: Typically the general onsale begins at a single timestamp; for Verified Fan, codes are released over a multi-day staggered onsale.
- Peak concurrent users at the moment of onsale: Easily 5–10 million simultaneously hitting the buying flow.
- Peak QPS for seat-availability reads: If 5M users refresh the seat map every 3 seconds = 1.6M QPS on the read path. Aggressive caching (5–10 second TTL on seat-map blocks) brings this down by 10×–100× to maybe 50K QPS at the data layer.
- Peak QPS for hold attempts: If 5M users each click “select seats” in the first minute = 80K hold attempts/second. Most of these conflict; the underlying CAS/lock fail rate is very high.
- Peak QPS for purchase (checkout): ~50K seats / 5 minutes ≈ 170 transactions/s — small if the queue paces correctly. Without pacing, all 5M attempt simultaneously and the system collapses.
- Steady-state load (non-onsale day): Tens of thousands of QPS across catalog reads and small-volume bookings. Easily handled.
- Storage: Catalog (events, venues, seat maps) is small (~hundreds of GB). Booking history at Ticketmaster scale: many billions of tickets historically — sharded relational, ~tens of TB/year.
The asymmetry is everything: steady-state is trivial; onsale-day capacity must be 1000× steady-state, scoped to specific endpoints (browse, queue, hold, buy) for specific events. Pre-warming, per-event capacity allocation, and the queue itself are the architectural responses.
3. API
GET /v1/events
Query: ?city=&genre=&date_from=&date_to=&page=
→ 200 { events: [...], next_page }
GET /v1/events/{event_id}
→ 200 { id, title, venue, datetime, status, hero_image, onsale_at,
ticket_tiers: [{tier, price_range, available_count}] }
GET /v1/events/{event_id}/seats
Query: ?section=
→ 200 { seats: [{seat_id, section, row, number, status, price}] }
// Cache TTL ~ 3-5 s during onsale
POST /v1/events/{event_id}/queue/join
Body: { user_id, captcha_token, device_fingerprint }
→ 202 { queue_position, eta_seconds, queue_token }
GET /v1/queue/{queue_token}/status
→ 200 { state: waiting | active | expired,
queue_position, eta_seconds }
// Polled every 3-10 s
POST /v1/holds
Body: { event_id, seat_ids: [...], queue_token, idempotency_key }
→ 201 { hold_id, expires_at, total }
→ 409 if any seat already held / sold
DELETE /v1/holds/{hold_id}
→ 200
POST /v1/orders
Body: { hold_id, payment_method_id, idempotency_key }
→ 201 { order_id, tickets: [{ticket_id, qr_url}], receipt_url }
GET /v1/orders/{order_id}/tickets
→ 200 { tickets: [{ticket_id, current_barcode, refresh_at}] }
// SafeTix barcodes rotate ~15 s
POST /v1/orders/{order_id}/refund
→ 200 (only for canceled/rescheduled events or within policy)
POST /v1/tickets/{ticket_id}/transfer
Body: { recipient_email }
→ 200
4. Data Model
4.1 Catalog: Events, Venues, Seats
events:
event_id, title, performer, venue_id, datetime_utc, timezone, status,
onsale_at, presale_at, hero_image_url, genre, created_at
venues:
venue_id, name, city, capacity, seat_map_url, lat, lng
seat_map_definitions:
venue_id, sections JSON, rows JSON, layout SVG/JSON
# Defines the *static* venue map: section A row 5 seat 12, etc.
event_seats: # the per-event materialization
event_id, seat_id, section, row, number, tier, base_price
PRIMARY KEY (event_id, seat_id)
4.2 Inventory: The Hot Path
seat_inventory:
event_id BIGINT
seat_id BIGINT
state ENUM (available, held, sold, blocked)
hold_id BIGINT NULL
hold_expires_at TIMESTAMP NULL
version BIGINT # for optimistic CAS
PRIMARY KEY (event_id, seat_id)
This single table is the consistency boundary. Sharded by event_id so all seats for one event live on one shard (tight locking; no cross-shard transactions).
4.3 Holds
holds:
hold_id, user_id, event_id, seat_ids JSON, expires_at, state, created_at
INDEX (expires_at) # for the expiry sweep
4.4 Orders and Tickets
orders:
order_id, user_id, hold_id, total, payment_state, idempotency_key UNIQUE,
created_at
tickets:
ticket_id, order_id, event_id, seat_id, current_owner_user_id,
delivery_state, transfer_history JSON
4.5 Queue State
queue_entries: # per (event, user)
queue_token UUID PRIMARY KEY
event_id, user_id, joined_at TIMESTAMP, position INT,
state ENUM (waiting, active, expired), released_at NULLABLE
# Or, more typically: a Redis sorted set per event keyed by joined_at,
# with the queue server reading the head and progressing it at a
# controlled rate.
5. High-Level Architecture
flowchart TB subgraph Edge DNS[GeoDNS] WAF[WAF + DDoS] CDN[CDN<br/>(static + cached seat-map blocks)] WaitRoom[Virtual Waiting Room<br/>(Cloudflare-style)] end subgraph Auth AuthSvc[Auth Service] VerifiedFan[Verified Fan Pre-Reg] end subgraph Browsing EventSvc[Event Catalog Service] EventDB[(Event DB)] end subgraph QueueTier["Queue Tier"] QSvc[Queue Service] QStore[(Redis Sorted Set per event)] Pacer[Queue Pacer] end subgraph BuyingCore["Buying Core (per-event sharded)"] SeatSvc[Seat Inventory Service] SeatStore[(Sharded by event_id<br/>RDBMS or Redis)] HoldExpiry[Hold Expiry Sweeper] OrderSvc[Order Service] OrderDB[(Sharded order DB)] end subgraph Pay PaySvc[Payment Service] PSP[(Card Networks / PSP)] end subgraph Trust BotDetect[Bot Detection] Fraud[Fraud Scoring] end subgraph Tickets TicketSvc[Ticket Issuance] Barcode[Barcode Rotation Svc] end subgraph Async Kafka[Kafka] DW[(Data Warehouse)] end DNS --> WAF WAF --> CDN WAF --> WaitRoom WaitRoom --> QSvc QSvc --> QStore Pacer --> QStore Pacer --> SeatSvc QSvc --> AuthSvc AuthSvc --> VerifiedFan CDN --> EventSvc EventSvc --> EventDB SeatSvc --> SeatStore HoldExpiry --> SeatStore SeatSvc --> OrderSvc OrderSvc --> OrderDB OrderSvc --> PaySvc PaySvc --> PSP OrderSvc --> Fraud OrderSvc --> TicketSvc TicketSvc --> Barcode SeatSvc --> Kafka OrderSvc --> Kafka Kafka --> DW BotDetect --> WAF
What this diagram shows. Three concentric tiers gate access. The outer Edge layer (DNS, WAF, CDN, virtual waiting room) absorbs the initial flood — most traffic never reaches the application. The Queue Tier (Redis sorted set + Pacer) holds users in FIFO order and admits them to the buying core at a controlled rate. The Buying Core (Seat Inventory + Order Service) handles only paced, authenticated traffic and does the actual seat reservation work. Anti-bot is a horizontal concern that informs WAF rules. The fundamental insight: the slow path (waiting room) handles the flood; the fast path (buying core) handles only what it can actually serve, and the rate is set by what the buying core can sustain.
6. Request Flow — Hot Onsale
sequenceDiagram participant U as User participant CDN as CDN / WAF participant Q as Queue Service participant Pacer as Queue Pacer participant Seat as Seat Inventory participant Order as Order participant Pay as Payment participant Ticket as Ticket Service U->>CDN: GET /events/E (pre-onsale, hero image cached) CDN-->>U: cached event page Note over U,Q: At onsale time T: U->>CDN: POST /events/E/queue/join (captcha + fingerprint) CDN->>Q: create entry Q->>Q: ZADD queue:E joined_at user_id Q-->>U: 202 {queue_token, position=2_315_432, eta=18min} loop Polling U->>Q: GET /queue/{tok}/status Q-->>U: {position=850k, eta=8min} end Note over Pacer: Every second, Pacer admits N users. Pacer->>Q: ZRANGE 0..N-1 (oldest) Pacer->>Seat: warm cache for seat map of E Q->>U: queue status → state=active U->>Seat: GET /events/E/seats (uses queue_token) Seat-->>U: seats with state=available U->>Seat: POST /holds {seat_ids=[s1,s2]} Seat->>Seat: per-seat CAS:<br/>UPDATE seat_inventory SET state='held', hold_id=H, expires=now+10m<br/>WHERE event_id=E AND seat_id IN (s1,s2) AND state='available' alt All updates succeed Seat-->>U: 201 {hold_id, expires_at} U->>Order: POST /orders {hold_id, payment_method} Order->>Pay: charge Pay->>PSP: capture PSP-->>Pay: ok Pay-->>Order: success Order->>Seat: convert hold → state='sold' Seat-->>Order: ok Order->>Ticket: issue Ticket-->>U: tickets + QR else Some seat already held (CAS failed) Seat-->>U: 409 (pick again) end Note over Seat: Hold expiry sweeper runs.<br/>Holds past expires_at → state='available'.
Reading the sequence. The Pacer is the central rate-limiting actor: it samples the queue head and admits users at a rate the Seat Inventory can handle (e.g., 1000 users/s). When admitted, the user fetches the seat map, picks seats, and the system CAS-updates per-seat rows from available → held. If the CAS fails (someone else got there first), the user re-picks. The hold has a TTL — typically 5–10 minutes — during which the user must complete checkout. A background sweeper resets expired holds back to available. Sales convert held → sold. The whole flow funnels through the queue — the un-paced flood never reaches the seat inventory.
7. Deep Dive 1 — High-Contention Seat Reservation
The single most-discussed sub-problem: how do you correctly reserve a specific seat under heavy contention?
7.1 Pessimistic Locking (SELECT ... FOR UPDATE)
A transaction locks the seat row before updating. Pros: simple, correct. Cons: lock duration includes the round-trip to the application; under heavy contention, a queue of waiters builds up at the database. For 80K hold attempts/s on a hot event, traditional row locks become a bottleneck.
7.2 Optimistic Concurrency Control (CAS)
Read the seat’s current state and version, then attempt:
UPDATE seat_inventory
SET state='held', hold_id=H, hold_expires_at=now()+'10m', version=version+1
WHERE event_id=E AND seat_id=S AND state='available' AND version=VIf 0 rows updated, someone beat us — return 409, user picks again. Pros: no lock held across user think-time; high throughput in low-conflict regime; the entire hold can be a single UPDATE. Cons: high re-pick rate when the same seat is being fought over.
7.3 Lease-Based Soft Holds (TTL)
A hold is itself a lease — the row says “I’m held by hold_id=H until expires_at.” A background sweeper resets expired holds. This is the production pattern. Why a TTL: users abandon the buying flow constantly; without expiry, abandoned holds would freeze inventory permanently. With TTL, abandoned seats are recycled within minutes.
The combined approach (used in practice):
- Per-seat row in a single shard for a given event.
- CAS-based hold acquisition: the conditional UPDATE described above.
- TTL-based expiry: a hold is valid only while
now() < expires_at. Reads that see expired holds treat the seat as available; the explicit sweeper exists mainly to surface the seat instatecolumns for indexes/queries. - Strict idempotency: the user’s checkout request includes the
hold_id; even if the user retries, only one Order is created.
7.4 Why Single-Shard
All seats for an event share a shard so the system never needs cross-shard transactions. A user holding 4 seats in section 102 + 2 seats in section 207 still hits the same DB row range — a single multi-row UPDATE in one transaction. Cross-event transactions don’t exist (you can’t atomically book seats at two different concerts as a unit).
7.5 Hot-Seat Mitigations
A handful of seats (front row, exact center) get hammered while peripheral seats sit idle. Standard mitigations:
- Optimistic UI assignment. When the user clicks “best available,” the system recommends a seat from a randomly-shuffled set of remaining good seats, dampening the dogpile on any single front-row seat.
- Assigned seating in groups. “Best 4 together” → one transaction picks any contiguous 4 still available.
- Wave-based onsales. Sometimes only some sections open at first, others released later, smoothing demand.
8. Deep Dive 2 — Virtual Waiting Room and Fairness
The virtual waiting room is the single most important architectural gating mechanism for high-contention events.
Mechanics. When a user clicks “buy tickets,” they receive a queue_token (a signed JWT) and a position in the FIFO queue for that event. They are shown an ETA and can poll for status. A Pacer at the front of the queue admits N users per second to the buying experience. N is set to what the buying core can sustain (e.g., 1000/s).
Why it works:
- Backpressure to the user level. The flood is held in cheap state (Redis sorted set keyed by joined_at) rather than slamming the inventory database.
- Determinism. A user who joined the queue 30 s after another is reliably served 30 s later, not arbitrarily depending on retry luck.
- Bot resistance. The queue server can apply behavioral checks (request rate, captcha, account age) before issuing a queue_token.
FIFO requirement nuances. Strict FIFO is impossible across distributed nodes — clock skew + network jitter make global ordering ambiguous at the millisecond scale. Practical waiting rooms use logical FIFO with bucketed timestamps (1-second resolution) and randomization within each bucket. Users care about “I joined at 10:00:01 and got in before someone who joined at 10:00:30” — that property is achievable.
Cloudflare Waiting Room (overview, queueing methods) implements this pattern on Cloudflare’s CDN edge — the origin only sees admitted traffic. Its design is instructive because the docs are explicit about the ordering trade-offs, offering four queueing methods:
- FIFO — orders visitors by arrival. The mechanism is a cookie containing a timestamp of when the user’s request first hit the actively-queueing room; Cloudflare sequences users by that timestamp and uses it to estimate wait. This is the “reward whoever waited longest” mode and is what most ticketing onsales want.
- Random — when a slot frees, a random waiting user is admitted. Cloudflare frames this as more equitable (good for limited-quantity drops where you don’t want pure speed-of-arrival to decide everything); earlier arrivals still get more selection chances before their ETA expires.
- Passthrough — admit everyone immediately (used off-peak, or just to gather traffic analytics without queueing).
- Reject — serve a static page and admit no one (maintenance / event-only endpoints).
This four-way menu maps directly onto the fairness debate below: pure FIFO rewards fast networks and bots-that-queue-early, while Random dilutes that advantage. The room “opens up new spots more quickly by tracking dynamic inflow and outflow” — i.e., the admit rate is adaptive to how fast admitted users leave the origin, not a fixed constant.
Ticketmaster’s “Verified Fan.” A pre-registration system that gates the queue itself. Users register days/weeks in advance with identity proof; only registered users get a code that lets them join the queue when onsale opens. This shifts the problem from “handle 14M concurrent” to “handle the much smaller subset of pre-registered Verified Fan users.” It is also the most controversial because the sorting of who counts as a Fan involves opaque scoring.
9. Deep Dive 3 — Anti-Bot and Fairness
Bots are the existential threat to Ticketmaster fairness. A bot can complete the buy flow in ~500 ms; a human takes 30+ seconds. Without defenses, bots capture all good inventory and resell on secondary markets at 5–20× face value.
Defense layers:
- WAF / CDN rate limits. Per-IP and per-account rate limits. Cheap and obvious; bots use rotating residential proxies to defeat.
- CAPTCHA. reCAPTCHA v3 (invisible) and v2 (puzzle). Bots use CAPTCHA-solving services ($0.001 per solve), so it’s mainly a friction tax.
- Device fingerprinting. Browser-fingerprint hash combined with user-agent, screen size, fonts, etc. Cookies. Bots run headless browsers and patch fingerprints, but at scale fingerprint heterogeneity is hard to fake.
- Behavioral analysis. Mouse movement entropy, time-between-keystrokes, scroll patterns, time-on-page. Trained ML models distinguish human vs bot at very high accuracy on naive bots; sophisticated bots replay recorded human traces.
- Login required. Anonymous buys are disabled for high-demand events; only authenticated users with verified email/phone can queue. Account age and history factor into bot scoring.
- Rate-limit by payment method. A single credit card can complete only N transactions in M minutes.
- Identity-locked tickets. Tickets bound to the buyer’s identity at purchase; transfer requires identity match. Reduces bot value.
- Verified Fan pre-registration. As above, the strongest gate.
Even with all these, bots get tickets. The arms race is permanent. Ticketmaster has been criticized publicly for inadequate enforcement. The often-quoted “3.5 billion” figure from the November 2022 Eras Tour onsale is widely misreported as “3.5 billion bot requests” — it is not. The accurate figures, from the Senate Judiciary Committee record and contemporaneous reporting (Wikipedia summary): the site received 3.5 billion total system requests that day — four times its previous peak — driven by a mix of legitimate fans and bots. Live Nation president/CFO Joe Berchtold testified that Ticketmaster faced “three times the amount of bot traffic than we had ever experienced,” with attackers specifically targeting the Verified Fan access-code servers. So the precise claim is: 3.5 billion total requests, and 3× the prior bot-traffic record — not 3.5 billion bot requests.
9.1 SafeTix — How the Rotating Barcode Actually Works
The anti-resale endgame is the ticket itself. Ticketmaster’s SafeTix replaces the static QR/PDF417 code (which is trivially screenshotted, copied, and resold or duplicated) with a barcode that changes every few seconds and can only be presented from the legitimate account’s logged-in mobile app. Ticketmaster’s help documentation states the barcode “automatically refreshes every 15 seconds.” Independent reverse-engineering by conduition (2024) explains the mechanism, which is more interesting than the marketing implies:
- Format. The on-screen image is a PDF417 two-dimensional barcode (the same family used on driver’s licenses), encoding UTF-8 text of the shape
[base64 bearer token]::[TOTP₁]::[TOTP₂]::[unix timestamp]. - Two rotating codes, not one. The two six-digit codes are TOTPs — Time-based One-Time Passwords, the same RFC 6238 construction as an authenticator app — each generated with a 15-second time step. Crucially they are derived from two different secrets: an
eventKey(shared across all tickets to that event) producesTOTP₁, and a per-ticketcustomerKey(unique to the ticket holder) producesTOTP₂. The dual-secret design lets a venue scanner verify both “this is a valid ticket for this event” and “this is this specific ticket,” and lets Ticketmaster invalidate one ticket without rotating the whole event. - Secret delivery. The two secrets are delivered to the phone inside the API response (the conduition analysis names a
/api/render-ticket/secure-barcodecall); the app then computes the rotating TOTPs locally, so the barcode keeps refreshing even with flaky venue connectivity. The bearer token itself is comparatively long-lived (the analysis observed ~20 hours). - Why this defeats screenshots. A screenshot freezes one 15-second TOTP window; by the time it is forwarded and presented, the code has rotated and fails validation. Possessing a valid live code requires the app, which requires being logged into the owning account. The visible animated “sweep” bar is purely cosmetic CSS — it provides no security and is not what the scanner reads.
The honest caveat: SafeTix raises the cost of casual screenshot resale, but it does not stop a determined reseller who hands over account credentials or uses a relay; it is friction, not a cryptographic impossibility proof. It also pushes users into Ticketmaster’s app and its official transfer flow — which is, not coincidentally, where Ticketmaster captures secondary-market economics.
10. Scaling
10.1 Per-Event Sharding
The single most important scaling axis: each event’s inventory lives on one shard. Capacity is provisioned per event, not per service. A mega-event (Eras Tour) gets a dedicated shard cluster with massively over-provisioned capacity for the onsale day, then scaled down post-onsale.
10.2 Pre-Warming
Before an announced onsale, the system pre-warms:
- Redis caches with the seat map of the event.
- CDN cache for venue maps and event hero images.
- Database connection pools at the buying core.
- The waiting-room sorted-set for the event.
- Inference servers (anti-bot ML models).
Failure to pre-warm produces a cold-start cliff at T=0 and is a documented cause of past Ticketmaster outages.
10.3 Geo-Distribution
Event catalogs are globally replicated for read; the buying core is regionally pinned (a U.S. show’s onsale serves from U.S. infrastructure). For global events (Olympics, World Cup), per-region quotas pre-allocate inventory to avoid cross-region transactions.
10.4 Service-Level Sharding
Within the queue tier, queues are sharded by event_id via Consistent Hashing across queue-server instances. Each queue instance owns its events’ Redis sorted sets and Pacer logic. This isolates a hot event’s queue from the rest.
10.5 Read Caching for Seat Map
The “show me the seat map” read is HUGE in QPS. Aggressive caching: seats are grouped into spatial blocks (e.g., a section of 100 seats), each block is a CDN-cacheable JSON document with a 3–5 second TTL. The user’s UI polls for fresh blocks. For 5M concurrent users, this is the only feasible approach — direct DB reads at that QPS are infeasible.
10.6 Async Pipeline
Booking events stream to Kafka for analytics, fraud scoring, accounting, and email/SMS notifications. The hot path doesn’t depend on these.
11. Real-World Example
Ticketmaster
- Live Nation Entertainment (parent) operates Ticketmaster as the dominant primary ticket platform in the U.S.
- Eras Tour Verified Fan presale, Nov 15 2022. Per the controversy record: 3.5 million fans registered for Verified Fan (the largest in platform history); 1.5 million were invited to buy and 2 million were waitlisted. Ticketmaster says it provisioned for the ~1.5 million invited buyers, but roughly 14 million people (fans plus bots) hit the site, and 12 million unique entities visited that day. The site logged 3.5 billion total system requests — four times its previous peak — and degraded for hours with “code error” messages, queue time-outs, and aborted checkouts. The general public on-sale was canceled on Nov 17 2022 citing “extraordinarily high demands on ticketing systems.”
- Senate hearing, Jan 24 2023. The U.S. Senate Judiciary Committee held “That’s the Ticket: Promoting Competition and Protecting Consumers in Live Entertainment,” questioning Live Nation’s Joe Berchtold, who blamed “industrial-scale ticket scalping” and an “unprecedented number of bots” — testifying to 3× the prior bot-traffic record (not, as often misquoted, 3.5 billion bot requests).
- SafeTix rotating-barcode mobile tickets. Per Ticketmaster’s own help docs, the barcode “automatically refreshes every 15 seconds.” Independent reverse-engineering reveals the real mechanism (detailed in §9.1 below): two time-based one-time-password (TOTP) codes embedded in a PDF417 barcode. The visible blue “sweep” animation is cosmetic; the security is in the rotating TOTPs.
- Verified Fan. Pre-registration program; registrants who “look like real fans” get codes to access the onsale. Scoring is opaque.
StubHub (secondary marketplace)
- Aggregates listings from sellers (mostly individuals, some brokers). The “inventory” is sellers’ offers; the “matching” is listing search + bidding.
- Ticketmaster also operates its own resale market, integrated with the original ticket so the resale is identity-tracked.
Cloudflare Waiting Room
- General-purpose virtual waiting room offering that runs on Cloudflare’s CDN edge, used by many large customers including ticketing companies. Per the queueing-methods docs it supports FIFO (cookie-timestamp ordering), Random, Passthrough, and Reject — so a deployment can choose between “reward early arrivals” and “equitable random draw” (see §8).
Uncertain
Verify: the internal technology choices for Ticketmaster’s
seat_inventory, queue, and order tiers (data stores, queue implementation, edge vendor). Reason: Ticketmaster’s former engineering blog (tech.ticketmaster.com) now redirects to a marketing site, so there is no current primary engineering source; the production stack is genuinely undocumented post-2018. The behavioral facts above — SafeTix 15-second rotation, the Eras Tour figures, Verified Fan mechanics, Cloudflare-style waiting rooms — are sourced and verified; only the named internal infrastructure is conjecture. To resolve: a current Ticketmaster/Live Nation engineering talk or post-mortem. uncertain
12. Tradeoffs
| Decision | Option A | Option B | When to pick A | When to pick B |
|---|---|---|---|---|
| Seat reservation | Pessimistic SELECT FOR UPDATE | Optimistic CAS + TTL hold | Low contention | High contention (always B for ticketing) |
| Queue location | Origin (in-app) | Edge (CDN-level) | Small scale | Mega-events; offload origin |
| Queue ordering | Strict global FIFO | Bucketed FIFO + jitter | Small concurrent count | Massive concurrency (B is the only viable) |
| Bot defense | Server-side only | Multi-layer (WAF + behavioral + identity) | Trivial events | Hot events (always B for ticketing) |
| Pricing | Static face value | Dynamic (Platinum tier) | Fan-friendly | Capture surplus, controversial |
| Inventory shard | Per-venue | Per-event | Reuse across shows | Onsale isolation (B is the standard) |
| Ticket delivery | Static PDF/barcode | Rotating SafeTix | Low-fraud tolerance | Reduce screenshot resale |
| Resale | None | Integrated | Pure primary-market | Capture secondary economics |
13. Pitfalls
- No queue, just rate limiting. Rate limits drop excess requests indiscriminately, producing a frustrating “try again” experience and unfair winners. A queue gives users an ETA and preserves order.
- Queue at origin during DDoS-scale traffic. A queue server behind your application stack still gets crushed by 14M concurrent. The queue must be at the edge.
- Holds without TTL. Abandoned holds permanently freeze inventory. Always use leases.
- Sweeper running too slowly. If holds expire but the sweeper takes 5 minutes to clear the row state, users see “available” seats they can’t actually buy. Sweep aggressively (every 1–2 s).
- Bot defenses applied after the queue. Bots front-run the queue itself; defenses must apply before queue_token issuance.
- Idempotency missing on POST /v1/orders. A retry duplicates the order; double charges follow. Always require idempotency_key.
- Best-available not randomized. If “best available” deterministically returns the same seats, simultaneous requests all collide on the same single row.
- Failing to pre-warm. Cold start at T=0 is a recurring outage cause. Pre-warm all caches and connection pools.
- Letting one event’s onsale affect others. Per-event sharding isolates the blast radius. Without it, an Eras Tour onsale takes down ticket sales for every other event.
- No graceful degradation. When the system can’t sustain demand, it must communicate clearly (“you are in queue, eta 1h”) rather than displaying generic 5xx pages — the Eras Tour incident’s most-criticized failure was the user-experience handling of degradation.
14. Common Interview Variants
- “Design Ticketmaster.” Canonical prompt; expected to cover queue, hold-with-TTL, anti-bot, and per-event sharding.
- “Design StubHub / a ticket resale marketplace.” Different inventory model — listings, not seats; auction or fixed-price; identity transfer.
- “Design a flash-sale system (Black Friday).” Same shape — virtual waiting room + per-product inventory + anti-bot.
- “Design a vaccine appointment booking system (COVID).” Same shape with a stronger fairness requirement (essentially identical to ticketing for 2021’s vaccine rollouts).
- “Design a movie ticket system.” Lower stakes; lighter on the queue, heavier on the seat map UI.
- “Design a stock-trading order book.” Different optimization (latency-critical, per-microsecond) but shares the high-contention reservation theme.
15. Open Questions
- What is Ticketmaster’s actual production data store for
seat_inventory? Industry talks variously suggest Cassandra, PostgreSQL with sharding, or in-memory grids; not clearly documented post-2018. - How does Ticketmaster’s Verified Fan scoring weight purchase history, social media activity, and identity proof? The scoring is opaque but claims to filter bots.
- What is the actual fraction of Eras Tour tickets that ended up on resale markets (StubHub, Vivid)? Live Nation’s stated percentages have been disputed in Senate testimony.
- Is a true “global FIFO” queue across 14M concurrent users theoretically achievable, or is bucketed-with-jitter the limit? Different engineering takes exist.
16. See Also
- Major System Designs MOC
- SWE Interview Preparation MOC
- Token Bucket — rate limiting at WAF and per-account
- Consistent Hashing — queue-server sharding by event_id
- Two-Phase Commit — single-shard avoids it; the alternative
- Distributed Lock Service System Design — TTL-based leases analog
- Airbnb Booking System Design — sibling reservation system, lower contention
- Distributed Log System Design — Kafka for async events
- Notification Service System Design
- Content Delivery Network System Design — edge waiting room
- Fraud Detection Pipeline System Design — bot scoring and account fraud