Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

System Design blueprint · Travel & Hospitality

Hotel reservation & room inventory

Guests search by city and dates, hold specific room types for every night of the stay, pay a deposit and get a confirmed booking, while inventory stays in sync with OTAs. The system must never sell a room-night beyond the physical count plus the explicit overbooking allowance, and must never confirm a booking without inventory decremented for every night.

Steps
8
Subjective questions
33
Objective questions
33

The problem and the outcome

Problem: A hotel sells the same rooms on its own site and on several online travel agencies, so per-night availability changes from many places at once. Naive checks oversell rooms, abandoned checkouts lock rooms for no one, and slow OTA updates sell rooms already gone.

Outcome: Each room type and night has one authoritative counter that is updated atomically with holds, bookings and cancellations. Every change goes out as an event to search and to OTAs, so oversells are limited to what the revenue team has chosen to allow.

Architecture

Guestweb / appOTA partnersBooking, ExpediaSearch servicecity + dates01Booking servicereservation saga02Channel managerARI sync07Search indexavailability cacheInventory serviceper-night counters03Overbooking policyper room type04Hold expirydelayed queueInventory DBroom_type x nightPayment gatewayauth / deposit05Outbox + Kafkainventory events06Cancellation svcpolicy + refund08city, dates, guestsread cached availselect and checkouthold all nightswithin allowance?conditional decrementschedule expiryrelease stale holdauthorise depositwebhook resultoutbox rowsrefresh availARI deltaspush availabilityOTA reservationcreate bookingcancel requestrelease nights

How it works, step by step

  1. The guest searches by city, dates and party size. The search service reads a denormalised availability and rate cache, finds hotels where every night in the range has stock, and labels results as indicative, not guaranteed.
  2. At checkout the booking service creates a pending reservation with an idempotency key and starts a saga: hold inventory, take payment, confirm.
  3. The inventory service holds the chosen room type for every night of the stay in one database transaction. It uses a conditional update (held + sold < limit) on each room_type-night row, writes a hold record with an expiry time, and schedules a release on the delay queue.
  4. The limit for each row is the physical room count plus the overbooking allowance set by revenue management for that room type and date. If any night fails the check, the whole hold rolls back and the guest sees alternatives.
  5. The booking service authorises the card or collects the deposit set by the rate plan's policy. It confirms the booking only after the gateway webhook succeeds, and converts the hold into sold in the same transaction as the status change.
  6. Every inventory change writes an outbox row in the same transaction. A relay publishes these rows to Kafka, where consumers refresh the search cache and send notifications.
  7. The channel manager turns events into availability, rate and inventory (ARI) deltas for each OTA and pushes them. It also takes in OTA reservations and modifications and sends them through the same booking and inventory path, using the OTA's booking id as the idempotency key.
  8. Cancellations apply the rate plan's policy, such as free-cancel window, penalty or non-refundable. The service releases each night back to inventory, starts any refund, and emits events so OTAs and search show the room again.

Tech stack

  • PostgreSQL with one row per hotel, room type and night, using row locks or conditional updates
  • Transactional outbox with Kafka or another durable log for inventory events
  • Redis or a delay queue for hold expiry timers, with a DB sweeper as backup
  • OpenSearch or Elasticsearch for geo and city search over cached availability
  • Payment gateway such as Razorpay or Stripe with auth/capture and webhooks
  • Channel manager integration using OTA APIs or an OpenTravel-style ARI interface
  • Temporal or a custom saga orchestrator for hold, pay and confirm steps

Key design decisions

  • Store inventory per room type per night, not per physical room. Guests book a type, and assigning a specific room at check-in keeps the counter math simple and contention low.
  • Hold inventory before taking payment, and expire holds with a timer. This avoids charging guests for rooms that cannot be given, at the cost of briefly locking stock during checkout.
  • Use a strongly consistent relational store for counters and an eventually consistent cache for search. Search can be slightly stale, but the booking write path rechecks everything.
  • Treat overbooking as explicit per-date configuration from revenue management, enforced in the same conditional update, rather than letting oversell happen by accident through sync lag.
  • Route OTA bookings through the same inventory path as direct bookings, so there is one source of truth and channel sync is only a projection.

Common mistakes

  • Checking availability with a read and then writing separately lets two bookings pass the check at once. Always use a conditional update or SELECT FOR UPDATE inside the transaction.
  • Decrementing nights one by one outside a single transaction leaves partial holds when a later night fails.
  • Not making OTA reservation ingestion idempotent means that when an OTA retries a webhook, the same booking is created twice and inventory is double-decremented.
  • Hold timers that rely only on Redis expiry can lose releases during failover. Keep the expiry time in the database and run a sweeper too.
  • Locking rows in different orders for multi-night stays causes deadlocks. Always lock nights in ascending date order.

