BookMyShow

Distributed SystemsPaymentsBooking SystemReal-time

0) Problem Restatement

Design a system like BookMyShow where users can browse movies, select seats in a theater, and complete booking with payments. The core challenge is preventing overselling of seats while keeping the system scalable.


1) Requirements

1.1 Functional

  • Browse movies, shows, theaters.
  • See seat layout with availability.
  • Select and hold seats.
  • Complete booking with payment.
  • Cancel/refund tickets.

1.2 Non-Functional

  • Consistency on seat booking (no double-booking).
  • Scalability (peak traffic during blockbuster releases).
  • Low latency for seat availability (< 1s refresh).
  • High availability, fault tolerance.

2) High-Level Architecture

2.1 Overview

  • Client (App/Web) → API Gateway → Services (Show, Inventory, Payment) → Cache/DB → Notifications.
  • Key challenges: seat locking (atomic, low-latency), payment integration (async), and scaling under spikes.

2.2 Flow Diagram

Architectural Diagram Locked


3) Components (what & why)

Client (App/Web)

  • UI for browsing shows, seat map, selecting seats, and paying.
  • Requests seat hold and completes payment.
  • Shows confirmation and stores tickets.

API Gateway

  • Authentication, authorization, rate limiting, request validation.
  • Routes to Show Service, Inventory Service, and Payment Service.
  • Provides edge-level protections and metrics.

Show Service

  • Movie metadata: movies, theaters, screens, show times.
  • Serves seat layout templates and static show info.

Seat Inventory Service (Core)

  • Maintains seat states: AVAILABLE, HELD, BOOKED.
  • Core responsibilities:
    • Atomically allocate/hold seats.
    • Maintain holds with TTL.
    • Confirm seats after successful payment.
    • Release seats on timeout/payment failure.
  • Should be horizontally scalable and partitioned by show_id.

Cache / DB

  • Redis (hot) for seat availability lookups and fast holds (with TTL).
  • Primary DB (SQL/NoSQL) for persistent bookings and audit trail.
  • Use CQRS: reads from cache, writes through Inventory Service to DB.

Locking Mechanism

  • Use atomic Redis operations (SETNX, Lua scripts) or DB row-level transactions for final persist.
  • TTL-based holds to auto-release uncompleted holds.

Payment Service

  • Integrates with external gateways (Stripe/PayU/Paytm).
  • Handles async callbacks, retries, idempotency tokens, and refunds.
  • Marks booking CONFIRMED on success.

Notification Service

  • Sends email/SMS/push confirmations and tickets.
  • Asynchronous worker processing.

4) Data Model (minimal)

Movie

sql
Movie(movie_id, title, language, duration, genre)

Show

sql
Show(show_id, movie_id, theater_id, screen_id, start_time, end_time)

Seat (per show)

sql
Seat(seat_id, show_id, row, col, status) -- status ∈ {AVAILABLE, HELD, BOOKED}

Booking

sql
Booking(booking_id, user_id, show_id, seats[], status, created_at, expiry_time)
-- status ∈ {PENDING, CONFIRMED, CANCELLED}

5) Key Flows

5.1 Browse Flow

  1. Client requests shows and seat layout.
  2. Show Service returns metadata and seat template.
  3. Seat availability comes from Redis (fast read).

5.2 Seat Hold (Select Seats)

  1. User selects seats → client calls Inventory Service: /holdSeats(show_id, seats[], user_id).
  2. Inventory Service atomically sets seats to HELD in Redis with TTL (e.g., 5 minutes) using Lua script or SETNX patterns.
  3. Service returns a hold_id (temp reservation) to the client.

5.3 Payment & Confirm

  1. Client initiates payment with Payment Service using idempotency token.
  2. Payment Service calls gateway; on success callback:
    • Payment Service notifies Inventory Service to confirm hold_id.
    • Inventory Service persists booking to DB, marks seats BOOKED, and removes TTL holds in Redis.
  3. On failure or timeout:
    • Hold TTL expires; Redis auto-releases seats.
    • If payment fails after confirmation, trigger rollback/refund flow.

5.4 Cancellation / Refund

  • Cancel booking → mark CANCELLED in DB, release seats (update Redis), initiate refund if applicable.

6) Concurrency & Correctness (Deep Dive A: Seat Locking, ~8–10 mins)

Problem

Multiple users may attempt to hold the same seat concurrently. We must prevent double-booking while keeping latency low.

