# System Design Interview: Design a Ticketing System (Ticketmaster)

Source: https://www.techinterview.org/post/3233463451/system-design-ticketing-system-ticketmaster/
Updated: 2026-04-17 · techinterview.org

## System Design Interview: Design a Ticketing System (like Ticketmaster)

Designing a ticket booking system like Ticketmaster tests your ability to handle high concurrency, flash sale scenarios, inventory management, and payment processing. The core challenge is preventing overselling while maintaining high availability.

## Requirements Clarification

### Functional Requirements

- Browse events and view available seats

- Select and reserve seats for a limited hold period (10 minutes)

- Complete purchase with payment processing

- Handle extremely high traffic during popular event on-sales (flash sales)

- View and cancel existing bookings

### Non-Functional Requirements

- Scale: 100K events, 50K seats/event, 500K concurrent users during peak

- Latency: seat selection < 200ms, checkout < 2s

- Consistency: never oversell (strong consistency for seat inventory)

- Availability: 99.99% uptime

## Core Challenge: Seat Inventory

The hardest part is concurrent seat selection. Two users cannot book the same seat. Options:

- **Optimistic locking**: read seat status, update with version check - retry on conflict

- **Pessimistic locking**: lock row in DB during selection - low throughput

- **Redis atomic operations**: SETNX for seat hold - fast, but need DB sync

- **Virtual queue**: queue users during peak, process sequentially - used by Ticketmaster

## High-Level Architecture


```
Users -> CDN -> Load Balancer
              |
       API Gateway
       |           |           |
  Browse        Queue        Checkout
  Service       Service      Service
       |           |           |
  Event DB    Redis Queue   Payment
  (Postgres)  + Seat Lock   Gateway
                   |
              Booking DB
              (Postgres)
                   |
              Notification
              Service (email/SMS)
```


## Seat Reservation Flow


```
1. User selects seat
2. API calls Redis: SET seat:{event_id}:{seat_id} {user_id} EX 600 NX
   - NX = only set if Not eXists (atomic)
   - EX 600 = auto-expire in 10 minutes (hold period)
   - Returns OK (reserved) or nil (already taken)
3. On success: persist hold to DB with expiry timestamp
4. User completes checkout within hold window
5. Payment charged via Stripe/Braintree
6. Mark seat as SOLD in DB, release Redis key (or let TTL expire)
7. Send confirmation email/SMS
```


## Virtual Queue for Flash Sales

When traffic spikes (popular concert goes on sale), use a virtual waiting room:


```
1. Users enter waiting room URL before sale starts
2. Backend assigns each user a random position token
3. At sale start, release users in batches (e.g., 1000 every 30s)
4. Token validated at API gateway - non-queued users rejected
5. Users behind the gate see estimated wait time
```

Implementation: Redis sorted set (ZADD queue:event123 {timestamp} {user_token}), ZPOPMIN to release batches. This prevents thundering herd from hitting seat inventory directly.

## Database Schema


```
events: id, name, venue_id, start_time, on_sale_time, total_seats
seats: id, event_id, section, row, number, status, hold_expires_at
  status: AVAILABLE | HELD | SOLD
bookings: id, user_id, event_id, total_amount, status, created_at
booking_seats: booking_id, seat_id
payments: id, booking_id, stripe_payment_intent_id, amount, status
```


## Handling Expired Holds

Three approaches for releasing expired seat holds:

- **Redis TTL**: Redis auto-expires the lock key; background job scans DB for holds past expiry and marks seats AVAILABLE

- **Cron job**: Every 30s, UPDATE seats SET status=AVAILABLE WHERE status=HELD AND hold_expires_at < NOW()

- **Event-driven**: Redis keyspace notifications on expiry trigger a Lambda/worker to release seat in DB

## Read Scalability for Seat Maps

- Cache seat map in Redis as a bitmap or hash: O(1) lookup per seat, invalidate on state change

- Use read replicas for event browse and seat map reads

- CDN-cache static event assets (images, venue maps) aggressively

- WebSocket or SSE for real-time seat status updates (seats turning red as others select them)

## Payment Processing

- Create Stripe PaymentIntent on checkout page load (idempotency key = booking_id)

- Client confirms with card details - Stripe handles PCI scope

- Stripe webhook on payment_intent.succeeded marks booking CONFIRMED

- If payment fails, release seat hold immediately

- Idempotency keys prevent double charging on retries

## Consistency Trade-offs

- **Seat inventory**: strong consistency - use distributed locks (Redis NX) + DB transactions

- **Event catalog**: eventual consistency acceptable - cache aggressively

- **Booking history**: strong consistency (user must see confirmed bookings immediately)

- **Notification delivery**: at-least-once via message queue (idempotent email handler)

## Interview Tips

- Lead with the inventory consistency problem - it is the core challenge

- Explain Redis SETNX for atomic seat holds

- Discuss virtual queue as a traffic management pattern for flash sales

- Know the difference between hold and confirmed booking states

- Mention idempotency in payment processing to prevent double charges

- Discuss real-time seat map updates via WebSocket
