Airbnb / Booking
Search listings by location/dates and book without double-booking.
Open the interactive version → diagrams, practice & moreRequirements
Functional
- Geo + availability search
- Booking with no overlap
- Pricing/calendar
- Reviews
Non-functional
- Consistent availability
- Fast search
Scale
Millions of listings
The approach
Geo+attribute search index for discovery (eventual OK); bookings go through a transactional availability check (no overlapping reservations) with holds; calendar per listing.
Key components
Search index (geo) · listing DB · transactional booking service
Numbers that matter
- ~7M active listings on Airbnb globally; geo-indexed search must return relevant results in <100ms P99 for a given location+date filter.
- ~15-minute hold TTL is the standard window between 'guest selects dates' and 'payment confirmed' — shorter hurts conversion, longer increases ghost-booking risk.
- ~10× spike factor for booking demand around holidays — the booking transaction tier must handle this without deadlocks or timeout cascades.
- ~2-3 availability queries per search result page as users refine dates — the availability check must be read-optimized and cached where possible.
Senior deep-dive
Search and booking are architecturally separate consistency domains — search is eventually consistent (showing a listing that was just booked is tolerable); the booking transaction must be strictly serializable (double-booking is catastrophic and unrecoverable).
Calendar availability is a specialized interval problem — naive row-per-date doesn't scale; you need an interval-based availability model (blocked ranges, not individual dates) with a smart overlap query.
Hold-then-confirm is the transactional primitive: a time-boxed lock (reservation hold, say 15 minutes) lets the guest fill payment details without holding a DB lock indefinitely — the hold expires atomically if payment fails.
Availability model: intervals, not daily rows
A naive model stores one row per available date: 365 rows per year per listing. Checking 500 listings for a 7-day stay in August means 3,500 row lookups. The interval model stores blocked ranges (`(listing_id, start_date, end_date, reason)`) and checks availability by querying for overlapping intervals: `WHERE listing_id = X AND NOT (end_date <= :checkin OR start_date >= :checkout)`. This is a much smaller table (bookings per listing, not days per listing) and a simple two-condition overlap check that indexes cleanly on `(listing_id, start_date)`.
The hold pattern: time-boxed reservation before payment
You cannot hold a DB lock for the 5-10 minutes a user spends entering payment details. The pattern: at 'Book Now' click, create a hold record in the DB (`status=held, expires_at=now+15m`) — this is a lightweight row that blocks other bookings from the same dates. A background job sweeps expired holds and releases them. On payment success, atomically update `status=confirmed`. On payment failure or timeout, the sweep releases the hold. The critical invariant: two holds for the same dates must be mutually exclusive — enforced by a unique constraint or serializable transaction at hold creation time.
Search consistency: eventual is fine, booking is not
When a listing is booked, it takes seconds to minutes for the availability index (Elasticsearch) to reflect the change. A guest might see the listing as available in search results, click through, and find it unavailable. This is expected behavior — Airbnb shows 'this listing is no longer available for your dates' at the booking attempt. The search index is optimized for speed and recall, not consistency. Eventual consistency in search is a conscious product decision: the alternative (synchronous index updates on every booking) adds latency to the hot-path transaction.
Geo search: what the listing index must support
Airbnb's discovery query is: 'listings in this bounding box, available for these dates, with these amenities, sorted by relevance.' Geo query + date filter + attribute filter must compose efficiently. Elasticsearch handles the geo + attribute part; date availability is an additional filter that the search layer applies against the availability service (or a pre-indexed availability bitmap). The challenge: pushing date availability into the search index requires rebuilding/updating index fields on every booking, which is expensive. The pragmatic split: geo+attributes in Elasticsearch, availability cross-checked from the availability service post-retrieval.
Pricing: not a static field
Listing prices are dynamic: base price + seasonality + demand-based surge + host-set special pricing for specific dates. Storing a single price field per listing is wrong. Airbnb's pricing is a per-night pricing calendar that the host manages, with Airbnb's Smart Pricing model overlaying demand signals. Exposing the right nightly total for a date range requires summing the pricing calendar for those nights — this must be fast enough to display inline in search results for 50+ listings. Pre-aggregated price ranges (min/max for next 30 days) are indexed for quick filtering; exact pricing is computed on the listing detail page.
What breaks at scale
Thundering herd on popular listings during holiday release windows: when a host opens their summer calendar, thousands of guests may attempt to book simultaneously, creating a write contention storm on the `reservations` table. Use serializable isolation and accept that some requests will retry — but at high contention this degrades to a queue. Calendar sync with external systems (hosts who manage their listing on VRBO too): stale external calendars cause Airbnb to show availability that's already been booked elsewhere — Airbnb must poll external iCal feeds frequently and apply pessimistic blocking on external-calendar uncertainty. Refund and cancellation state machine complexity: a cancelled booking must atomically release the calendar hold, initiate a refund, notify both parties, and update availability — any partial failure leaves the system in an inconsistent state.
In production
Airbnb's engineering blog describes their Chronos availability service and a reservation service backed by MySQL with pessimistic locking for the critical booking transaction. Search uses Elasticsearch with geo queries for listing discovery (eventual consistency is fine here). The real engineering challenge is the two-phase booking flow: search shows available listings (read from a cached availability index), but by the time the guest confirms, another guest may have booked. The fix is a re-check at commit time using a database-level serializable transaction with an explicit `SELECT ... FOR UPDATE` on the calendar rows — then either commit the booking or return a conflict error and let the frontend handle it gracefully.
Common mistakes
- Eventual consistency on bookings
- Scanning listings for geo search
- No date-range overlap guard