Bird
Raised Fist0
LLDsystem_design~25 mins

Reservation and hold system in LLD - System Design Exercise

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Design: Reservation and Hold System
Includes reservation and hold management, timeout handling, user notifications. Excludes payment processing and inventory restocking.
Functional Requirements
FR1: Users can reserve items (e.g., seats, products) for a limited time before purchase.
FR2: Users can place a hold on an item to prevent others from reserving it temporarily.
FR3: The system must automatically release holds after a configurable timeout.
FR4: Users can confirm a reservation to finalize the purchase.
FR5: The system should prevent double booking of the same item.
FR6: Support concurrent users making reservations and holds.
FR7: Provide APIs for creating, confirming, canceling reservations and holds.
FR8: Notify users when their hold is about to expire.
Non-Functional Requirements
NFR1: Handle up to 5,000 concurrent users.
NFR2: API response time p99 under 150ms.
NFR3: Availability target 99.9% uptime (max 8.77 hours downtime/year).
NFR4: Reservation hold timeout configurable between 5 to 30 minutes.
NFR5: Data consistency must prevent double booking (strong consistency).
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
❓ Question 5
❓ Question 6
❓ Question 7
Key Components
API Gateway for client requests
Reservation and Hold Service for business logic
Database for storing reservations and holds
Cache for quick availability checks
Scheduler or background worker for hold expiration
Notification Service for alerts
Authentication Service
Design Patterns
Optimistic or pessimistic locking to prevent double booking
Event-driven architecture for hold expiration
Cache aside pattern for availability data
Timeout and retry mechanisms
Circuit breaker for external notification service
Reference Architecture
Client
  |
  v
API Gateway
  |
  v
Reservation & Hold Service <--> Cache (Redis)
  |
  v
Database (PostgreSQL)
  |
  v
Scheduler / Background Worker
  |
  v
Notification Service
Components
API Gateway
Nginx or AWS API Gateway
Handles client requests, routes to services, enforces authentication
Reservation & Hold Service
Node.js or Python microservice
Manages reservation and hold logic, enforces business rules
Cache
Redis
Stores current availability and hold status for fast reads
Database
PostgreSQL
Stores persistent reservation and hold records with strong consistency
Scheduler / Background Worker
Celery or AWS Lambda scheduled functions
Checks for expired holds and releases them automatically
Notification Service
Push notification system or email service
Sends alerts to users about hold expiration and reservation status
Authentication Service
OAuth 2.0 provider or custom auth
Verifies user identity before allowing reservation actions
Request Flow
1. User sends a request to reserve or hold an item via API Gateway.
2. API Gateway authenticates the user and forwards the request to Reservation & Hold Service.
3. Service checks cache for item availability.
4. If available, service creates a hold or reservation record in the database using a transaction to ensure no double booking.
5. Service updates cache to reflect new hold/reservation status.
6. Scheduler periodically scans database for expired holds and releases them, updating cache accordingly.
7. Notification Service sends alerts to users before hold expiration.
8. User confirms reservation to finalize purchase, service updates database and cache.
9. User can cancel hold or reservation, service updates records and cache.
Database Schema
Entities: - Item (item_id PK, description, total_quantity) - Reservation (reservation_id PK, user_id FK, item_id FK, quantity, status ENUM('held', 'confirmed', 'canceled'), hold_expiry TIMESTAMP, created_at, updated_at) - User (user_id PK, name, contact_info) Relationships: - One Item can have many Reservations. - One User can have many Reservations. Constraints: - Application logic in transactions ensures sum(quantity where status='held' or status='confirmed') <= total_quantity to prevent double booking. - Index on hold_expiry for efficient expiration queries.
Scaling Discussion
Bottlenecks
Database write contention when many users try to reserve the same item simultaneously.
Cache consistency issues between cache and database.
Scheduler load when scanning large numbers of holds for expiration.
Notification service overload during peak expiration times.
Solutions
Use row-level locking or optimistic concurrency control in database to prevent double booking.
Implement cache invalidation strategies and use cache aside pattern to keep cache consistent.
Partition holds by item or time window to distribute scheduler workload; use event-driven expiration with message queues.
Throttle notifications and batch alerts; use scalable push notification services.
Interview Tips
Time: Spend 10 minutes clarifying requirements and constraints, 15 minutes designing components and data flow, 10 minutes discussing scaling and trade-offs, 10 minutes answering questions.
Clarify reservation vs hold differences and timeout behavior.
Explain how to prevent double booking using database transactions and locking.
Describe the role of cache for performance and how to keep it consistent.
Discuss how scheduler automates hold expiration and user notifications.
Highlight scalability challenges and solutions for concurrency and notification spikes.

Practice

