Online Auction
An auction needs an authoritative rule for bid acceptance and closing.
Interview scope and guarantees
Create an auction, place bids, display the current leading bid, close at a defined time, and settle the winner. Define proxy bidding, tie rules and anti-sniping extensions before designing. The accepted bid order must be authoritative.
Capacity worksheet
Assume 10,000 active auctions and 100,000 bids/minute: about 1,667 bids/s average. A single celebrity auction can dominate the total. If one serialized bid transaction takes 5 ms, one owner processes roughly 200/s before queueing; that hot-auction limit matters more than aggregate shard count.
Concrete API contract
POST /auctions/{id}/bids {amount,currency,clientBidId}
GET /auctions/{id}
GET /auctions/{id}/events?after=...
POST /auctions/{id}/close {expectedGeneration}Data model and access paths
auctions(id PK,state,closes_at,highest_bid_id,version)
bids(auction_id,sequence,bid_id,bidder_id,amount,accepted_at)
bid_keys(bidder_id,client_bid_id,bid_id)
settlements(auction_id UNIQUE,winner_id,payment_ref,state)Evolve a solution and explain each change
Figure — Three architecture decisions for Online Auction, including the pressure each introduces.
Step 1: Serialize bids
Store accepted bids against an auction’s current price and deadline. Reading the price and then writing can admit invalid concurrent bids.
Step 2: Commit closure
Use the same authority for bids and auction close with server time. A delayed worker can close after the displayed countdown reaches zero.
Step 3: Deliver live views
Stream committed bid/close events and reconcile payment separately. Socket arrival order must not define the winning bid.
Responsibility overview
Figure — Connected responsibilities for Online Auction. Trace the authoritative and derived paths separately.
Worked end-to-end scenario
Auction A currently has bid 100. Two bidders submit 110 and 120. The authority validates and commits them in its defined serialization order; an equal or lower amount can be rejected under the increment rule. At close, the transaction establishes the winner from accepted state and prevents later bid admission. A client packet sent before the countdown but received after the authoritative deadline follows the stated admission policy, not its device clock. The winner’s payment is a separate workflow. If collection fails, record the outcome and apply the auction policy rather than retroactively altering the bid history.
Why these access paths matter
Use auction ID as the serialization domain; bids need immutable identity, actor, amount, and accepted order. Index auctions by closure deadline for scheduling, but validate the deadline in bid admission even if the closer is late. Persist close results and notification intents together. Money uses integer minor units or an exact decimal model, not floating point.
Build the baseline first
- Bidder → auction owner → transactional bid acceptance.
- Committed bid → event log → live viewers.
- Authoritative close → winner record → settlement.
Evolve the design under load
- Auction-key routing → independent owners.
- Hot auction → bounded sequential command queue.
- Read projection → regional fanout → viewers.
Defend the hardest decision
Use integer minor currency units and enforce the minimum increment against the committed highest bid. Server receipt or commit order must be chosen as the acceptance rule; client clocks are not authoritative. The close command and bid commands must share an ordering boundary. If anti-sniping extends the auction, persist the new close time in the same bid transaction.
Failure and recovery analysis
A close worker retries after the winning bid was stored but settlement publication failed. The unique settlement row and outbox make recovery safe. If payment fails, apply a documented next-bidder or cancellation policy; do not silently rewrite the accepted auction history. During leader failover, fence the old owner so two leaders cannot accept incompatible winners.
Security and privacy boundary
Authenticate bidders, protect payment data, and detect abusive bidding while keeping the acceptance rules reproducible.
Interview follow-ups with reasoning
Question: Can Redis pub/sub be the bid record?
Show answer and explanation
Answer: No, viewers can miss messages; persist accepted bids first.
Question: How do you handle last-second bursts?
Show answer and explanation
Answer: Admission and queue-delay visibility plus a precise deadline policy.
Question: How do you audit disputes?
Show answer and explanation
Answer: Retain accepted and rejected decision evidence with server ordering.
Operate and verify the design
Bid decision latency, per-auction queue age, close conflicts and unresolved settlement.
Race the final bid and close command under the declared deadline rule and inspect the accepted order.
A second scenario to test transfer
At 12:00:00, a bid handler and a close worker race. The database or auction owner evaluates them under the declared deadline and ordering rule. If the bid is accepted before closure under that rule, it can become the winner. If closure wins, the bid is rejected. The client’s displayed clock and a delayed WebSocket event cannot override the authoritative decision.
Can a bid be accepted solely because the client timestamp is before the displayed close time?
Show answer and explanation
Answer: No. Client clocks and delivery delays are untrusted for the authority decision. Apply the server-defined deadline rule atomically with auction state, and return a stable accepted or rejected result for retries.
Compare alternatives
| Choice | Benefit | Trade-off |
|---|---|---|
| Single auction owner | Clear ordering | Hot-auction capacity bound |
| Anti-sniping extension | Reduces last-instant bidding effects | More complex deadline state |
| Live display cache | Fast reads | May lag accepted bids |
A design-changing exercise
The close worker is ten seconds late. Are bids during those ten seconds valid?
Show answer and explanation
Answer: Only if that is the explicit product policy. Normally bid admission checks the authoritative deadline independently of when the worker runs.
Design workshop: bids and closure share one order
Choose ascending auctions with a one-dollar minimum increment, no proxy bidding in the baseline, and a server admission rule: a bid is accepted only when its authoritative transaction evaluates before closes_at. Network send time and client clocks do not qualify. Assume bid acknowledgment under 200 ms when admitted; after overload, reject or queue under a declared rule rather than claim every pre-deadline client click wins.
Lock the auction row, verify active state/deadline/current price, persist the command result and accepted bid, and update highest bid in one transaction. Store commands(auction,actor,client_request_id,fingerprint,outcome,bid_id), unique by identity, including rejected decisions when the contract promises stable retries. A retry lookup occurs before repeating deadline checks, so an accepted bid does not become rejected after its response is lost.
BEGIN;
SELECT * FROM auctions WHERE id=:auction FOR UPDATE;
-- First return recorded command result if this identity exists.
-- Verify active, authoritative time < closes_at, and amount >= high+100.
INSERT INTO bids VALUES (:bid,:auction,:actor,:amount_minor,:accepted_at);
UPDATE auctions SET highest_bid_id=:bid,highest_minor=:amount_minor,
version=version+1 WHERE id=:auction;
-- Store command result and bid event/outbox in the same transaction.
COMMIT;The close worker locks the same row and validates the current deadline and generation before freezing the winner. A late worker cannot use an old timer if anti-sniping extended the deadline. Settlement creates a stable winner/order identity and contacts the payment provider outside SQL. Failure to collect invokes a documented resolution policy; it does not silently promote another bidder without audit.
Figure — Bid, extension, and old close timer.
If proxy bidding is added, store each bidder's maximum privately. Current price is min(highest maximum, second-highest maximum+increment), bounded below by reserve/start price, with a declared tie order for equal maxima. For maxima A=$150 and B=$120 at $1 increment, A leads at $121, not $150. A later B maximum $160 makes B lead at $151. This extension requires bid-history semantics and confidentiality; a public highest-price field does not implement it.
Figure — Close and settlement states.
A live price stream uses committed auction versions and a replay cursor. Display can lag; it never grants acceptance. Hot auctions have a serial capacity bound, so limit in-flight bid decisions and surface overload early. Owner failover restores durable head/version and fences the old epoch before accepting new commands. Monitor command queue wait, deadline rejection, hot-auction transaction time and settlement unknown age separately.
Exercise: Client sends at 11:59:59 but its bid transaction reaches authority at 12:00:01. The deadline is noon. Is it accepted?
Show answer and explanation
Answer: No under this chosen admission rule, unless the authority had already committed an eligible extension. A different receive-before-deadline command-log policy is possible, but must specify the trusted ingress boundary and how queued commands are ordered before closure.
Technical references
Can a bid be accepted solely because the client timestamp is before the displayed close time?
Your design draft
Clarify assumptions, explain your approach, and test the difficult cases. Save your draft, then compare it with the study notes.
Self-review checklist
Self-guided practice. Automated AI feedback and code execution are not connected.