Back🌐

Designing a Ticket Booking System

AWAKENING0 / 100
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░]0%

Designing a Ticket Booking System

High Level Design·intermediate·~25 min read

  • system-design
  • ticket-booking
  • concurrency
  • distributed-systems
  • hld

What you'll learn

  • Requirements

  • Functional Requirements

    1. Search and Discovery — users can search movies, view showtimes, browse cinemas by city 2. Seat Map Viewing — display real-time seat availability for a selected show 3. Seat Selection and Reservation — user selects seats, system tempor…

  • Non-Functional Requirements

    - Consistency — absolutely no double-booking (two users cannot book the same seat) - Low Latency — seat selection and reservation must feel instantaneous - High Availability — search and browsing must always work - Fairness — waiting use…

  • Extended Requirements

    - Notifications (email/SMS/push) for booking confirmation and waiting queue alerts - Booking history and past tickets - Analytics: popular shows, peak hours, revenue dashboards - Multi-language and multi-currency support ---

  • Capacity Estimation

  • Traffic

    | Metric | Value | |--------|-------| | Page views/month | 3 Billion | | Tickets sold/month | 10 Million | | Reads:Writes ratio | ~300:1 (browsing vs. booking) | | Peak concurrent users | 100K+ during popular movie launch |

  • Infrastructure

    | Resource | Calculation | |----------|------------| | Total seats in system | 500 cities x 10 cinemas x 2000 seats = 10M seats | | Shows per day (estimate) | 500 x 10 x 4 shows/screen x 3 screens = 60K shows/day | | Reservations per sec…

  • Storage

    | Data | Size Estimate | |------|---------------| | Seat record | ~100 bytes (IDs + status + metadata) | | All seats for all shows/month | 10M seats x 4 shows x 30 days x 100B ~ 120GB/month | | Reservations | 10M x 500B ~ 5GB/month | | B…

  • Core Architecture

  • Component Responsibilities

    | Component | Role | |-----------|------| | Booking Service | Handles seat selection, reservation, payment flow | | ActiveReservationService | Tracks unpaid reservations, expires after 5 min | | WaitingUserService | FIFO queue for users…

  • Design Evolution & Trade-offs

    How this system grew from an MVP to what a production version looks like — and the choices you'd defend in an interview.

  • Stage 1 — MVP: One box, one database

    The simplest thing that could possibly work for a ticket-booking service. - A single API server (monolith) that handles browsing, seat selection, reservation, and payment. - A single relational database (Postgres) with show, seat, reserv…

  • Stage 2 — Scale-out: Multiple pods behind a load balancer

    - API layer becomes stateless; N replicas of the Booking / Show / Search services behind an L7 load balancer. - Sessions and reservation timers moved out of process memory into Redis so any pod can serve any request. - Read replicas abso…

  • Stage 3 — Bottleneck: Concurrent seat locking & overselling

    This is the problem most characteristic of ticket-booking-system: many users, ONE seat, exactly one winner, no double-books. Every option below has been used in production; the trade-off is where you draw the line between latency, correc…

  • Stage 4 — Second bottleneck: Read-heavy seat-map fan-out & thundering herds

    Ticket booking is ~300:1 reads:writes. The seat-map endpoint — "show me every seat's current status for show X" — is hit by every browsing user, refreshed constantly on the client, and can be requested by 100K+ concurrent users the secon…

  • Stage 5 — Mature architecture

    All the additions from stages 2–4, wired together. Sessions and seat locks live in Redis. Postgres primary handles writes; read replicas serve the browse tier. Kafka carries async work — notifications, analytics, expiry events, and per-s…

  • Cost vs Complexity — When to Pick What

    | Concern | MVP choice | Growth choice | Enterprise choice | |---------|-----------|---------------|-------------------| | Compute | Single VM running the monolith | K8s Deployment per service (Booking / Show / Search) | Multi-region K8s…

  • Migration Path (MVP → Enterprise)

    Stepped, in order. Each step is safe to ship on its own and each one unlocks the next. 1. Add read replicas. Zero risk — no writes affected. Point the seat-map and browse endpoints at replicas via a read-only connection pool. Booking wri…

  • End-to-End Flow

    1. User browses shows → Show Service returns available shows 2. User selects a show → Cache returns seat map (with availability hints) 3. User selects seats → Booking Service starts SERIALIZABLE transaction 4. Transaction succeeds → seat…

  • ActiveReservationService — Deep Dive

  • Purpose

    Keeps track of all currently active (unpaid) reservations and automatically expires them after the 5-minute payment window.

  • Data Structure

  • Why LinkedHashMap?

    - Maintains insertion order — the oldest reservations are always at the front - O(1) insertion — new reservations go to the end - O(1) lookup by ShowID — check active reservations for any show - O(1) identification of next-to-expire — th…

  • Background Expiry Thread

  • Why 5 Minutes?

    - Too short (1-2 min): users can't complete payment, leads to frustration - Too long (15-30 min): seats held too long, especially during peak demand - 5 minutes: enough for most payment flows, short enough to keep seats circulating

  • Timer Skew Handling

    The client shows a countdown timer to the user, but the server adds a 5-second buffer beyond the displayed time. This prevents a race condition where the client believes time remains but the server has already expired the reservation. Wi…

  • WaitingUserService — Deep Dive

  • Purpose

    Fair queuing mechanism for users who want seats that are currently reserved or booked.

  • Data Structure

    The inner LinkedHashMap maintains insertion order so the head pointer always references the longest-waiting user — enabling O(1) FIFO fairness without scanning the queue.

  • How It Works

    1. User requests seats that are all RESERVED/BOOKED 2. System offers: "Join waiting queue? You'll be notified if seats free up" 3. User joins → added to FIFO queue for that ShowID 4. When ActiveReservationService releases seats: - Dequeu…

  • Edge Cases

    - User in queue cancels → remove from queue, shift others forward - More seats released than first user needs → can notify multiple users - Show starts → purge entire queue for that ShowID - Maximum wait time in queue: 1 hour — after thi…

  • Partial Availability Problem

    User #1 wants 10 seats but only 5 are currently available. User #2 wants exactly 5 seats. The system handles this by: when seats become available, all WaitingUserService servers check the free seat count. Waiting users who need more seat…

  • All-or-Nothing Booking Policy

    Users get ALL requested seats or none — no partial ticket orders. If a user requests 4 seats and only 3 are available, the request fails entirely. This simplifies reservation logic (no partial holds, no "do you still want 3 of 4?" flows)…

  • Database Schema

  • Tables

  • Indexes

    ---

  • Partitioning Strategy

  • Partition by ShowID

    Key insight: All seats for a single show must be on the same database partition. Why this works: - A booking transaction only touches seats within ONE show - All seats for that show are on the SAME partition - Therefore: no distributed t…

  • Why NOT Partition by MovieID?

    If partitioned by MovieID, a hot movie (blockbuster release) overloads a single server since all shows of that movie land on one partition. ShowID distributes the load naturally because a popular movie has many shows across many theaters…

  • Why NOT Partition by CinemaID or City?

    - A cinema has multiple shows → one partition handles ALL concurrent bookings for all shows at that cinema - Creates unnecessary contention between shows that don't conflict - ShowID gives the finest granularity where transactions actual…

  • Consistent Hashing for Daemon Services

    ActiveReservationService and WaitingUserService are distributed across multiple servers. Use consistent hashing on ShowID to allocate approximately 3 servers per show. This provides: - Load distribution — different shows map to different…

  • The Problem

    When a blockbuster launches, thousands of users try to book the SAME show simultaneously: - All traffic concentrates on ONE show's seats - One DB partition handles all these requests - Normal horizontal scaling doesn't help (it's the sam…

  • Solutions

    1. Vertical Scaling of Hot Partition - Temporarily assign more CPU/RAM to the partition handling the hot show - Works well for predictable peaks (known release dates) 2. Queue-Based Serialization - All booking requests for a show go into…

  • Caching Strategy

  • What to Cache

    | Data | Cache Strategy | Invalidation | |------|---------------|-------------| | Seat availability map per show | Read-through | Write-through on reservation/booking | | Show listings by cinema/city | TTL-based (5 min) | Update on show…

  • The Consistency Challenge

    Solution: Cache is advisory only - Cache provides fast reads for seat map display - Users may see a seat as "available" that was just reserved - The actual booking attempt ALWAYS goes through a SERIALIZABLE DB transaction - If the DB tra…

  • Fault Tolerance

  • Failure Scenarios and Recovery

    | Failure | Impact | Recovery | |---------|--------|----------| | Booking Service dies mid-reservation | Seats stuck in RESERVED | ActiveReservationService timer still runs → seats released after 5 min | | Payment Service timeout | User…

  • Key Resilience Properties

    - Self-healing reservations — the 5-minute timeout is a natural circuit breaker. Any failure that prevents payment completion results in seats being automatically freed. - Stateless services — Booking Service is stateless; all state is i…

  • Fault Tolerance Gap: WaitingUserService

    WaitingUserService data is in-memory only — a crash loses the entire waiting queue unless master-slave replication is configured. Unlike ActiveReservationService (which can recover from the Booking table by querying Status = Reserved), t…

  • Key Design Decisions Summary

    | Decision | Rationale | |----------|-----------| | SERIALIZABLE isolation | Prevents double-booking without application-level locks | | 5-minute payment timeout | Prevents seat hoarding while giving users enough time | | 5-second server…

  • Anti-Abuse Measures

  • Maximum Seats Per Booking

    Restrict users to a maximum of 10 seats per booking to prevent scalping. Without this limit, a single user (or bot) could reserve an entire show's worth of seats, holding them hostage for 5 minutes at a time or reselling at inflated pric…

  • Enumerations

  • BookingStatus

    | Status | Meaning | |--------|---------| | Requested | User initiated booking, awaiting seat lock | | Pending | Seats reserved, awaiting payment | | Confirmed | Payment successful, ticket issued | | Checked-in | User scanned ticket at t…

  • SeatType

    | Type | Description | |------|-------------| | Regular | Standard seating | | Premium | Extra legroom, better view, higher price | | Accessible | Wheelchair-accessible, companion seats | | EmergencyExit | Exit row seats with extra legro…

  • PaymentStatus

    | Status | Meaning | |--------|---------| | Unpaid | Payment not yet initiated | | Pending | Payment processing in progress | | Completed | Payment successfully charged | | Failed | Payment attempt failed (retry possible) | | Declined |…

  • Scale Reference Numbers

    | Parameter | Value | |-----------|-------| | Cities | 500 | | Cinemas per city | 10 | | Seats per cinema | 2,000 | | Shows per day per cinema | 2 | | Storage per day | 500 x 10 x 2000 x 2 x 100 bytes = 2 GB/day | | 5-year storage | ~3.6…

  • API Design

  • Key Endpoints

    ---

  • Comparison: Ticket Booking vs. Ride-Sharing Allocation

    | Aspect | Ticket Booking | Ride-Sharing (Driver Assignment) | |--------|---------------|----------------------------------| | Resource | Fixed seat (static) | Moving driver (dynamic) | | Contention pattern | Many users → ONE seat | ONE…

← back to High Level Design

Designing a Ticket Booking System — High Level Design | GoCrack | GoCrack