ayushsalampuriya.xyz Revise

System Design · Design Question

Design a Booking System

Hotels / tickets / appointments: inventory, holds, payments, and how to avoid overselling under concurrency.

In simple words: Search can be slightly stale, but a final hold must reserve inventory atomically so two buyers cannot take the same unit.

Concrete example: Two users booking the last deluxe room for the same night both pass search, but only one hold succeeds because the hold API decrements inventory inside a transaction or uses optimistic locking on the room-night row.

1. Requirements

Functional

Non-functional

Back-of-envelope

1M bookings/day ≈ 12/s average; a launch can peak above 500/s. If each booking causes 100 availability searches, reads average ~1.2k/s and dominate traffic.
Pick a concrete domain in interview: "hotel room nights" Ask: unique rooms vs pooled room-type inventory? overbooking %?

2. API

GET /v1/availability?hotelId=&checkIn=&checkOut=&guests= POST /v1/holds { hotelId, roomTypeId, checkIn, checkOut, customerId } → { holdId, expiresAt, price } POST /v1/bookings { holdId, paymentMethod, Idempotency-Key } → { bookingId, status } POST /v1/bookings/{id}/cancel

3. Data model

hotels, room_types(room_type_id, hotel_id, capacity_attrs) inventory(room_type_id, date, total, reserved, held) -- reserved = confirmed; held = soft lock holds(hold_id, room_type_id, dates, expires_at, status) bookings(booking_id, hold_id, user_id, status, amount) UNIQUE constraints / TX protect inventory rows

For assigned seats/rooms, model each unit; for hotels often count-based inventory per room type per night.

4. High-level design

Client → API Gateway → Search Service (read replicas / cache) → Booking Service → Inventory DB (strong consistency path) → Payment Service → Notification queue Hold Expiry Worker releases expired holds

5. Concurrency: do not oversell

-- In one DB transaction: SELECT total, reserved, held FROM inventory WHERE room_type_id=? AND date IN (...) FOR UPDATE IF any day reserved+held+need > total → abort UPDATE inventory SET held = held+need ... INSERT hold expires_at = now()+10min COMMIT

6. Hold → pay → confirm saga

1) Create hold (inventory held++) 2) Charge payment (idempotent) 3) On success: held-- , reserved++ ; booking confirmed 4) On fail/expiry: held-- ; hold cancelled 5) Payment success but confirm crash → reconcile job uses payment id / hold id

7. Search scaling

8. Overbooking (optional product)

Airlines intentionally overbook statistically. If required, raise total effective capacity and define compensation workflow — say it explicitly; do not “accidentally” oversell.

9. Scaling writes

10. Edge cases

11. Failure modes

Interview talking points

  • Lead with: Separate cacheable search from the strongly consistent hold path.
  • Separate search (cacheable) from hold/book (strongly consistent TX).
  • Hold with TTL; worker releases expiry.
  • Protect inventory with row locks or optimistic versions.
  • Saga: hold → pay → confirm; compensate on failure.
  • Idempotency keys on booking and payment.
  • Shard by hotel; watch hot-date contention.
  • Overbooking only as an explicit policy, not a race bug.