Enhancement:
Assuming we have 1M reservations per day.
Each reservation entity takes around 1 KB storage, the total storage required for reservation is 1GB. 5 year ~ 2TB
Assuming 100 searches lead to one reservation on average, it would be 10M searches per day,
10 * 10^6 / 86400 ~= 1000 searches per second.
read : write ratio ~ 10 : 1
api/hotels/search/v1/?filter=location=XXXdates=XXX&minScore=XXX&brand=XXX
{
method: GET
}
response:
{
hotels: [{
id,
name,
brand,
rooms: {
[{
room_type,
room_name,
capacity,
price,
}]
}
}]
}
api/book/v1
{
method: POST
body: {
hotelId,
roomId,
price
}
}
response:
{
id,
hotelId,
roomId,
price,
status
}
api/user/reservations/v1
{
method: GET
}
response
{
[{
reservation_id,
hotel_id,
room_id,
price,
status
}]
}
api/payment/v1
{
method: POST
body: {
reservationId
}
}
response {200}
HotelId | GeoHash | Name | Rating | Brand
Hotels info is pretty static, and the overall hotels count is not huge, we could we SQL here. If need to scale, we could shard based on GeoHash.
RoomId | HotelId | Date | Price | Stock |
Since we don't want to oversell rooms, the room stock table need to be strong consistent, and support transactions, so use SQL DB.
Hotel Id | Hotel Info
RoomId | Room Metadata
Could use document DB like mongoDB as we want more flexibility on info/metadata, they should be schemaless
User Id | Month_Bucket | Created At | Hotel Id | Room Id | Dates | Status |
Wide column DB like Cassandra to support high throughput. Partition key User id + Month_bucket, clustered by Created At.
Payment Id | User Id | Reservation Id | Created At
SQL, Indexed based on User Id
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
To support multiple filter like location + ratings + brand, .etc, we could use SQL query by combining the filters. But it's inefficient even we build index for each column. To speed up, we could use ElasticSearch, where we could build indices for each filter and dramatically increase query speed. To scale up further, we could add cache for (query, hotels) using Redis with an expiration.
After we get a list of hotels, the second level search is check if each hotel has enough room on the specific date range. We could cache room stock info into Redis to avoid reading DB directly. To scale up, we can shard based on hotel id, and query for the same hotel will always routed to the same cache.
With the second level search for rooms, we would return a list of hotels, and for each hotel it contains a list of available rooms to the client.
After client receive the hotel and room info, the user could pick a room to book.
At this point, we create a reservation with the hotel and room id, status is PENDING. As mentioned above, we have a Redis cache for room stock info between the hotel search service and Room Stock DB. Whenever the reservation is created, we update the cache Room Stock count. E.g if the user book two rooms, the cache will decrease in Stock Room by 2. At the same time, we create an expiration key with user id, locked count and reservation id. The key will expire after 30 minutes, when it expires, we check if the reservation status is still PENDING, if so, we add back locked count to room stock count, and mark reservation as TIME_OUT. If the status is PAID, do nothing.
For following query by other users, if found the cache Room Stock info Stock == 0, we won't return the room info anymore.
User could pay for reservation after lock down a room. We could take use of some third party payment system like Stripe/Plaid. We create an payment with reservation info, user info and price, then user pay the order. We could have a Webhook service to receive payment updates. If the payment for a reservation succeed, we update the reservation status to PAID and also send a task to MQ to update the Room Stock DB stock.
We use SQL to store hotels info as it has fixed schema and we could query with multiple creteria. To scale up and make complex query faster, we could upgrade to use ElasticSearch.
We use GeoHash to store location info as it's much more efficient to query based on location. (No need to calculate distance between lat, lon)
We use Redis as cache layer for Room Stock info as it supports atomic updates (using Lua script), which could avoid oversell. It also supports key with expiration to achieve "hold" state for reservation. We could also save a lot DB reads to the Room Stock DB.
If hotel query volume is super high, it would be a bottleneck even if we use ElasticSearch. We could add cache for query result with LRU eviction. To scale further, we could shard based on hotel GeoHash.
The Redis cache for Room Stock could be broken. Redis has built in cluster support, whenever the master node goes down, we could replace with slave node. If there a tons of expiration keys in a single Redis node, it could be a bottleneck, we could improve by sharding on hotel id or hotel id + room id.
There could also be case where a payment for a reservation succeed, but the lock for the reservation already expired and the original room was taken. In such case, we could firstly check if there is still stock for the same room type, if not we need to send notification to users and offer refund and alternative rooms.