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=V

If 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 in state columns 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:

  1. WAF / CDN rate limits. Per-IP and per-account rate limits. Cheap and obvious; bots use rotating residential proxies to defeat.
  2. CAPTCHA. reCAPTCHA v3 (invisible) and v2 (puzzle). Bots use CAPTCHA-solving services ($0.001 per solve), so it’s mainly a friction tax.
  3. 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.
  4. 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.
  5. 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.
  6. Rate-limit by payment method. A single credit card can complete only N transactions in M minutes.
  7. Identity-locked tickets. Tickets bound to the buyer’s identity at purchase; transfer requires identity match. Reduces bot value.
  8. 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) produces TOTP₁, and a per-ticket customerKey (unique to the ticket holder) produces TOTP₂. 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-barcode call); 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

DecisionOption AOption BWhen to pick AWhen to pick B
Seat reservationPessimistic SELECT FOR UPDATEOptimistic CAS + TTL holdLow contentionHigh contention (always B for ticketing)
Queue locationOrigin (in-app)Edge (CDN-level)Small scaleMega-events; offload origin
Queue orderingStrict global FIFOBucketed FIFO + jitterSmall concurrent countMassive concurrency (B is the only viable)
Bot defenseServer-side onlyMulti-layer (WAF + behavioral + identity)Trivial eventsHot events (always B for ticketing)
PricingStatic face valueDynamic (Platinum tier)Fan-friendlyCapture surplus, controversial
Inventory shardPer-venuePer-eventReuse across showsOnsale isolation (B is the standard)
Ticket deliveryStatic PDF/barcodeRotating SafeTixLow-fraud toleranceReduce screenshot resale
ResaleNoneIntegratedPure primary-marketCapture secondary economics

13. Pitfalls

  1. 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.
  2. 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.
  3. Holds without TTL. Abandoned holds permanently freeze inventory. Always use leases.
  4. 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).
  5. Bot defenses applied after the queue. Bots front-run the queue itself; defenses must apply before queue_token issuance.
  6. Idempotency missing on POST /v1/orders. A retry duplicates the order; double charges follow. Always require idempotency_key.
  7. Best-available not randomized. If “best available” deterministically returns the same seats, simultaneous requests all collide on the same single row.
  8. Failing to pre-warm. Cold start at T=0 is a recurring outage cause. Pre-warm all caches and connection pools.
  9. 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.
  10. 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