← All Problems

MakeMyTrip – Hotel Booking System

MakeMyTrip – Hotel Booking System

Distributed SystemsPaymentsBooking System

0) Problem Restatement

Design a hotel booking platform like MakeMyTrip where users can search for hotels, view availability, book rooms, and make payments.


1) Requirements

1.1 Functional

  • Search hotels by location, dates, and filters (price, rating, amenities).
  • View hotel details, photos, reviews, and room availability.
  • Book rooms with real-time availability checks.
  • Payment processing and confirmation.
  • Cancellation and refund handling.
  • Hotel partner dashboard (manage inventory, pricing, bookings).
  • User reviews and ratings.

1.2 Non-Functional

  • High Availability: 99.9% uptime for search and booking.
  • Low Latency: Search results < 500ms, booking confirmation < 2s.
  • Consistency: No double-booking of rooms.
  • Scalability: Handle millions of searches/day, peak traffic during holidays.
  • Reliability: Accurate inventory sync across channels.
  • Data Integrity: Secure payment and booking records.

1.3 Scale Estimates

  • Hotels: 1 million hotels, avg 50 rooms/hotel = 50M rooms.
  • Daily searches: 100M searches/day (~1200 searches/sec avg, 5000/sec peak).
  • Daily bookings: 1M bookings/day (~12 bookings/sec avg, 50/sec peak).
  • Users: 100M registered users.
  • Data: Hotel metadata ~100 GB, booking records ~10 TB/year.

1.4) API Specifications

User-Facing APIs

  • GET /api/hotels/search - Search hotels by location, dates, and filters
  • GET /api/hotels/{hotel_id} - Get detailed hotel information including room types and amenities
  • POST /api/hotels/availability - Check real-time room availability for specific dates
  • POST /api/bookings - Create a new booking with room hold (returns booking_id and payment_url)
  • GET /api/bookings/{booking_id} - Get booking details and status
  • POST /api/bookings/{booking_id}/cancel - Cancel booking and initiate refund

Hotel Partner APIs

  • PUT /api/partner/inventory - Update room inventory, pricing, and availability
  • GET /api/partner/bookings - View bookings for partner's hotels

2) High-Level Architecture

2.1 Overview

  • ClientAPI GatewaySearch ServiceInventory ServiceBooking ServicePayment ServiceNotification Service.
  • Key components: Search with caching, inventory management with locking, async payment processing, and real-time availability updates.

2.2 Flow Diagram

Architectural Diagram Locked


3) Components (what & why)

Client (Web/Mobile)

  • Search interface with filters (location, dates, price, rating, amenities).
  • Hotel detail pages with photos, reviews, room types, and pricing.
  • Booking flow with date selection, room selection, and payment.

API Gateway

  • Authentication, rate limiting, request validation.
  • Routes to appropriate microservices.
  • SSL termination and DDoS protection.

Search Service

  • Responsibilities:

    • Handle search queries with location, dates, and filters.
    • Rank hotels by relevance, price, rating.
    • Integrate with Inventory Service for real-time availability.
  • Optimization:

    • Cache popular searches in Redis (location + dates as key).
    • Use Elasticsearch for full-text search and filtering.
    • Pre-aggregate data for common queries.

Inventory Service (Core)

  • Responsibilities:

    • Manage room availability per hotel, room type, and date.
    • Handle atomic room allocation (hold and confirm).
    • Sync with hotel partner updates.
  • Data Structure:

    • Inventory(hotel_id, room_type, date, available_count, booked_count)
  • Locking Mechanism:

    • Use Redis for temporary holds (TTL-based) while we wait for the booking to be confirmed.
    • Use DB transactions for final booking confirmation.

Booking Service

  • Responsibilities:

    • Orchestrate booking flow: hold room → payment → confirm.
    • Create booking records with user, hotel, dates, and payment info.
    • Handle cancellations and refunds.
  • State Machine: PENDING → PAYMENT_INITIATED → CONFIRMED / CANCELLED.