What to measure

  • Oversell count per property per night beyond the configured allowance (target zero)
  • Hold-to-confirm conversion rate and count of expired holds
  • Channel sync lag from an inventory change to OTA acknowledgement, p95
  • Booking confirmation latency p95 and saga failure or compensation rate
  • Search-to-book availability mismatch rate (shown available, failed at hold)

Most-asked interview questions

Subjective questions 33

1How do you prevent double-booking the last room when two guests check out at the same time?Asked almost every time

I keep one row per hotel, room type and night with total, held and sold counts. The hold runs in a single transaction that updates every night of the stay with a conditional statement like: set held = held + 1 where held + sold + 1 <= limit. If any update affects zero rows, I roll back the whole transaction. I lock rows in date order to avoid deadlocks. Because the database checks the condition atomically, only one of the two guests gets the last room. The other gets a clean failure and alternatives. Search results are just a cache, so they never decide this. The booking path always rechecks against the authoritative rows.

2Walk me through the high-level architecture of this hotel reservation system.Asked almost every time

I split it into a read path and a write path. The read path is the search service. It queries a denormalised availability and rate cache that Kafka consumers keep fresh, so heavy browsing never touches the inventory database. The write path starts in the booking service. It creates a pending reservation with an idempotency key and runs a saga: hold inventory, take payment, confirm. The inventory service owns room_type-night rows in a relational database and uses conditional updates, so it is the source of truth. Every change writes an outbox row, and a relay publishes it to Kafka. From there, search, notifications and the channel manager consume it. The channel manager pushes ARI deltas to OTAs and sends OTA bookings back through the same booking path.

3Describe the schema for the inventory table and the hold records.Asked almost every time

The inventory table is keyed by hotel_id, room_type_id and stay_date. Its columns are physical_count, overbooking_allowance, sell_limit, held, sold and updated_at. A check constraint enforces held + sold <= sell_limit as a second safety net. The hold table stores hold_id, reservation_id, status (HELD, CONVERTED, RELEASED, EXPIRED), expires_at and created_at. A child table has one line per night, giving the room type, date and quantity. Reservations store the guest reference, rate plan, a snapshot of the cancellation policy, the quoted price for each night, status and the idempotency key under a unique index. I store per-night lines so that cancellations and modifications release exactly what was taken, even if the limits have changed since.

4The payment webhook arrives after the hold has already expired and been released. What do you do?Asked almost every time

The confirm step must never assume the hold is still alive. In one transaction I try to convert it with a conditional update: set status to CONVERTED only where status is HELD. If that affects zero rows, the hold is gone. Next I try to re-acquire every night with the same conditional update. If that succeeds, I convert and confirm. If any night fails, I mark the reservation FAILED. I then void the authorisation or refund the deposit and offer alternatives. Either way the invariant holds, because nothing is confirmed without inventory decremented for every night. I also alert when this happens often, since it suggests hold TTLs are shorter than real payment times.

5How would you keep inventory consistent with OTAs like Booking.com and Expedia?Very often asked

The inventory database is the single source of truth. Every change writes an outbox row in the same transaction. A relay publishes these to Kafka, and the channel manager turns them into availability and rate deltas for each OTA, retrying with backoff. Inbound OTA reservations go through the same hold-and-confirm path, using the OTA booking id as the idempotency key. Sync is still asynchronous, so there is a small window where an OTA can sell a room already gone. I handle that with a small overbooking allowance on high-demand dates, closing out a room type early when stock is low, and running a periodic full reconciliation that compares our counts with each channel's view.

6How do holds work during checkout, and what happens if payment is slow or fails?Very often asked

When the guest picks a room, I hold every night of the stay and store an expiry time on the hold. I also schedule a release on a delay queue, and a database sweeper acts as a backup. Payment authorisation runs against that hold. If the gateway webhook says success, one transaction turns the hold into sold and marks the booking confirmed. If payment fails or the hold times out, the hold is released and counts go back up. The tricky case is a late payment success after the hold expired. I recheck availability then. If the room is gone, I void the authorisation or refund it and tell the guest, instead of confirming a room we cannot provide.

7Why are search results labelled indicative, and how do you handle a guest who picks a room that is actually sold out?Very often asked

The search cache is updated asynchronously through the outbox and Kafka, so it lags the database by a short time. Making search strongly consistent would push enormous read traffic onto hot inventory rows, and most of those searches never become bookings. So I accept staleness on reads and enforce correctness only at hold time. When the conditional update fails for any night, the transaction rolls back and the guest sees alternatives straight away. These might be other room types, nearby hotels or flexible dates. I also make the failed hold trigger a targeted cache refresh for that hotel, so the next guest does not see the same stale result.

