Lowest-Price Book Aggregator Fanning Out to Hundreds of Async Bookstores
DatabricksSoftware Engineer · OnsiteLast updated
Design a middleman service that takes an ISBN, bid price and payment method, queries several hundred asynchronous bookstores for the lowest price, and then orders, returns the price or reports no stock. Deep dives cover partial results and circuit breakers, caching against thundering herds, and order-and-charge consistency.
You run a middleman service for buying books. A customer submits an ISBN, a bid price and a payment method. The system fans out a price query to several hundred partner bookstores and finds the lowest price. If the lowest price is at or below the bid, the system places the order right away; otherwise it returns the lowest price to the customer. If no store has the book in stock, the system notifies the customer.
The interviewer sets two premises: the downstream bookstores are asynchronous, and the volume of external calls is large. Design the system, then go deep on the areas below.
Clarifying Questions
- What does "asynchronous" mean for a store: we send a request and the quote arrives later through a callback or message, or we simply call the store without blocking?
- Does the customer wait on an open request for the outcome, or learn it later through a notification?
- Is a store's quoted price binding when we place the order, or can the price change between quote and order?
- When the lowest price is above the bid, does the request end there, or can the customer accept the returned price and proceed?
- What does the payment step look like: an authorization hold followed by a capture, or a single immediate charge?
Part 1 — Core flow
Describe the APIs, the main components, and the lifecycle of one purchase request, from submission to each of its outcomes: order placed, lowest price returned, or out of stock.
What This Part Should Cover
- The client-facing API, and how the result reaches the customer
- Fan-out and aggregation mechanics for asynchronous replies
- Persistent state for a purchase request, and its transitions
Part 2 — Downstream fault tolerance
Several hundred stores are too many to wait for, and some time out or are down. How do you aggregate partial results, and should you use a circuit breaker?
What This Part Should Cover
- Deadlines, and the rule for deciding with a partial set of quotes
- Circuit breakers or equivalent per-store health handling, and what they cost
- How an "out of stock" answer is qualified when some stores never replied
Part 3 — Reducing load on the stores
How do you cut the number of calls to downstream stores? What should be cached, how long should the TTL be, and how do you prevent a thundering herd when many requests for the same popular ISBN arrive at once?
What This Part Should Cover
- What is cached, how it is keyed, and how the TTL is chosen and bounded
- Coalescing concurrent requests for the same ISBN
- Other levers that reduce call volume
Part 4 — Order and charge consistency
Placing the order and charging the customer are two separate external calls. Which goes first? How do you design compensation if the process crashes between them, and how do you prevent charging the customer twice? The interviewer probes this area in detail.
What This Part Should Cover
- The chosen ordering of the order and payment steps, and its justification
- A durable state machine with compensation for each failure point
- Idempotency for both external calls and for the customer's own retries
Hints
Hint 1
A request is a long-lived object: With several hundred asynchronous replies per request, think about where the in-progress state of one request lives, and what closes it.
Hint 2
When is "enough" enough?: Decide what event ends the waiting for a request, and what that means for the correctness of "lowest price" and "out of stock".
Hint 3
Freshness has a price: Think about what goes wrong when a cached price is stale, and at which step of the flow a stale price is harmless and at which it is harmful.
Hint 4
Name every crash point: List where a crash can happen relative to the two calls, and ask what state you would find on restart in each case.
Follow-up questions
- A store confirms an order but charges a price different from its quote. How do you handle it?
- How would you rank stores on more than price, for example on reliability or delivery time?
- How would you roll out a new store integration without hurting existing request latency?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.