Payment Service

  • Responsibilities:

    • Integrate with external payment gateways (Razorpay, Stripe, PayPal etc).
    • Handle async callbacks, retries, and idempotency.
    • Process refunds for cancellations.
  • Security: PCI-DSS compliance, tokenization, encrypted storage.

Hotel Metadata DB

  • Store hotel info: name, location, amenities, photos, reviews, ratings.
  • Read-heavy workload; use read replicas and caching.

Inventory DB

  • Store room availability per hotel, room type, and date.
  • High write load during bookings; use partitioning by hotel_id.

Booking DB

  • Store booking records: user_id, hotel_id, room_type, dates, status, payment_id.
  • Audit trail for all booking state changes.

Notification Service

  • Asynchronous worker consuming from message queue.
  • Send booking confirmations, reminders, and cancellation notifications via email/SMS/push.

Partner Dashboard

  • Hotel partners manage inventory, pricing, and view bookings.
  • Updates trigger cache invalidation for real-time consistency.

4) Data Model (minimal)

Hotel

sql
Hotel(hotel_id, name, location, address, rating, amenities[], photos[], description)

Room Type

sql
RoomType(room_type_id, hotel_id, type_name, capacity, base_price, amenities[])

Inventory (per date)

sql
Inventory(hotel_id, room_type_id, date, total_rooms, available_rooms, price)

Booking

sql
Booking(
  booking_id, 
  user_id, 
  hotel_id, 
  room_type_id, 
  check_in_date, 
  check_out_date, 
  num_rooms,
  total_price,
  status, -- PENDING, CONFIRMED, CANCELLED
  payment_id,
  created_at,
  updated_at
)

User

sql
User(user_id, name, email, phone, preferences[])

Review

sql
Review(review_id, user_id, hotel_id, rating, comment, created_at)

5) Key Flows

5.1 Search Flow

  1. User enters location (e.g., "Goa"), check-in/check-out dates, and filters.
  2. Search Service checks Redis cache for matching query.
  3. On cache miss: Query Elasticsearch for hotels matching location/filters.
  4. For each hotel, check availability via Inventory Service.
  5. Rank results by relevance, price, rating, and return to user.
  6. Cache results in Redis with TTL (5-10 minutes).

5.2 Booking Flow (Happy Path)

  1. User selects hotel, room type, and dates.

  2. Client calls Booking Service: /book {hotel_id, room_type_id, dates, user_id}.

  3. Booking Service calls Inventory Service to hold rooms.

  4. Inventory Service:

    • Atomically decrements available_rooms in DB using row-level lock.
    • Creates temporary hold in Redis with TTL (10 minutes).
    • Returns hold_id to Booking Service.
  5. Booking Service initiates payment via Payment Service.

  6. User completes payment on gateway.

  7. Payment Gateway calls callback URL with success status.

  8. Payment Service verifies callback and notifies Booking Service.

  9. Booking Service confirms booking:

    • Calls Inventory Service to finalize (remove hold, persist booking).
    • Inserts booking record in Booking DB.
  10. Emit booking confirmation event to message queue.

  11. Notification Service sends confirmation email/SMS to user.

5.3 Failure Handling

  • Payment Timeout: Hold TTL expires → auto-release rooms in Redis.
  • Payment Failure: Mark booking as CANCELLED, release rooms.
  • Double-Booking Prevention: Use DB row-level locking or optimistic concurrency control (version field).

5.4 Cancellation Flow

  1. User requests cancellation → Booking Service validates cancellation policy.

  2. If allowed:

    • Update booking status to CANCELLED.
    • Increment available_rooms in Inventory DB.
    • Initiate refund via Payment Service.
    • Invalidate search cache for affected dates.

6) Deep Dive A: Inventory Management & Overbooking Prevention (~10 mins)

Problem

Multiple users may attempt to book the last available room concurrently. We must prevent overbooking while maintaining high throughput.

