# System Design Interview: Design a Ticket Booking System (Ticketmaster)

Source: https://www.techinterview.org/post/3233461983/system-design-ticket-booking/
Updated: 2026-04-16 · techinterview.org

## System Design: Design a Ticket Booking System (Ticketmaster/Fandango)

A ticket booking system is a classic system design problem involving seat inventory management, race conditions, payment processing, and fairness under extremely high traffic spikes (viral event on-sale moments).

## Requirements

**Functional:** browse events, search by location/date/artist, view seat map, reserve seats (hold temporarily), complete purchase, receive tickets, cancel/refund.

**Non-functional:** strong consistency for seat allocation (no double-booking), low latency for search, handle spikes (Taylor Swift on-sale: millions of requests in seconds), support 10M+ events, 1B+ tickets sold.

## Core Challenge: Preventing Double-Booking

Two users cannot purchase the same seat. This requires careful concurrency control.

### Option 1: Optimistic Locking

Each seat row has a version number. Read seat + version, attempt update with WHERE version=old_version. If another transaction committed first, version mismatch → retry.


```
-- Reserve seat atomically
UPDATE seats
SET status = 'held', held_by = :user_id, held_until = NOW() + INTERVAL '10 minutes', version = version + 1
WHERE seat_id = :seat_id AND status = 'available' AND version = :expected_version;
-- If 0 rows updated: seat taken, show error
```


### Option 2: Redis Distributed Lock

Use Redis SET NX EX to acquire a lock per seat. Only the process holding the lock can reserve the seat. Release after committing to DB.


```
lock_key = f"seat_lock:{seat_id}"
acquired = redis.set(lock_key, user_id, nx=True, ex=30)  # 30s timeout
if not acquired:
    return "Seat already being reserved"
try:
    # reserve in DB
    db.execute("UPDATE seats SET status='held'... WHERE seat_id=?", seat_id)
finally:
    redis.delete(lock_key)
```


## Seat Hold Flow


```
User clicks seat
        │
        ▼
   Hold Request
        │
  ┌─────▼────────────────────────────────────┐
  │ Redis: SET seat:{id}:held {user_id} EX 600│  (10-min hold)
  └─────┬────────────────────────────────────┘
        │ success
        ▼
  Checkout timer starts (10 min countdown)
        │
  User completes payment
        │
        ▼
  Payment Service (Stripe/Braintree)
        │ success
        ▼
  Mark seat SOLD in PostgreSQL
  Send confirmation + e-ticket (email/push)
        │
  If timer expires without payment:
        ▼
  Release hold → seat becomes available again
```


## Handling Traffic Spikes (On-Sale Moments)

When a major event goes on sale, millions of users hit the system simultaneously. Solutions:

- **Virtual waiting room**: queue users on entry, issue time-stamped tokens, let them in N at a time. Fair, prevents server overload. Ticketmaster, Amazon use this.

- **Queue-based inventory**: pre-allocate seat groups to queues; each queue worker processes one user at a time for its group

- **Read replicas**: all seat browsing hits read replicas; only reservation writes hit primary DB

- **Cache availability map**: cache section-level availability in Redis; only query DB for specific seat selection

## Data Model


```
events (id, name, venue_id, start_time, artist_id, ...)
venues (id, name, address, capacity, seat_map_url)
seats (id, event_id, section, row, number, status, held_by, held_until, sold_to)
orders (id, user_id, event_id, total, status, created_at)
order_items (id, order_id, seat_id, price)
tickets (id, order_id, seat_id, qr_code_hash, issued_at)
```


## Search and Discovery

- **Full-text search**: Elasticsearch for artist name, event name, venue search

- **Geo search**: PostGIS or Elasticsearch geo queries for "events near me"

- **Faceted filters**: date range, price range, category, availability

- **Trending**: pre-computed trending events updated every 5 minutes from purchase velocity

## Ticket Validation (Anti-Fraud)

- Each ticket has a unique QR code containing signed JWT (venue ID, event ID, seat ID, expiry)

- Scanner app verifies signature offline or via low-latency API call

- Mark ticket as scanned in Redis to prevent reuse

- Detect suspicious patterns: same ticket scanned twice, bulk purchases from single account

## Interview Checklist

- Lead with the double-booking problem — it's the crux; explain optimistic locking or Redis lock

- Describe the hold → payment → confirmed flow with TTL expiry

- Address traffic spike: virtual waiting room is the industry standard answer

- Separate read (search/browse) from write (reservation) paths

- Mention ticket validation with signed QR codes

- [Coinbase Interview Guide](https://techinterview.org/coinbase-software-engineer-interview/)

- [Lyft Interview Guide](https://techinterview.org/lyft-software-engineer-interview/)

- [Shopify Interview Guide](https://techinterview.org/shopify-software-engineer-interview/)

- [Uber Interview Guide](https://techinterview.org/uber-software-engineer-interview/)

- [Airbnb Interview Guide](https://techinterview.org/airbnb-software-engineer-interview/)

- [Stripe Interview Guide](https://techinterview.org/stripe-software-engineer-interview/)