Approach

  1. Redis Atomic Hold (preferred for latency)

    • Maintain a Redis hash or bitmap per show_id (e.g., show:{show_id}:seats).
    • Use a Lua script that checks all requested seats are AVAILABLE and atomically sets them to HELD with metadata {user_id, hold_id, expires_at}.
    • Script returns success/failure, thereby guaranteeing atomic multi-seat holds.
  2. TTL for Holds

    • Set a TTL for each held seat or the hold entry (e.g., 5 minutes).
    • If the user does not complete payment, seats auto-release.
  3. DB Confirmation (durable)

    • After payment success, perform a DB transaction to persist booking and set seats to BOOKED.
    • Use an idempotency token to ensure re-entrant safety for retries.
  4. Fallback to DB Locking (if Redis fails)

    • Use DB row-level locking or optimistic concurrency (version numbers) as a fallback to ensure correctness.

Edge Cases & Mitigations

  • Partial success in multi-seat hold: Lua script ensures all-or-nothing holds.
  • Redis crash before DB persist: Use a short window; on DB reconcile job, mark seats from HELD to AVAILABLE if not confirmed.
  • Clock skew / TTL drift: Use server-side timestamps and conservatively short TTLs.

7) Deep Dive B: Payments & Idempotency (~8–10 mins)

Payment Flow Characteristics

  • External gateways are async and may be slow or flaky.
  • Must avoid double-charging and ensure eventual consistency.

Steps

  1. Initiate Payment

    • Client posts to Payment Service with hold_id and an idempotency token.
    • Payment Service calls gateway.
  2. On Gateway Callback

    • Gateway calls our callback URL with transaction status.
    • Payment Service verifies callback signature, matches idempotency token, and notifies Inventory Service.
  3. Confirm Booking

    • Inventory Service verifies the hold is still valid and atomically performs DB write to mark seats BOOKED.
    • Persist payment record and link to booking.
  4. Failure Path

    • If payment fails or times out, do not confirm booking. Allow TTL to release seats.
    • If payment is captured but DB update fails, Payment Service retries the finalization idempotently.

Idempotency & Safety

  • Use an idempotency key for the entire payment+confirm workflow.
  • Payment Service stores state machine per key: INITIATED → PROCESSING → SUCCESS → NOTIFIED.
  • Re-entrant callbacks detect already-processed keys and no-op.

8) Scaling & Performance (~5 mins)

Partitioning

  • Partition services by show_id (each show independent) so seat state is sharded and hotspots are distributed.

Caching

  • Use Redis for fast read path (availability) and for holds.
  • Cold data (past shows/bookings) persisted in DB, moved to cold storage.

Resilience to traffic spikes

  • Autoscale Inventory Service, use request queuing at API Gateway for surge control.
  • For blockbuster releases, implement pre-book queuing and adaptive rate limits.

Monitoring & SLOs

  • Metrics: holds/sec, confirms/sec, failed payments, Redis ops, DB latency.
  • SLOs: <1s seat availability read, >99.9% booking durability, near-zero double-book.

9) Failure Modes & Recovery

Redis Failure

  • If Redis unavailable: degrade to DB-backed locking (slower but consistent), or apply circuit-breaker to reject holds temporarily.

Payment Gateway Delays

  • Accept asynchronous confirmation; keep holds short; offer “pending” UI feedback.

Partial Writes

  • Reconciliation job: scan HELD entries older than TTL and reconcile with DB.
  • Audit logs for every hold/confirm operation for forensic and retry.

10) Trade-offs & Alternatives

Redis vs DB locking

  • Redis provides very low latency and atomic multi-key Lua scripts — ideal for seats. DB locking is more durable but slower.

Synchronous vs Asynchronous Booking Persist

  • Persisting synchronously on payment confirmation ensures strong durability. Asynchronous persist risks temporary inconsistencies but scales writes.

Seat Hold TTL length

  • Short TTL reduces lock contention but risks user UX friction (time to complete payment). Choose ~3–5 minutes, tuned by analytics.

11) Security & Data Integrity

  • Use TLS for all endpoints; secure payment callbacks with signatures.
  • Validate user identity before allowing holds.
  • Audit logs of holds and confirmations; immutable payment records.

12) Interview Time Allocation (45 min)

  • 5 min: Requirements & scope
  • 10 min: HLD & architecture diagram
  • 5 min: Data model & flows
  • 10 min: Scaling & failure handling
  • 15 min: Deep dives (Seat locking + Payments)

13) Summary

  • Core of BookMyShow is safe seat allocation under high concurrency and integrating payment semantics reliably.
  • Use Redis for low-latency holds (atomic Lua scripts + TTL), DB for durable booking records, and Payment Service with idempotency for safe external interactions.
  • Partition by show_id, autoscale services, and prepare reconciliation jobs for failure recovery.
ResumeSkool Logo

No-Nonsense AI Resume
Building Platform for Free

©2026 - ResumeSkool