(1/5)
1. What is the primary purpose of a hold in a reservation and hold system?
easy
A. To delete all reservations from the system
B. To permanently reserve a resource without expiration
C. To cancel a confirmed reservation immediately
D. To temporarily block a resource before final booking

Solution

  1. Step 1: Understand the role of a hold

    A hold temporarily blocks a resource to prevent others from booking it while the user decides.
  2. Step 2: Differentiate hold from reservation

    A reservation is permanent until canceled, while a hold expires if not confirmed.
  3. Final Answer:

    To temporarily block a resource before final booking -> Option D
  4. Quick Check:

    Hold = Temporary block [OK]
Hint: Holds are temporary blocks, not permanent reservations [OK]
Common Mistakes:
  • Confusing hold with permanent reservation
  • Thinking holds never expire
  • Assuming holds cancel reservations
2. Which data structure is best suited to track holds with expiration times efficiently?
easy
A. Simple array without ordering
B. Linked list without timestamps
C. Hash map with timestamps and a priority queue for expirations
D. Stack data structure

Solution

  1. Step 1: Identify requirements for hold tracking

    We need fast lookup by hold ID and efficient expiration handling.
  2. Step 2: Choose data structures

    A hash map allows quick hold lookup; a priority queue orders holds by expiration for timely removal.
  3. Final Answer:

    Hash map with timestamps and a priority queue for expirations -> Option C
  4. Quick Check:

    Hash map + priority queue = efficient hold tracking [OK]
Hint: Use hash map for lookup and priority queue for expirations [OK]
Common Mistakes:
  • Using unordered arrays causing slow expiration checks
  • Choosing stack which is LIFO, not suitable for expirations
  • Ignoring timestamps in data structure
3. Consider this pseudo-code for confirming a hold:
if hold.exists(hold_id) and not hold.is_expired(hold_id):
    reservation.create(hold.resource)
    hold.remove(hold_id)
    return "Confirmed"
else:
    return "Failed"
What will be the output if the hold has expired?
medium
A. "Failed"
B. "Confirmed"
C. Error due to missing hold
D. "Confirmed" but resource is double booked

Solution

  1. Step 1: Check hold existence and expiration

    The code confirms only if hold exists and is not expired.
  2. Step 2: Analyze expired hold case

    If hold is expired, condition fails and returns "Failed" without creating reservation.
  3. Final Answer:

    "Failed" -> Option A
  4. Quick Check:

    Expired hold = "Failed" confirmation [OK]
Hint: Expired holds cause confirmation to fail [OK]
Common Mistakes:
  • Assuming expired holds confirm successfully
  • Expecting errors instead of failure message
  • Ignoring hold expiration check
4. A developer wrote this code to release expired holds:
for hold in holds:
    if hold.expiration_time < current_time:
        holds.remove(hold)
What is the main issue with this code?
medium
A. Holds should not be removed, only marked expired
B. Modifying a list while iterating causes skipped elements or errors
C. Expiration time comparison is incorrect
D. Loop should use while instead of for

Solution

  1. Step 1: Understand iteration and modification

    Removing items from a list while iterating over it causes skipping or runtime errors.
  2. Step 2: Identify correct approach

    Use a separate list to collect expired holds or iterate over a copy to safely remove.
  3. Final Answer:

    Modifying a list while iterating causes skipped elements or errors -> Option B
  4. Quick Check:

    Remove during iteration = skipped elements [OK]
Hint: Never remove items from list while looping over it [OK]
Common Mistakes:
  • Ignoring iteration modification side effects
  • Assuming expiration comparison is wrong
  • Thinking loop type causes the issue
5. You need to design a scalable reservation and hold system for a popular event with thousands of simultaneous users. Which approach best ensures no double booking and timely hold expiration?
hard
A. Use distributed locks on resources, store holds with TTL in a distributed cache, and confirm with atomic transactions
B. Store all holds in a single database table without expiration, confirm by updating status
C. Allow multiple holds per resource and resolve conflicts manually later
D. Use client-side timers to expire holds and update server asynchronously

Solution

  1. Step 1: Prevent double booking with distributed locks

    Distributed locks ensure only one user can hold a resource at a time across servers.
  2. Step 2: Use TTL in distributed cache for hold expiration

    TTL automatically expires holds after timeout, preventing indefinite blocking.
  3. Step 3: Confirm holds atomically

    Atomic transactions guarantee reservation creation without race conditions.
  4. Final Answer:

    Use distributed locks on resources, store holds with TTL in a distributed cache, and confirm with atomic transactions -> Option A
  5. Quick Check:

    Distributed locks + TTL + atomic confirm = scalable, safe system [OK]
Hint: Combine distributed locks, TTL cache, and atomic confirm for scale [OK]
Common Mistakes:
  • Ignoring concurrency causing double booking
  • Relying on client-side expiration only
  • Not using atomic operations for confirmation