Challenges

  • Race Condition: Two users check availability (5 rooms available) → both book → oversell.
  • Distributed Systems: Multiple Inventory Service instances need coordination.
  • Performance: Locking should not bottleneck high traffic.

Solution: Two-Phase Booking with Locking

The key insight is to combine fast in-memory holds with persistent database confirmation to achieve both performance and correctness.

Phase 1: Temporary Hold (Redis)

  • When: User initiates booking (clicks "Book Now").

  • Action: Atomically decrement available_rooms in Redis using Lua script. Redis executes Lua scripts atomically - the entire script runs as a single operation without any other commands interleaving.

  • TTL: Hold expires in 10 minutes if payment not completed.

  • Benefit: Fast, in-memory operation; auto-releases on timeout.

  • Why Redis?:

    • Ensures atomicity
    • TTL automatically handles abandoned bookings (user closes browser, payment times out)
    • Low latency (~1-2ms) for high throughput

Flow:

  1. User clicks "Book Now" → Booking Service calls Inventory Service
  2. Inventory Service executes Lua script on Redis (atomic check-and-decrement)
  3. If successful: Create hold record with TTL, return hold_id to user
  4. If failed: Return "Room unavailable" error
  5. User proceeds to payment page (has 10 minutes to complete)

Phase 2: Confirm Booking (DB)

  • When: Payment succeeds (payment gateway callback received).
  • Action: Persist booking in DB using transaction with row-level lock.
  • Pessimistic Locking: FOR UPDATE ensures no other transaction can modify the same inventory row

Flow:

  1. Payment gateway sends success callback → Payment Service → Booking Service
  2. Booking Service starts DB transaction with FOR UPDATE (acquires row lock)
  3. Re-check availability in DB (defense against Redis failures or race conditions)
  4. If still available: Decrement DB count, insert booking record, release Redis hold
  5. If unavailable: Rollback, refund payment (rare edge case)
  6. Commit transaction (releases lock)

Why Two Phases?

  • Phase 1 (Redis): Fast optimistic lock for user experience (no user wants to wait for DB during seat selection)
  • Phase 2 (DB): Final pessimistic lock for correctness (ensures money and inventory are consistent)
  • Separation of concerns: Redis handles ephemeral holds, DB handles permanent records

Two-Phase Booking Flow Diagram

Architectural Diagram Locked

SQL Example:

sql
BEGIN TRANSACTION;
SELECT available_rooms FROM Inventory 
WHERE hotel_id = ? AND room_type_id = ? AND date = ? 
FOR UPDATE;  -- Row-level lock

UPDATE Inventory 
SET available_rooms = available_rooms - 1 
WHERE hotel_id = ? AND room_type_id = ? AND date = ? 
AND available_rooms > 0;

-- If update count = 0, rollback (race condition detected)
COMMIT;

Lua Script for Atomic Hold (Redis)

lua
-- Check if enough rooms available
local available = redis.call('GET', 'inventory:' .. hotel_id .. ':' .. room_type .. ':' .. date)
if tonumber(available) >= num_rooms then
  redis.call('DECRBY', 'inventory:' .. hotel_id .. ':' .. room_type .. ':' .. date, num_rooms)
  redis.call('SETEX', 'hold:' .. hold_id, 600, num_rooms)  -- 10 min TTL
  return 1  -- Success
else
  return 0  -- Insufficient rooms
end

Alternative: Optimistic Concurrency Control

Instead of row-level locks, use a version number approach:

Step 1: Read current state

SELECT available_rooms, version 
FROM Inventory 
WHERE hotel_id = 'h123' AND room_type_id = 'r456' AND date = '2024-01-15';

--Returns: available_rooms = 5, version = 42

Step 2: Update with version check

UPDATE Inventory 
SET available_rooms = available_rooms - 1,
    version = version + 1
WHERE hotel_id = 'h123' 
  AND room_type_id = 'r456'
  AND date = '2024-01-15'
  AND version = 42  -- Only succeed if version hasn't changed
  AND available_rooms > 0;

