Hotel Reservation Concurrency
Understand concurrency control in a hotel reservation system. Learn how inventory check, pessimistic locking, optimistic locking, idempotent payments, ledger logging
Continue with the guided workspace
Use the paired workspace to connect this guide's mental model with live component state, visible signals, and the outcome each step or scenario is meant to prove.
Designing a hotel reservation system requires addressing the core challenge of double-booking under high concurrency.
Mental Model
When multiple customers try to book the last available room simultaneously, a system with inadequate isolation will suffer from overbooking. The system coordinates booking requests using either optimistic locking (detecting conflicts via version checks) or pessimistic locking (blocking concurrent updates via database row locks). When a reservation is requested, the service checks the cache, acquires a row lock in the transactional database for pessimistic locking (or checks a version for optimistic locking), and creates a pending booking record. The system then processes the payment using an idempotency key to prevent duplicate charges. If the payment succeeds, the transaction commits, updates the read cache, and writes to an audit ledger; if payment fails, the database transaction rolls back, releasing locks and restoring cached inventory.
Components and Why They Exist
Customer Browser
The client-side entry point representing the user searching for rooms and initiating bookings. Submits search queries, reservation requests, and payments to the API gateway. In the demo, watch it fire parallel checkout requests to see how concurrency conflicts are resolved.
API Gateway
The single entry point that routes client requests, performs rate limiting, and handles authentication. Directs reads to the query model (Elasticsearch) and writes to the reservation service. In the demo, watch it dispatch checkouts and search requests.
Redis Cache
Improves search performance by caching room availability grids (Cache-aside pattern). It is not the source of truth for reservation locking; the database row lock or version check protects the booking write. In the demo, watch availability counts drop and lock status update.
Reservation Service
Coordinates the booking lifecycle, transaction checks, locking strategies, and third-party integrations. Checks cache, requests database locks, submits payments, logs to audit ledgers, and manages rollbacks. In the demo, watch it coordinate the workflow and transition reservation states.
Database (RDBMS)
The transactional source of truth for booking records, requiring strict ACID compliance. Implements row locking (e.g. SELECT FOR UPDATE for pessimistic locking) and version-based checks (for optimistic locking) to protect booking rows. In the demo, watch row lock state or row version change.
Elasticsearch (CQRS Read View)
Scalable read model to handle heavy room-search queries. Stores denormalized room information, updated asynchronously from the primary database via CDC (Change Data Capture). In the demo, observe how reads bypass the primary DB and sync asynchronously after database commits.
Payment Gateway
Handles third-party credit card processing securely. Processes payments and requires an idempotency key to prevent duplicate charges. In the demo, watch payment processing status and notice how rollbacks trigger if a payment fails.
Audit Ledger
Keeps a double-entry transaction record for financial reconciliation. Appends immutable, append-only logs for all successful and reversed reservations. In the demo, watch entries added to the ledger after a booking is finalized.
Notification Service
Notifies users about their booking results asynchronously. Sends confirmation emails or push notifications asynchronously once a reservation is committed. In the demo, watch confirmation emails trigger after successful ledger commits.
Edge Cases and Misconceptions
- Misconception: Caching availability guarantees double-booking protection: Redis is fast but usually not the transaction source of truth. If Redis cache availability says a room is free, the reservation service must still secure a database lock (optimistic or pessimistic) on the primary database to guarantee liveness under contention.
- Misconception: Optimistic locking is always superior to pessimistic locking: Optimistic locking is great for low contention because it avoids database lock overhead. However, under high contention, optimistic locking will cause many transactions to fail and retry, degrading performance. Pessimistic locking is better suited for high contention.
- Edge Case: Payment Success with DB Failures: If payment succeeds but the server crashes before committing the reservation to the database, a severe inconsistency occurs. Idempotent retries and write-ahead logs help ensure the database eventually catches up, or the payment is refunded.
Common Questions
What is the difference between pessimistic and optimistic locking in reservations?
Pessimistic locking locks the database row immediately (e.g., SELECT FOR UPDATE) to block other transactions, suitable for high contention. Optimistic locking checks a version column during update without holding locks, failing the transaction if a concurrent update modified the version first.
Why is an idempotency key critical for payment processing?
If a network error occurs, clients might retry a payment. An idempotency key ensures the payment gateway processes the charge exactly once, returning the cached response for subsequent retries instead of double-charging.
How does the system recover if a payment is declined?
The reservation service rolls back the database transaction, releases any active row locks, updates the Redis cache to restore inventory availability, and returns an error response to the client.
Why is CQRS used in reservation systems?
CQRS separates high-throughput search queries from write transactions. Room searches are directed to Elasticsearch or Redis, while write transactions go directly to the primary relational database, preventing search traffic from overwhelming inventory writes.