8How do you design idempotency across booking creation, payment and OTA ingestion?Very often asked

Each entry point has a natural key. Direct checkout uses a client-generated idempotency key stored under a unique index on reservations. A retry returns the existing reservation instead of creating a second one. OTA bookings use channel plus the OTA's booking id. Payment calls to the gateway pass our reservation-scoped idempotency key, so retries never double-charge. Webhooks are deduplicated by the gateway's event id in a processed-events table, written in the same transaction as the state change. Inside the saga, every step is a conditional state transition, such as HELD to CONVERTED. Replaying a step therefore does nothing. This matters because Kafka, the outbox relay and webhooks all deliver at least once.

9How is the overbooking allowance modelled, and what happens operationally when more guests arrive than rooms?Very often asked

Revenue management sets an allowance for each room type and date, based on its own forecast of no-shows and cancellations. We store it next to the physical count, and the sell limit is the sum of the two. The system enforces that limit strictly and never sells beyond it. The physical count still matters, because if sold exceeds it on the night of arrival, the front desk has a walk situation. We expose an oversold-risk report per night so staff can upgrade guests, move them to sister properties or arrange relocation under the hotel's policy. Allowance changes are audited. Lowering an allowance below the current sold count never cancels existing bookings. It only blocks new sales.

10Give me a back-of-the-envelope estimate for the load on search compared with booking.Very often asked

I would state my assumptions out loud rather than quote real figures. Say we have N hotels, each with about ten room types, and an 18-month booking window. Then inventory rows are roughly N × 10 × 540. For a hundred thousand hotels that is about 540 million rows, which a sharded relational store handles comfortably. Traffic is very read-heavy. If each booking comes from hundreds of searches, and each search checks many hotels and nights, search QPS is orders of magnitude above hold QPS. That is why search runs on a cache or search index that scales horizontally. The inventory database only sees holds, confirms, releases and ARI-driven updates, so it is sized for write contention, not read volume.

11A big concert drives thousands of checkouts at one hotel at the same moment. How do you handle hot-row contention?Very often asked

Each hold locks only a few rows, one per night, and does so briefly, so the transaction must stay tiny. There are no external calls inside it, and rows are locked in date order to avoid deadlocks. Under heavy contention the conditional update serialises requests on those rows, and losers fail fast instead of retrying in a loop. I would add admission control per hotel, such as a small queue or a rate limit in front of the hold endpoint, so the database is not overwhelmed. I would also short-circuit at the edge with a stale-but-recent sold-out flag. Other designs, such as pre-splitting stock into buckets, cost a lot in complexity and are rarely needed for hotels.

12How would you shard the inventory database?Very often asked

I shard by hotel_id. A stay is always for one hotel, so every room_type-night row for a hold sits on the same shard. That keeps the all-nights-or-nothing hold a single local transaction, with no distributed transaction at all. Reservations and holds for that hotel are co-located for the same reason. Hash-based sharding spreads popular cities evenly. I keep a directory or consistent hashing scheme so I can move very large chains or hot hotels without reshuffling everything. Cross-hotel queries, such as a chain's reporting or guest booking history, go to read replicas or analytics stores fed by the same Kafka events, not to the transactional shards.

13How does hold expiry work, and what if the delay queue loses or delays a message?Very often asked

When creating a hold we write expires_at and schedule a release message on the delay queue. When the message fires, the release runs one transaction. It sets status to EXPIRED only where status is still HELD, and if that succeeds it decrements held on every night. Running it twice does no harm. I never trust the queue alone. A periodic sweeper scans for holds where status is HELD and expires_at has passed, and releases them through the same code. So lost messages only delay the release a little. Late messages hit the conditional and do nothing if the hold was already converted. The search cache is also counted from held, so leaked holds would otherwise hide real stock.

14A guest changes their dates on a confirmed booking. How do you do it safely?Very often asked

I treat it as one inventory transaction on the hotel's shard. First I conditionally acquire every new night that is not already part of the booking. If any night fails, the whole thing rolls back and the original booking stays untouched. If all succeed, I release the nights that are no longer needed, update the reservation lines and write outbox events, all in the same commit. Overlapping nights are left as they are. This avoids a window where the guest holds nothing. Price differences and any modification fees are handled afterwards as a separate payment step that can be compensated. OTA modifications go through exactly the same path, keyed by the OTA's booking id plus a modification version.

15An OTA sends us a booking for a room-night we no longer have, because it hadn't received our last update. What happens?Very often asked