--Check rows affected:
--1 row affected → Success(you got the booking)
--0 rows affected → Conflict(someone else modified it, retry)

Why it prevents race conditions:

  • User A reads version = 42, tries to update WHERE version = 42 → SUCCESS(version now 43)
  • User B reads version = 42, tries to update WHERE version = 42 → FAILS(version is now 43)
  • User B retries with new version

Trade-offs:

  • Pro: No locking overhead, better concurrency
  • Con: Requires retry logic, can have high contention during peak times
  • Best for: Low - conflict scenarios(OCC assumes conflicts are rare)

7) Deep Dive B: Search Optimization & Caching(~8 mins)

Problem

With 1200 searches / sec(peak 5000 / sec), querying the DB for every search is expensive and slow.

Solution: Multi - Layer Caching

Layer 1: Redis Cache(Hot Queries)

- ** Key **: `search:{location}:{check_in}:{check_out}:{filters_hash}`
  • Value: List of hotel IDs with availability and pricing.
  • TTL: 5-10 minutes (balance freshness vs cache hit rate).
  • Cache Invalidation: On inventory update (hotel partner changes pricing/availability).

Layer 2: Elasticsearch (Full-Text Search)

  • Purpose: Fast filtering by location, amenities, rating, price range.
  • Indexing: Hotel metadata indexed with geo-coordinates.
  • Query: Geo-proximity search + filters.
  • Update: Near real-time indexing (1-2 sec delay acceptable).

Layer 3: Pre-Aggregation (Popular Routes)

  • Strategy: For popular destinations (e.g., Goa, Dubai), pre-compute hotel lists for common date ranges.
  • Storage: Store in cache with longer TTL (1 hour).
  • Refresh: Periodic background job updates cache.

Caching Architecture

Architectural Diagram Locked

Search Ranking Algorithm

score = w1 * relevance_score          // location match
      + w2 * (1 / price)               // price preference
      + w3 * avg_rating                // user reviews
      + w4 * availability_premium      // more rooms = higher score
      + w5 * user_preference_match     // personalization

Personalization

  • Track user's past bookings, searches, and preferences.
  • Boost hotels matching user's preferred amenities (e.g., pool, gym).

8) Deep Dive C: Payment & Idempotency (~7 mins)

Problem

Payment processing is async and may fail, timeout, or be retried. We must ensure exactly-once semantics.

Challenges

  • Double Charging: User clicks "Pay" multiple times.
  • Network Failure: Payment succeeds at gateway but callback fails.
  • Retry Storm: Client retries on timeout, causing duplicate requests.

Solution: Idempotency Key

Implementation

  1. Client generates idempotency key (UUID) on first payment request.
  2. Payment Service checks if key exists in Idempotency Store (Redis/DB).
    • If exists: Return cached response (no-op).
    • If new: Process payment and store result with key.
  3. Store state machine per key:
    • INITIATED → PROCESSING → SUCCESS → NOTIFIED / FAILED.

State Machine Diagram

Architectural Diagram Locked

Idempotency Store Schema

json
{
  "idempotency_key": "uuid-123",
  "booking_id": "booking-456",
  "status": "SUCCESS",
  "payment_id": "pay-789",
  "amount": 5000,
  "created_at": "2023-10-15T10:00:00Z",
  "response": { ... }  // Cached response
}

Webhook Signature Verification

  • Payment gateway signs callback with secret key.
  • Payment Service verifies signature to prevent spoofing.

Refund Handling

  • On cancellation, create refund request with idempotency key.
  • Track refund status separately: PENDING → PROCESSED.

9) Scaling & Performance (~5 mins)

Horizontal Scaling

  • Search Service: Stateless; scale with load balancer.
  • Inventory Service: Partition by hotel_id (consistent hashing).
  • Booking Service: Stateless; scale horizontally.
  • DB: Shard by hotel_id or geography (e.g., US-West, EU, Asia).

