🎟️ Design Ticket Booking (BookMyShow) — System Design Interview Guide
Medium · Transactions & Concurrency
Design a ticket booking system like BookMyShow or Ticketmaster where users can browse events, select seats, and purchase tickets with guaranteed no double-booking under high concurrency.
Open the interactive Ticket Booking (BookMyShow) design on PrepGrind → Drag load balancers, caches, databases, and queues onto a canvas, run a live traffic simulation to watch latency and bottlenecks under load, and follow the full interview walkthrough below — free, in your browser.
Functional requirements
- Browse movies, events, venues, and available shows
- View seat map and select specific seats
- Temporarily hold selected seats (5-min reservation)
- Complete payment to confirm booking
- E-ticket generation with QR code
- Cancellation and refund flow
Non-functional requirements & scale
- 10M concurrent users during blockbuster release (IPL/World Cup)
- No two users can book the same seat — exactly once
- Seat hold must expire automatically after 5 minutes
- Booking confirmation < 3 seconds
- Read path (browsing): latency < 100ms
- 99.99% uptime during event window
Capacity estimation
Core challenge: seat selection under concurrent load. A venue has 10,000 seats. 100,000 users try to book simultaneously. Must ensure no double-booking. Seat hold: user selects seat → seat reserved for 5 min → if payment not completed, seat released. Distributed lock required.
Core entities
- Event — eventId, name, venueId, datetime, category, description, basePrice
- Show — showId, eventId, datetime, availableSeats, totalSeats
- Seat — seatId, showId, row, number, type (standard/premium/vip), status, price
- Booking — bookingId, userId, showId, seatIds[], status, amount, paymentId, expiresAt
API design
GET /api/v1/shows/:showId/seats— Returns seat map with availability status. Cached, refreshed every 5s.POST /api/v1/bookings/hold— Temporarily hold seats. Body: { showId, seatIds[] }. Returns { bookingId, expiresAt }.POST /api/v1/bookings/:bookingId/confirm— Confirm payment and finalize booking. Returns e-ticket with QR.DELETE /api/v1/bookings/:bookingId— Cancel booking. Releases seats back to available pool.
High-level design
Seat selection: Redis distributed lock on seatId. Hold: mark seats as HELD in DB + Redis SET with 5-min TTL. On TTL expiry: release seats. Payment: Stripe/Razorpay → confirm booking → generate QR. High traffic: queue requests and process sequentially per show.
Deep dives
🔒 Seat Locking Strategy
Option A: Pessimistic DB lock — SELECT FOR UPDATE on seat rows. Serialized, slow, doesn't scale to 100K concurrent users. Option B: Redis SETNX — SET seatId:showId userId NX EX 300 (atomic, 5-min TTL). If returns nil, seat taken. Sub-millisecond. Multiple seats: Lua script for atomic multi-seat hold (all-or-none). Release: Redis key deletion or TTL expiry.
⏱️ Hold Expiry
Redis key TTL = 300s (5 min). On expiry: Redis keyspace notification → Booking Service receives event → mark seats as AVAILABLE in DB. Problem: Redis keyspace notifications have at-least-once delivery. Solution: also store expiresAt in DB. Background job scans for expired holds every 30s (belt and suspenders). On any seat status read, check expiresAt.
🌊 Handling Traffic Spikes
IPL ticket release: 10M users hit "Book" simultaneously. Strategies: (1) Queue requests in SQS — serialize per show, process FIFO. (2) Virtual waiting room: user gets queue position, auto-refresh. (3) Rate limiting at gateway per showId (max 1000 req/sec per show). (4) Pre-warm cache with seat availability before sale opens. Show users fair queue experience vs HTTP 500 errors.
🎫 QR Code Generation
After payment confirmed: generate booking record in DB. QR payload: bookingId + userId + showId + HMAC signature (prevents forgery). Encode as QR code image → store in S3. Send link via email/SMS. On venue scan: decode QR → verify HMAC → check booking status in DB → mark as used. Offline QR validation via cached booking list for each show.
Scaling considerations
- DB row-level locking for payments; Redis for pre-payment seat holding
- Read replicas for seat browsing (high read traffic before sales open)
- Horizontal scale of Booking Service (stateless — lock state in Redis)
- Separate low-latency path for seat hold from payment path (slower)
- CDN caches seat availability snapshots (5s TTL) — reduces DB load by 80%
What interviewers expect by level
- Junior: Describe seat selection and booking flow. Understand why two users can book the same seat without locking.
- Mid: Redis SETNX for seat locking, hold expiry with TTL, optimistic vs pessimistic locking trade-offs.
- Senior: Full spike handling with SQS queue, virtual waiting room, atomic multi-seat Lua script, payment idempotency.
- Staff: Multi-region for global events, CDN-cached seat maps, capacity planning for 10M concurrent users, cost model.
Practice more system design case studies
- Design URL Shortener
- Design Social Media Feed
- Design Chat System
- Design Video Streaming
- Design Ride-Sharing Platform
- Design E-Commerce Platform
- Design UPI Payment Gateway
- Design Google Docs
- Design Tinder
- Design Google Drive / Dropbox
- Design Instagram
- Design Type-Ahead Search
- Design Web Crawler
- Design Pastebin
- Design Notification System
- Design Rate Limiter (Standalone)
- Design Simple Web App
- Design Food Delivery (Swiggy)
- Design Stock Trading System
- Design Live Streaming (Twitch)
- Design Distributed Key-Value Store
- Design Ad Click Aggregation
- Design Monitoring / Metrics (Datadog)
- Design Online Judge (LeetCode)
- Design FB Post Search
- Design Yelp
- Design Cache Layer
- Design Message Queue
- Design Full Production Stack
PrepGrind runs entirely in your browser, free, no installation required. Loading the interactive playground…