The OTA booking goes through the same conditional update as direct bookings, so our invariant is never broken by a channel. If the check fails, we reject or flag the booking according to that OTA's integration contract. Some channels accept a rejection, while others treat the booking as already confirmed to the guest. In the second case it lands in an exceptions queue for the hotel to resolve through relocation or by using spare overbooking allowance if revenue management permits. To make this rare, we push ARI updates quickly, prioritise close-outs over other updates, and keep a small buffer of stock off the channels near sell-out if the hotel chooses.

16How do you deal with ordering and duplicates when the outbox relay publishes to Kafka?Very often asked

The relay reads outbox rows in commit order and publishes them, but a crash can cause republishing, so delivery is at least once. I partition Kafka topics by hotel_id, or hotel plus room type, so events for the same inventory stay in order within a partition. Each event carries a monotonically increasing version for its room_type-night. Consumers keep the last version they applied and ignore older or duplicate ones. Where possible, events carry the absolute state, such as available = limit − held − sold, rather than deltas. That makes it safe to reapply them and lets a consumer recover just by reading the latest event.

17How do you handle card data securely in this flow?Very often asked

I keep our systems out of PCI DSS scope as far as possible. Card entry happens in the gateway's hosted fields or redirect page, and we receive only a token and the last digits. We never store the full card number, and we never store CVV after authorisation, which PCI DSS forbids. Webhooks are verified by signature and timestamp, and processed only once using the event id. Pay-at-hotel guarantees use network tokens or vaulted tokens. Card details are never passed to the hotel in plain text. Secrets for gateway and OTA APIs live in a secrets manager and are rotated. Access to payment admin tools needs strong authentication and is audited.

18Why not use a Redis distributed lock or Redis counters as the inventory source of truth?Very often asked

Redis locks with leases can be lost when a process pauses, a failover happens or the clock drifts. Two holders could then both believe they own the last room. Redis counters also cannot easily join a durable transaction with the hold record and the outbox row. The all-nights-or-nothing requirement and the outbox both need ACID atomicity in one store. A relational database's conditional update gives correctness without a separate lock service, and rows already serialise concurrent writers. I use Redis for the read cache and rate limiting, where staleness is acceptable, and keep the invariant in the database, where a crash cannot break it.

19The gateway webhook never arrives. How does the booking avoid getting stuck?Very often asked

I never rely on webhooks alone. A saga timer runs for each pending reservation. If no webhook arrives within the expected window, a worker asks the gateway's payment-status API using our reference. If the payment succeeded, we run the same confirm logic as the webhook. If it failed, we release the hold. If it is still unclear, we extend the hold within a cap and keep polling. Webhook and poller both go through one idempotent confirm function, so whichever arrives second does nothing. Guests see a 'confirming your booking' state instead of a false failure, which also reduces duplicate retry bookings.

20Rates differ per night. How do you price a multi-night stay and protect against price changes during checkout?Often asked

Rates are stored per rate plan, room type and date, sometimes with length-of-stay or occupancy rules. At checkout the pricing service calculates a quote, which is a list of prices per night plus taxes and fees. We store it on the pending reservation with a quote id and an expiry. The amount charged is the stored quote, not a fresh lookup. That way a rate change in the middle of checkout cannot alter what the guest agreed to. If the quote expires before payment, we re-quote and show the guest any difference before charging. The cancellation policy is also stored as a snapshot on the reservation, so later edits to the rate plan never change an existing booking's terms.

21OTAs enforce rate limits on ARI pushes. How does the channel manager cope?Often asked

I coalesce instead of forwarding every event. The channel manager keeps the latest desired state for each OTA, hotel, room type and date, and marks it dirty when an event arrives. A sender for each channel drains dirty keys within that channel's rate limit, batching dates into ranges where the OTA's API supports it. Ten quick changes to one night then become one push of the final value. Close-outs and low-availability updates get priority over rate tweaks. Failed pushes retry with backoff and jitter, and each attempt is logged for each channel so the hotel can see delivery status.

22How do you detect and fix drift between our inventory and what OTAs display?Often asked

Incremental pushes can be lost, rejected or misapplied, so I add reconciliation. On a schedule, and on demand after incidents or a new mapping, the channel manager runs a full sync. It pushes the full ARI for a date window, which overwrites whatever the OTA holds. Where the OTA offers a read or reservation-retrieval API, we also pull its reservations and compare them with ours to find missed bookings or cancellations. Mismatches create tickets or trigger automatic re-ingestion through the idempotent booking path. Mapping errors, where an OTA room is linked to the wrong room type, are a common root cause, so mapping changes are validated and audited.

23Walk through cancellation, including what happens if the refund fails.Often asked

