- Published on
- Views
Design a Ticket Booking System Design Interview Guide
- Authors

- Name
- Javed Shaikh
← System Design Interview Preparation
This guide walks through Design a Ticket Booking System the way you would in a backend or Java interview. The numbers are interview estimates. They help you show your thinking. They are not a production capacity plan.
1. Problem
Concerts, movies, and trains sell unique seats. Two people must not get seat 12A.
Example:
- Browse events and a seat map
- Click two seats → they lock for 8 minutes
- Pay
- Tickets are issued
- If they walk away, locks expire and seats return
- On sale morning, traffic is a spike, not an average
This is e-commerce inventory with hard uniqueness and a timer.
2. Functional Requirements / FR
| Requirement | What it means |
|---|---|
| Event listing | Shows, venues, times. |
| Seat map | Available / locked / sold. |
| Seat locking | Hold for this user until TTL. |
| Booking flow | Lock → pay → confirm. |
| Payment | Same rules as payment system. |
| Lock expiry | Worker or TTL frees seats. |
| No double booking | Unique constraint on seat + show. |
| Waiting room | Optional queue before the map on huge onsales. |
| Idempotency | Retry lock/pay safely. |
Out of scope: dynamic pricing engine internals, paper tickets, full fraud product.
3. Non-Functional Requirements / NFR
| Requirement | Why it matters |
|---|---|
| Fairness under spike | Sale opens at 10:00. |
| Lock correctness | Redis TTL and DB unique both help. |
| Low latency map | Seat map should feel live. |
| Idempotent pay | Double submit cannot sell twice. |
| High availability of reads | Browsing can degrade; selling cannot lie. |
Interview line: lock with TTL, confirm with a unique booking row, pay in between.
4. Back-of-the-Envelope Calculation
Traffic assumptions
- Popular onsale: 1 million users in 10 minutes trying to buy 20,000 seats
- Steady state: 5 million map views/day across many events
- Read/write: map reads dominate; locks/writes cluster at onsale
QPS
Steady map views:
5,000,000 / 86,400 ≈ 58 QPS average
If peak is 5x, around 290 QPS — easy.
Onsale spike (1 million users / 10 minutes):
1,000,000 / 600 seconds ≈ 1,670 QPS of “enter sale”
Seat clicks could be 5× that → around 8,000 QPS
If you call that 5x a “peak on peak”, design lock service for around 8,000–10,000 QPS.
Successful bookings cannot exceed seats: 20,000 writes, not millions.
Storage
Seat row tiny. 20k seats × 200 bytes = a few MB per show. Bookings 20k × 1 KB = 20 MB. History is the long-term store.
Cache / memory
Seat map in Redis: 20k seats × 32 bytes ≈ 640 KB per show. Hundreds of live shows still fit in memory.
Locks: Redis keys showId:seatId with TTL.
Servers
Waiting room + gateway absorb the 8k QPS. Booking/lock nodes: 10–20 if each handles ~500 lock ops/sec. Interview estimates, not exact production numbers.
5. APIs
GET /v1/events
GET /v1/events/{eventId}/seatmap
POST /v1/holds
Idempotency-Key: <uuid>
{ "eventId", "seatIds": ["12A","12B"] }
POST /v1/bookings
Idempotency-Key: <uuid>
{ "holdId", "paymentMethodToken" }
POST /v1/webhooks/payment
Waiting room (optional): GET /v1/sales/{eventId}/ticket returns a wait token.
Errors: 409 seat taken, 410 hold expired, 429 waiting room.
6. Data Model
events / seats
Seat is unique per event_id + seat_id. Status: open, held, sold.
holds
hold_id, user_id, event_id, seat_ids, expires_at, status.
bookings
booking_id, hold_id, payment_id, status, unique (event_id, seat_id) on sold seats table.
Redis lock
SET hold:{event}:{seat} {userId} NX EX 480 (8 minutes).
DB unique index is the last line of defense if Redis is wrong.
7. High-Level Design
Locks are the hot path. Bookings are durable.
Ticket Booking architecture
Components:
- User → API Gateway → Booking Service
- Seat Lock Service → Redis
- Booking DB on confirm
- Payment Service
- Expiry worker releases leftover holds
- Notification after ticket
Flow:
- (Optional) waiting room token.
- Load seat map from cache.
SET NXeach seat. All-or-nothing: if one fails, release the others.- Pay with hold id.
- Insert booking; mark seats
sold; delete lock keys or let them expire harmlessly. - Email tickets.
Redis TTL is the clock. The worker is backup for DB held rows.
8. Deep Dives
Prevent double booking
- Redis
NX - Unique
(event_id, seat_id)where status=sold - Hold owned by
user_id— another user cannot confirm your seats
Lock expiry
8–10 minutes is a common interview number. Payment start can extend TTL once.
Waiting room
Issue sequential tokens. Gateway only forwards N users/sec to the map. Extra users poll. This is rate limiting plus a queue, not a fake progress bar.
Idempotency
Same hold request returns the same holdId if seats still yours. Checkout key maps to one booking_id.
High spikes
Cache the map. Do not hit SQL per hover. Pub/sub optional to refresh sold seats; polling every 2s is acceptable in interview.
9. Bottlenecks
- Redis hot keys on one event (shard by eventId — one event still hot)
- Seat map thundering herd at T=0
- Payment provider slower than lock TTL
- Unfair bots — CAPTCHA / waiting room / account limits
10. Tradeoffs
| Choice | Upside | Downside |
|---|---|---|
| Redis locks | Fast | Need DB unique too |
SQL SELECT FOR UPDATE | Simple | Onsale will choke |
| Long TTL | Users finish pay | Seats frozen |
| Waiting room | Protects core | Feels slow |
Pick: Redis NX + TTL, SQL unique on sold, waiting room for famous onsales.
11. Failure Modes
| Failure | Handling |
|---|---|
| Redis down | Fail holds (do not sell from stale map) |
| Paid after TTL | Try re-lock; else refund |
| Worker down | Redis TTL still frees; DB held rows catch up later |
| Duplicate confirm | Unique seat constraint |
| Bot army | Waiting room + rate limit |
12. Interview Answer in 10 Minutes
"Ticket booking is unique inventory with a timer.
Average traffic is small. The onsale is the design: a million people in 10 minutes is about 1,670 QPS to enter, maybe 8,000 seat clicks. Only 20,000 seats can sell. I would put a waiting room in front, serve the seat map from cache, and lock seats in Redis with SET NX and an 8-minute TTL.
Checkout uses an idempotency key, charges payment, then writes a booking row with a unique constraint on event plus seat. An expiry worker is backup. If payment is late, we refund rather than double-sell.
The map is a hint. The lock plus the unique row is the truth."
13. Interview Talking Points
- SET NX + TTL.
- Unique sold seat.
- All-or-nothing multi-seat holds.
- Waiting room for spikes.
- Extend lock when payment starts.
- Metrics: lock failure rate, expired holds, double-sell (must be 0), time-to-ticket.
- Same money path as payments.
14. Follow-up Questions
GA (no seats)?
Decrement a counter with the same conditional pattern as e-commerce stock.
Trains with waitlist?
After sold out, queue requests; promote when a booking cancels.
Multi-city inventory?
Partition locks by event_id.
WebSockets for map?
Nice to have. Polling is enough if you say why.
15. Internal Links
Related JavaThoughts reading:
- System Design Interview Preparation
- Design E-commerce Cart, Order, and Inventory
- Design a Payment System
- Design a Rate Limiter
- Design a Distributed Cache
- Java
- Spring Boot
- Kafka
- Microservices
- Distributed Systems
- Rate limiting
- 20 system design concepts
Next in this series: Design Logging, Metrics, and Observability Platform.