Database Partitioning

  • Inventory DB: Shard by (hotel_id, date) to distribute load.
  • Booking DB: Shard by booking_id or user_id.
  • Hot Partition Problem: Popular hotels (e.g., Taj Mahal Hotel) → use replica reads.

Caching Strategy

  • Read-Heavy: Hotel metadata, reviews → Cache in CDN and Redis.
  • Write-Heavy: Inventory updates → Write-through cache with invalidation.

CDN for Static Assets

  • Hotel photos, videos → store in S3, serve via CloudFront.

Performance Metrics

  • P99 Search Latency: < 500ms.
  • P99 Booking Latency: < 2s.
  • Cache Hit Rate: > 80% for searches.

10) Failure Modes & Recovery

Database Failure

  • Read Replica Failover: Promote replica to master.
  • Write Failures: Queue writes in Kafka, replay after recovery.

Redis Failure

  • Search Cache: Degrade to Elasticsearch (slower but functional).
  • Inventory Holds: Fallback to DB-only locking.

Payment Gateway Outage

  • Queue Payments: Store in message queue, retry when gateway recovers.
  • Fallback Gateway: Integrate multiple gateways (Stripe + PayPal).

Inventory Sync Issues

  • Reconciliation Job: Nightly job compares Redis vs DB inventory counts.
  • Audit Logs: Track all inventory changes for forensic analysis.

Geo-Redundancy

  • Multi-region deployment (US, EU, Asia).
  • DNS-based routing to nearest region.

11) Trade-offs & Alternatives

Eventually Consistent vs Strongly Consistent Inventory

  • Strong: Use DB locks; slower but no overbooking.
  • Eventual: Use Redis; faster but requires reconciliation.
  • Choice: Hybrid (Redis for holds, DB for confirms).

SQL vs NoSQL

  • SQL: ACID guarantees for bookings and payments.
  • NoSQL: Better for hotel metadata (flexible schema).
  • Choice: SQL for transactional data, NoSQL for metadata.

Synchronous vs Asynchronous Booking

  • Sync: Immediate confirmation, better UX.
  • Async: Decouple payment from booking, better resilience.
  • Choice: Async with optimistic UI updates.

12) Security & Compliance

Payment Security

  • PCI-DSS Compliance: No card data stored; use tokenization.
  • TLS: All communication encrypted.

User Data Protection

  • GDPR: User consent for data collection, right to deletion.
  • Encryption: Encrypt PII (email, phone) at rest.

Rate Limiting

  • Prevent scraping and DDoS attacks.
  • Use API keys for partners.

Fraud Detection

  • Monitor for unusual booking patterns (e.g., 100 bookings in 1 minute).
  • Use CAPTCHA for suspicious activity.

13) Interview Time Allocation (45 min)

  • 5 min: Requirements & scope (functional, non-functional, scale).
  • 10 min: HLD & architecture diagram (components, data flow).
  • 5 min: Data model & key flows (search, booking).
  • 10 min: Deep dive on inventory management & overbooking prevention.
  • 8 min: Deep dive on search optimization & caching.
  • 5 min: Scaling, failure handling, and trade-offs.
  • 2 min: Security, Q&A, and wrap-up.

14) Summary

  • Core Challenges: Inventory consistency (overbooking prevention), search optimization (caching), payment reliability (idempotency).
  • Key Components: Search Service (Elasticsearch + Redis), Inventory Service (Redis holds + DB confirms), Booking Service (orchestration), Payment Service (idempotency + async callbacks).
  • Scaling Strategy: Horizontal scaling, DB partitioning by hotel_id, multi-layer caching (Redis + Elasticsearch + CDN).
  • Correctness: Two-phase booking (Redis hold + DB confirm), row-level locking, idempotency keys for payments.

This design handles millions of searches and bookings per day while ensuring no overbooking and consistent user experience.

ResumeSkool Logo

No-Nonsense AI Resume
Building Platform for Free

©2026 - ResumeSkool