The cancellation service evaluates the stored policy snapshot at the cancellation time, using the hotel's timezone. This gives free cancellation, a penalty or no refund. In one transaction it sets the reservation to CANCELLED conditionally, releases each night by decrementing sold, and writes outbox events. The room is back on sale quickly whether or not the refund has finished. The refund is a separate saga step, called with an idempotency key and retried with backoff. If it keeps failing, it goes to a finance exceptions queue, and we never re-sell or un-cancel the booking to fix it. The guest gets a notification with the refund status, and support can see the payment state.

24What privacy obligations apply to guest data, and how do you design for them in India?Often asked

Bookings hold personal data: names, contact details, sometimes ID documents, and stay history. India's Digital Personal Data Protection Act, 2023 requires a lawful basis, purpose limitation and security safeguards. It also requires honouring rights such as access and erasure, subject to legal retention needs such as tax records. In practice, I encrypt PII at rest, keep it in a guest service separate from inventory, and put only opaque guest ids in events. I share with OTAs and hotels only what the booking needs, set retention periods, and mask PII in logs. Guest registration and ID rules for hotels may add their own requirements, so I check with legal rather than assume.

25How do you handle time zones for stay dates and cancellation windows?Often asked

A room-night is a calendar date in the hotel's local timezone, not a UTC instant. Inventory rows use a plain date type, and each hotel stores an IANA timezone id. Search converts the guest's chosen dates as dates and never shifts them through UTC. Cancellation deadlines, such as free cancellation until 6 pm the day before arrival, are calculated in the hotel's timezone and then stored as a UTC instant on the reservation for enforcement. Daylight-saving changes in some countries make this matter. Hold expiries and audit timestamps stay in UTC, because they are real instants rather than business dates.

26How is the search availability cache structured so it can answer 'every night has stock' quickly?Often asked

Consumers update a key for each hotel, room type and date, holding available count and the best rate, in a store such as Redis or a search index. For a query, we first narrow hotels by city and party size using a geo or city index. Then, for each candidate room type, we fetch the nights in the range and check that the minimum available is at least one and that rules such as minimum stay pass. Many teams precompute a bitmap of available dates or a hotel-level summary so they can prune quickly before the detailed check. The price shown is the sum of nightly rates from the cache and is marked indicative. The real quote comes at checkout.

27Why use a saga here instead of a distributed two-phase commit?Often asked

Two-phase commit needs every participant to support prepare and commit, and payment gateways and OTAs do not. It also holds locks while waiting for a coordinator, which is very costly on hot inventory rows when payments can take tens of seconds. A saga breaks the flow into local transactions with compensations: hold, then authorise, then convert. If authorisation fails we release the hold. If inventory is lost after payment we void or refund. The trade-off is that intermediate states, such as pending or held, are visible. So each step must be idempotent and recoverable, and a timeout-driven sweeper must finish or compensate any stuck saga.

28How do deposit, full prepayment and pay-at-hotel rate plans change the flow?Often asked

The rate plan's policy decides the payment step of the saga. For prepaid or deposit plans we capture an amount, either the full stay or a deposit, and confirm when we receive the success webhook. For pay-at-hotel with a card guarantee we may do a zero-value or small authorisation to verify the card and store a token, then confirm. Authorisations expire after a period set by the card network and issuer. So for long lead times we capture or re-authorise according to the policy rather than assume the authorisation lasts. Non-refundable plans capture in full immediately. In every variant, the inventory conversion happens in the same transaction as the status change to confirmed.

29The hotel marks rooms out of order, so physical count drops below what is already sold. What happens?Often asked

Updating physical count recalculates the sell limit, but existing bookings are never cancelled automatically. The check constraint should therefore allow sold to exceed the limit, or the limit change should be done outside that constraint. Otherwise the update itself would fail. From then on, held + sold >= limit, so every new conditional update fails and the night is closed for sale. An outbox event pushes a close-out to the OTAs. The night appears on an oversold report so the front desk can plan moves or relocations. The change is audited with who made it and why, because it directly affects revenue and guest experience.

30Which metrics and alerts would you put on this system?Often asked

For correctness, I would run an invariant checker that alerts on any row where held + sold > limit. I would also check for confirmed reservations missing per-night lines and for sold counts that disagree with the bookings summed per night. For flow health, I would track hold success rate, hold-expired-before-payment rate, saga time-to-confirm, stuck pending sagas, refund failures and outbox relay lag. For channels, I would watch ARI push latency and errors per OTA, OTA bookings rejected for lack of stock, and reconciliation mismatches. For performance, I would watch search latency, cache staleness and lock wait time on inventory shards. Each alert links to a runbook.

31How would you support a booking of several rooms, possibly of different room types?Sometimes asked

