System design sample solution

System design: search hotel availability

A step-by-step system design answer you could actually give in 60 minutes.

System design interview · Sample 60-minute answer · Not Booking.com's actual design · Independent, not affiliated with Booking.com

The system design question

Design a hotel search and availability system. Given a city and a date range, return the hotels with rooms available for every night, and make sure it holds up under high traffic.

Agree on the scope (5 minutes)

Say up front what you're covering.

We'll designWe'll skip
Searching a city for a date rangeThe booking and payment flow
Checking every night of the stayRanking and personalization
Keeping availability up to date as rooms sellMaps and filters like price or stars
Staying fast during traffic spikesReviews and photos

Then agree on what "good" means:

  • Fast: search results come back in a few hundred milliseconds, even for big cities.
  • Correct enough: results can be a few seconds stale, but we check again for real when someone books.
  • Holds up under load: a summer holiday rush shouldn't slow search down or touch the booking database.
  • Every night counts: a hotel only shows up if it has a room free for every night of the stay.

Rough numbers (5 minutes)

These are assumptions, made up but reasonable: 1 million properties, about 20 room types each, availability kept for a year ahead, 100 million searches a day with peaks at 10 times the average, and 50 million availability changes a day from bookings and hotels updating their rooms.

~1.2K
searches per second, on average
~12K
searches per second at a peak
~600
availability changes per second
~7B
room type and night combinations
QuestionRough mathWhat it means
How much availability data?1 million properties × 20 room types × 365 nightsAbout 7 billion rows. At about 20 bytes each that's around 150 GB, which fits in memory across a handful of machines
How much work is one search?A big city with 3,000 hotels × 20 room types × 3 nightsAbout 180K checks per search, way too many to run against a database 12K times a second
Reads versus writes?12K searches a second against 600 changes a secondReads win by a lot, so it's worth doing extra work on each write to make reads cheap

The overall design (10 minutes)

Inventory databasesource of truthChange streamevery room changeAvailability indexin memory, by citySearch servicefilter and rankGuestcity and dates
Every change to rooms goes into a stream, which keeps an in-memory search copy up to date. Searches only ever read that copy.
  1. The inventory database holds how many rooms of each type are free on each night. Bookings and hotel admins write to it, and nothing else does.
  2. Every change to it gets published to a change stream, like 'Hotel 42, double room, June 3: 4 left'.
  3. Availability index servers read that stream and keep a compact copy of availability in memory, split up by city.
  4. A search goes to the servers holding that city. They check every hotel against every night of the stay and return the ones that pass.
  5. The search service adds hotel details from a cache, ranks the list, and sends back the first page.

What the apps need to do

ActionWho does itWhat's sent
SearchGuestCity, check-in, check-out, number of guests, page
Get a hotel's rooms for datesGuestHotel ID, check-in, check-out
Update rooms or pricesHotel adminHotel ID, room type, nights, rooms free, price
Availability changedInternal streamHotel ID, room type, night, rooms free, price

What we store and where

DataWhere it goesWhy
Rooms free per room type per nightRelational database, split by hotelThe source of truth that bookings check against
Availability indexIn memory on index servers, split by citySo a search can check thousands of hotels without a database call
Hotel details like name and photosA cache in front of a hotel databaseChanges rarely and gets read on every search
Popular search resultsShort-lived cacheTakes the edge off spikes for the same city and dates

Step 1: Checking every night fast (12 minutes)

The tricky part is 'every night'. A hotel with rooms free on June 3 and 5 but not June 4 has to drop out for a June 3 to 6 stay. If you think of each hotel as a row and each night as a column, you need the hotels whose cells are all 'yes' for the columns in the range.

So for each hotel, the index keeps one small number per night: the most guests any free room type could fit, or zero if it's sold out. A search loops over the hotels in the city and checks just those few nights. For 3,000 hotels and a 3-night stay that's about 9,000 lookups in memory, which takes well under a millisecond.

Keep it as a plain array per hotel, indexed by days from today. Checking a range is then just reading a few numbers next to each other. You can get fancier with bitmaps, where each night is one bit and you combine them in one step, but a simple array is fast enough to start.

Why not just ask the database?

You could, with a query that groups by hotel and checks the count of free nights matches the stay length. It works for a small site. At 12K searches a second, each touching thousands of rows, the database would melt, and it's also the thing bookings depend on. Keeping search off it protects bookings. That's a classic case for picking the storage that matches how you read, which is the point of relational vs non-relational thinking.

Step 2: Keeping availability fresh (10 minutes)

When a room sells, the booking writes to the inventory database, and the change goes onto a stream. Index servers read the stream and update their arrays. Most changes show up in search within a second or two.

That means search can be slightly wrong. Someone might see a hotel that sold its last room two seconds ago. That's fine, because the booking step checks the database for real and won't let it go through. We're picking speed for search and strict correctness for booking, a trade-off you'll see described under consistency.

If an index server restarts, it loads a recent snapshot of its cities and then replays the stream from where the snapshot left off. Each change carries the full new value, like '4 left', so replaying one twice does no harm.

Step 3: Handling traffic spikes (8 minutes)

Split the index by city, a lot like sharding, and keep several copies of each split. Busy cities like Paris or London get more copies, so a rush there just means sending searches to more servers.

Lots of people search the same city and dates, especially around holidays. A short-lived cache of results, say 30 seconds, keyed by city, dates and guests, absorbs a lot of that. A short expiry keeps it from going too stale.

If things still get overloaded, cut back on the extras first, like fancy ranking or showing prices for every room, before you start rejecting searches.

Choices you should be able to defend

We choseInstead ofBecauseWe'd reconsider if
A separate in-memory search indexQuerying the bookings databaseSearch traffic can't slow down bookingsTraffic was small enough for one database
Search a second or two staleAlways exact search resultsExact results would need every search to hit the source of truthNever for search, since booking checks again anyway
Splitting the index by citySplitting by hotelA search only ever touches one city, so it goes to one group of serversSearches often spanned many cities, like a whole region
Short cache on popular searchesNo cache, or a long oneTakes the edge off spikes without showing old data for longHit rates were too low to bother

What could go wrong (5 minutes)

What happensWhat we do about it
An index server diesOther copies of that city keep serving. A new one loads a snapshot and catches up from the stream
The change stream falls behindSearch gets more stale but keeps working. Booking still checks the database, so nobody gets a room that isn't there
A guest books a room search said was free, but it's goneThe booking fails cleanly, and we show other rooms at that hotel or nearby
A huge holiday spikeAdd index copies for busy cities, lean on the results cache, and drop extras before rejecting searches
A hotel bulk updates a year of roomsThat's a lot of stream events, so we batch them per hotel and apply them in one go

Follow-up questions (5 minutes)

How would you add a price filter?

Store the cheapest price per night next to the availability number, and add them up over the stay while you check. Then filtering by total price is one more comparison.

What about stays longer than the index covers?

Cap search at something like 30 nights and a year ahead. Longer stays go to a slower path that asks the database directly.

How would search show a hotel that has rooms if you switch room types mid-stay?

Most sites don't offer that by default. If we did, the check becomes 'some room free each night' instead of 'one room type free every night', which our per-hotel number already handles.

How the 60 minutes add up

MinutesWhat you're doing
0–5Agree on what you're building
5–10Rough numbers
10–20Draw the overall design
20–32Checking every night fast
32–42Keeping availability fresh
42–50Handling traffic spikes
50–55What could go wrong
55–60Follow-up questions

Key concepts used here

Further reading

We wrote this sample answer ourselves.

Practice Loop is independent and isn't affiliated with or endorsed by Booking.com.