The hold becomes a multi-line hold. There is a quantity for each room type for each night, and the conditional update checks held + sold + qty <= limit. All lines across all room types and nights run in one transaction on the hotel's shard, locked in a fixed order by room type and then date to avoid deadlocks. The group is all-or-nothing by default. A guest can choose partial fulfilment, but only as an explicit option. Large group or allotment blocks for events are usually handled separately, as contracted allocations with release dates. Those reduce the sellable limit without creating individual bookings.

32How do you design for regional failure of the inventory database?Sometimes asked

Each shard runs a primary with synchronous or semi-synchronous replicas in other availability zones, so a zone failure costs no committed holds. For a full regional outage, an asynchronous cross-region replica can be promoted. I would only allow writes in one region per shard at a time, because running two active writers for the same rows would break the invariant. After failover, any recent commits that were not replicated create a reconciliation task against payment and OTA records. During an outage, search can keep serving from cache while checkout returns a clear 'try again shortly' message rather than taking payments it cannot back with inventory.

33How would you test that the system never oversells?Sometimes asked

I would start with property-based and concurrency tests. They fire many parallel holds, modifications and cancellations at one room-night, then assert that held + sold never exceeds the limit and that every confirmed booking has every night decremented. I would add fault injection. That means killing the service between hold and confirm, duplicating webhooks and Kafka messages, dropping delay-queue messages and delaying the gateway, then checking that sweepers and idempotency recover correctly. In staging I would replay recorded OTA traffic. In production the invariant checker and reconciliation jobs act as continuous tests. Formal models such as TLA+ can help for the saga state machine if the team has the skills.

Objective questions 33

1Which SQL statement correctly holds qty rooms for one room_type-night without overselling?Asked almost every time
  1. AINSERT INTO holds VALUES (...) and check the totals in a nightly batch job
  2. BSELECT held, sold FROM inv WHERE id = :id; then UPDATE inv SET held = held + :qty in application code
  3. CUPDATE inv SET held = held + :qty WHERE id = :id AND held + sold + :qty <= sell_limitCorrect answer
  4. DUPDATE inv SET held = :new_value WHERE id = :id

Why: The condition and the increment run atomically in one statement, so concurrent writers cannot both pass the check.

2A guest checks in on the 10th and checks out on the 13th. How many room_type-night rows must the hold decrement?Asked almost every time
  1. A2
  2. B1
  3. C3Correct answer
  4. D4

Why: The guest occupies the nights of the 10th, 11th and 12th. The checkout date is not a night of the stay.

3When should the booking be marked CONFIRMED?Asked almost every time
  1. AAfter the confirmation email is sent
  2. BWhen the hold succeeds
  3. CWhen the guest clicks Pay
  4. DAfter the gateway's success webhook, in the same transaction that converts the hold to soldCorrect answer

Why: Confirmation must be atomic with the inventory conversion and must come after verified payment success.

4What is the most suitable unit for storing sellable hotel inventory?Very often asked
  1. AOne row per hotel, room type and nightCorrect answer
  2. BOne document per booking with embedded availability
  3. COne row per physical room for the whole year
  4. DOne counter per hotel for all dates

Why: Guests book a room type for a set of nights, so per-type-per-night rows allow atomic range checks with low contention. Specific rooms are assigned later.

5Which technique safely stops two concurrent bookings from both taking the last room-night?Very often asked
  1. AA conditional UPDATE that only increments when held + sold < limit, inside a transactionCorrect answer
  2. BRetry the booking if the guest reports a problem
  3. CClient-side locking in the browser
  4. DRead availability from the search cache, then insert the booking

Why: The database evaluates the condition and the write atomically, so only one transaction can succeed when one unit is left.

6How should the inventory service avoid deadlocks when two multi-night holds overlap?Very often asked
  1. AUse a longer lock timeout
  2. BLock the rows in the order the guest selected the dates
  3. CRetry every transaction exactly three times
  4. DLock the rows in a consistent order, such as by room type and then by dateCorrect answer

Why: Acquiring locks in the same global order removes the circular waits that cause deadlocks.

7What is the sell limit for a room_type-night in this design?Very often asked
  1. AThe physical room count only
  2. BThe physical room count plus the overbooking allowance for that room type and dateCorrect answer
  3. CWhatever each OTA requests
  4. DThe physical count multiplied by the average occupancy

Why: Revenue management sets an allowance for each room type and date, and it is added to the physical count.

8Which idempotency key should be used for a reservation coming from an OTA?Very often asked
  1. AOur auto-increment reservation id
  2. BThe guest's email address
  3. CA hash of the check-in date
  4. DThe channel identifier plus the OTA's booking idCorrect answer

Why: The OTA's booking id is stable across retries. Scoping it by channel avoids collisions between different OTAs.

9A hold-release message arrives after the hold was already converted to sold. What should happen?Very often asked
  1. ANothing, because the release only acts if the hold status is still HELDCorrect answer
  2. BDecrement held anyway
  3. CCancel the booking
  4. DMove the message to a dead-letter queue and page on-call

Why: A conditional state transition makes the release idempotent and safe against late messages.

10Why is a periodic sweeper still needed alongside the delay queue for hold expiry?Very often asked
  1. AThe delay queue cannot hold messages longer than one second
  2. BDelay-queue messages can be lost or delayed, and the sweeper releases expired holds anywayCorrect answer
  3. CThe sweeper confirms bookings
  4. DSweepers are needed to update the OTAs

Why: A sweeper that checks expires_at guarantees that leaked holds are eventually released.

11Why is it acceptable for the search cache to be slightly stale?Very often asked
  1. AThe cache is updated synchronously
  2. BThe conditional update at hold time re-checks real inventory before anything is soldCorrect answer
  3. CGuests never book rooms they have searched for
  4. DKafka guarantees exactly-once delivery

Why: Correctness is enforced on the write path, so stale reads can only lead to a failed hold, never to an oversell.

12What delivery guarantee should consumers assume from the outbox relay to Kafka?Very often asked
  1. AExactly-once, with no need for deduplication
  2. BBest-effort with no retries
  3. CAt-least-once, so consumers must be idempotentCorrect answer
  4. DAt-most-once

Why: The relay can republish a row after a crash, so duplicates are possible and consumers must handle them.

13Under PCI DSS, which card data must never be stored after authorisation?Very often asked
  1. AThe last four digits
  2. BThe cardholder's name
  3. CThe gateway token
  4. DThe CVV/CVC security codeCorrect answer

Why: PCI DSS prohibits storing sensitive authentication data such as the card verification code after authorisation.

14Which shard key keeps the all-nights-or-nothing hold a single local transaction?Very often asked
  1. Aguest_id
  2. Bcity
  3. Chotel_idCorrect answer
  4. Dstay_date

Why: Every night of a stay belongs to one hotel, so sharding by hotel keeps all the rows on one shard.

15Why is a transactional outbox used for channel manager sync?Often asked
  1. AIt makes OTA APIs respond faster
  2. BIt guarantees every committed inventory change is eventually published, without dual-write gapsCorrect answer
  3. CIt removes the need for idempotency at OTAs
  4. DIt replaces the inventory database

Why: Writing the event in the same transaction as the inventory change means no committed change is lost and no event is published for a rolled-back change.

16Which Kafka partition key preserves the order of inventory events for a hotel's room-nights?Often asked
  1. AThe event timestamp
  2. BThe hotel_id, or hotel_id plus room typeCorrect answer
  3. CThe guest_id
  4. DA random UUID for each event

Why: Kafka orders events only within a partition, so events for the same inventory must share a key.

17In hotel distribution, what does ARI stand for?Often asked
  1. AAvailability, Rates and InventoryCorrect answer
  2. BAllocation, Refunds and Invoicing
  3. CAverage Room Income
  4. DAutomated Reservation Interface

Why: ARI is the standard term for the availability, rates and inventory data pushed to channels.

18Which Indian law mainly governs how guest personal data is processed?Often asked
  1. AThe Companies Act, 2013
  2. BThe Indian Contract Act, 1872 only
  3. CThe Digital Personal Data Protection Act, 2023Correct answer
  4. DThe Payment and Settlement Systems Act, 2007 only

Why: The DPDP Act, 2023 is India's general law on processing digital personal data.

19How should stay_date be stored on inventory rows?Often asked
  1. AAs a calendar date in the hotel's local timezoneCorrect answer
  2. BAs the guest's local time
  3. CAs epoch milliseconds
  4. DAs a UTC timestamp at midnight

Why: A room-night is a business date at the property. Shifting it through UTC can move it to a different day.

20In PostgreSQL under READ COMMITTED, why is UPDATE ... WHERE held + sold + :qty <= sell_limit safe against races?Often asked
  1. AThe statement always runs at SERIALIZABLE isolation
  2. BREAD COMMITTED prevents all anomalies
  3. CPostgreSQL ignores the WHERE clause on locked rows
  4. DThe second writer waits for the row lock, then re-checks the WHERE condition against the newly committed rowCorrect answer

Why: PostgreSQL re-evaluates the condition against the latest committed version of the row after acquiring the lock.

21A guest changes their dates. What is the safest inventory approach?Often asked
  1. AOnly update the dates on the reservation row
  2. BAsk the hotel to adjust inventory by hand
  3. CCancel the old booking first, then try to book the new dates
  4. DAcquire the new nights and release the dropped nights in one transaction, rolling back entirely if any new night failsCorrect answer

Why: A single atomic swap means the guest never loses their original booking if the new dates are unavailable.

22Why is a saga preferred over two-phase commit for hold, payment and confirm?Often asked
  1. A2PC cannot work with relational databases
  2. BSagas need no compensation logic
  3. CExternal payment gateways do not take part in 2PC, and 2PC holds locks for a long timeCorrect answer
  4. DSagas guarantee isolation between steps

Why: A saga uses local transactions with compensations, which suits external systems and keeps lock times short.

23An OTA enforces strict rate limits. How should the channel manager send ARI updates?Often asked
  1. APush only once a day
  2. BCoalesce to the latest state for each key, batch date ranges, and prioritise close-outsCorrect answer
  3. CStop pushing until the rate limit resets
  4. DForward every Kafka event immediately

Why: Coalescing removes intermediate values and stays within limits while urgent sell-outs still go out first.

24What is the main purpose of a periodic full ARI sync to OTAs?Often asked
  1. ATo fix drift caused by lost, rejected or misapplied incremental updatesCorrect answer
  2. BTo replace the outbox
  3. CTo refresh guest PII
  4. DTo reduce database storage

Why: Overwriting the OTA's state with our source of truth repairs any divergence.

25How should the system verify a payment gateway webhook?Often asked
  1. ACheck that the request comes from any HTTPS source
  2. BTrust it if the amount matches
  3. CAccept it if it contains the reservation id
  4. DVerify the gateway's signature and timestamp, and deduplicate by event idCorrect answer

Why: Signature verification prevents forged confirmations, and deduplication by event id makes processing idempotent.

26No webhook arrives within the expected time for a pending payment. What is the best action?Often asked
  1. AQuery the gateway's payment-status API and confirm or compensate through the same idempotent logicCorrect answer
  2. BCancel immediately and charge again
  3. CWait indefinitely
  4. DConfirm the booking anyway

Why: Polling recovers lost webhooks, and sharing one idempotent confirm function avoids double processing.

27Rooms are marked out of order, so the sell limit drops below the current sold count. What happens to new hold attempts?Often asked
  1. AThey succeed because the old bookings are grandfathered
  2. BExisting bookings are auto-cancelled to make room
  3. CThey fail, because held + sold already meets or exceeds the limitCorrect answer
  4. DThe limit is reset to the physical count plus 10%

Why: The conditional update blocks further sales, and existing bookings are handled operationally.

28Why should the pending reservation store the quoted per-night prices and a policy snapshot?Often asked
  1. ABecause rates are never stored anywhere else
  2. BSo search can read them
  3. CSo OTAs can change them
  4. DSo a rate or policy change during or after checkout does not alter what the guest agreed toCorrect answer

Why: Snapshots fix the commercial terms at booking time, which prevents disputes and mismatched charges.

29A guest cancels and the refund API keeps failing. What should happen to the released inventory?Sometimes asked
  1. AIt should stay released, while the refund is retried or escalated separatelyCorrect answer
  2. BIt should be re-held until the refund succeeds
  3. CThe booking should be reinstated
  4. DThe night should be closed on every OTA

Why: Inventory release and refund are separate saga steps. A refund failure should not take a room off sale.

30Which check best detects an inventory invariant violation in production?Sometimes asked
  1. AAny row where held + sold > sell_limitCorrect answer
  2. BThe average hold latency
  3. CThe number of searches per minute
  4. DThe Kafka consumer group count

Why: That condition directly expresses the overselling the system must never allow.

31What is the main drawback of optimistic version-column updates on a very hot room-night?Sometimes asked
  1. AMany conflicts lead to repeated retries and wasted workCorrect answer
  2. BThey need two-phase commit
  3. CThey store card data
  4. DThey cannot detect conflicts

Why: Under heavy contention most optimistic attempts fail and retry, while a conditional update simply serialises the writers.

32For a multi-room group booking, how should the hold check quantity?Sometimes asked
  1. ACheck the total rooms across the whole hotel
  2. Bheld + sold + qty <= sell_limit for every room type and night, in one transactionCorrect answer
  3. CCheck one room at a time in separate transactions
  4. DCheck only the first night

Why: One all-or-nothing transaction with the quantity included prevents partial or oversold groups.

33An OTA booking fails the inventory check, but the OTA treats it as already confirmed to the guest. What is the best handling?Sometimes asked
  1. ASend it to an exceptions queue for the hotel to resolve, without breaking the invariant automaticallyCorrect answer
  2. BSilently drop it
  3. CForce sold above the limit automatically
  4. DRetry the conditional update until it succeeds

Why: Humans resolve the conflict through relocation or an approved allowance change, while the automated invariant stays intact.

Track your progress through every blueprint in your dashboard.

Start learning