Lesson 8 of 8 · 60 min
Worked design — marketplace/dispatch + mock
Geo dispatch (H3/S2), atomic matching, ETA/surge, two-sided consistency, fraud altitude, 45-min mock rubric, and pointer to ml-system-design for AI-native rounds.
Lesson 8 · Capstone
Marketplace / dispatch + mock rubric
Two-sided systems under time pressure
Requirements & NFRs
- 01Cities / multi-region launch?
- 02p99 match time target (e.g. < 5–30s to offer)?
- 03Can drivers reject? stacked trips?
- 04Pricing: upfront fare vs meter + surge?
- 05Fraud: promo abuse, GPS spoofing altitude?
- 06Consistency: double-dispatch to two drivers forbidden.
- 07Payment auth hold vs capture timing?
- 08Cold start in a new city?
Capacity sketch (decision-linked)
1# Heuristic example — label as interview assumptions2rides_per_day = 5_000_0003ride_qps = rides_per_day / 86_400 # ~60 QPS starts; peaks 5–10× in cities4# Location updates dominate if naive:5# 5M active drivers * 1 Hz = 5M writes/s → impossible as row-per-ping OLTP6# Budget after sampling/batching, e.g. target ~10k–100k geo-index updates/s class78# Uber-class reported anchors (label reported):9# - push platform >70k QPS10# - H3 res ~9 city-scale cells; neighbor rings for nearby query11# - dispatch is not the largest byte pipe; GPS ingest is1213# DECISIONS FORCED:14# 1) do NOT write raw 1Hz GPS to primary OLTP15# 2) in-memory geo index (H3/S2 cells) for active supply16# 3) Kafka (or equiv) for location stream → cold analytics17# 4) match engine separate from trip SoT state machineKey idea
Key idea
1def find_candidates(lat, lng, k=2, limit=20):2 cell = geo.cell(lat, lng)3 cells = geo.kring(cell, k)4 drivers = []5 for c in cells:6 drivers.extend(supply.active_in(c)) # memory index7 drivers = filter_eligible(drivers) # status, capacity, constraints8 return rank_by_eta(drivers, lat, lng)[:limit]910GEO INDEX TRADEOFF11Index Pros Cons Pick when12---------- --------------------------- ------------------------ ---------------------13PostGIS SQL, rich geo queries hard shard at 1M+ movers low scale / polygons14H3 uniform hex neighbors approx; library ride/food dispatch15S2 strong geometry lib neighbor dist varies Google-centric stacks16Geohash simple rectangles; density skew prototypesMatching / dispatch
compare-and-set trip state CREATED→OFFERED→ACCEPTED) so two drivers cannot own one trip. On cancel: re-dispatch with new candidates; preserve rider UX with realtime status events.1def accept_trip(trip_id, driver_id):2 # atomic transition protects two-sided consistency3 ok = db.cas(4 trip_id,5 expect_status="OFFERED",6 expect_driver=driver_id,7 new_status="ACCEPTED",8 )9 if not ok:10 return 409 # already taken / expired offer11 notify_rider(trip_id)12 return 2001314# Trip state machine (sketch)15# REQUESTED → OFFERED → ACCEPTED → EN_ROUTE → IN_PROGRESS → COMPLETED16# ↘ EXPIRED/CANCELED → re-dispatch or failCommon mistake
“We can notify five drivers and let the first SMS reply win without coordination.”
ETA, surge, payments, fraud (altitude)
End-to-end ride narration
Senior signals vs mid-level
1TOPIC MID-LEVEL SENIOR2---------- -------------------------------- -----------------------------------------3Geo “Postgres + PostGIS” H3/S2 cells; resolution; boundary effects4Dispatch “find nearest driver” parallel offers; timeouts; re-dispatch;5 fairness; atomic claim6Surge “raise price when busy” cell supply/demand; smoothing; caps; UX7Failure “retry” GPS dropout, ETA degrade, payment fallback8Scale “more servers” GPS sampling budget; index write pathCapstone mock — 45 minutes
1MOCK CLOCK20–8 min Requirements + NFRs + non-goals (multi-city stretch)38–13 min Capacity: rides/day, location update strategy, geo query QPS413–20 min API + data model (trip, driver, location cells, payments)520–30 min High-level diagram + dispatch deep dive (CAS offer/accept)630–40 min ETA/surge OR multi-city OR failure modes (pick one deep)740–45 min Cost, metrics that page, 10× story, open questions89Self-score (1–5): framing · capacity-with-why · data model ·10dispatch correctness · tradeoffs · failure/ops · communication1112SENIOR TELLS (use across the whole track)131 Names a number in first 60s142 States NFRs explicitly153 Identifies hot partition + mitigation164 Calls out read/write asymmetry175 Consistency vs availability for the use case186 Failure modes + recovery197 Cost unit ($/ride, $/1k req)208 What NOT to build at this scale219 Observability: what pages; SLO2210 Evolution at 10× / 100×Common mistake
“If I finish a perfect multi-city active-active design I pass even if matching can double-book drivers.”
When the round turns AI-native
ml-system-design) for GPU memory, KV cache, embedding/index, eval harnesses, and model serving. Classic primitives here still apply to the non-ML edges (gateway, rate limit, multi-tenant, multi-region).Capstone win condition: you drive the clock, keep dispatch correct under concurrency, and narrate what breaks at 10× — without cargo-culting a dozen databases.
Interview answers — marketplace capstone
- 01Match in <5s? → H3 candidates + cached ETA + parallel offers + timeout ladder.
- 021M GPS pings/s? → sample/batch; Kafka; memory geo index; cold store async.
- 03Why H3? → hex neighbor uniformity (Uber reported rationale); still approx.
- 04Surge fairly? → cell supply/demand, smooth, cap, explain to rider.
- 05Driver gaming? → fraud signals, acceptance quality, cooldown.
- 06Payment fail mid-trip? → policy state machine; retry capture; support path.
- 07GPS drift? → last known + radius penalty; map-match when available.
- 08Traffic routing? → ETA service / map provider; cache segments.
- 091000 cities? → city-sharded control planes; local supply indexes.
- 10A/B dispatch? → shadow assignment + marketplace metrics (cancel, ETA error).
- 11Cold start city? → incentives/surge caps; seed supply; smaller rings.
- 12ETA accuracy? → road graph + traffic features; measure error, not vibes.
Data model and APIs
1POST /trips/estimate → {eta, price, surge}2POST /trips → {trip_id, status=REQUESTED}3POST /trips/{id}/accept4POST /trips/{id}/start|end5POST /drivers/{id}/location {lat,lng,ts,speed?}6GET /drivers/nearby?lat&lng&radius78drivers(id, cell_id, lat, lng, status, last_ping_at, vehicle_type)9trips(id, rider_id, driver_id, status, pickup, dropoff, prices, timestamps)10trip_events(trip_id, type, ts, payload) -- audit1112# Active supply in memory keyed by H3 cell; durable driver profile in OLTPDispatch deep dive — parallel offers
1DISPATCH SEQUENCE21. Create trip REQUESTED + fare quote id32. Query H3 ring → candidates43. Rank → top N54. Mark OFFERED (version++) + notify drivers65. Accept: CAS OFFERED→ACCEPTED for that driver only76. Revoke outstanding offers87. Track EN_ROUTE → IN_PROGRESS → COMPLETED98. Payment capture; async analytics1011If step 5 is not atomic, you will double-dispatch under load.Cross-cutting synthesis for the whole track
1FIVE INSIGHTS TO CARRY OUT OF TRACK 221. Hot partitions are the recurring villain (celeb, viral code, busy channel, surge cell).32. Read/write asymmetry shapes architecture before brand names do.43. Caches are state with a staleness budget, not free performance.54. Quantify before you architect; label numbers reported vs heuristic.65. Production systems evolve (Cassandra→Scylla, pure push→hybrid, squares→H3).78FANOUT CHEAT9one-to-many normal → push10celeb/power-law → hybrid11DM → small fanout either way12chat rooms → stream + per-channel order13marketplace → geo pull + parallel offerCheckpoint
Two drivers hit accept on the same trip at once. What prevents double dispatch?
Checkpoint
Active drivers send GPS at 1 Hz. Naively writing every ping to a relational primary will…
Checkpoint
Rider requests a ride; 50 drivers within 2 km; need sub-5s match. Best approach?
Checkpoint
Surge pricing design needs to…
Checkpoint
Interviewer pivots: “Now add an LLM that rewrites support tickets for trip disputes.” What should you do in this curriculum’s spirit?
Marketplace math + mock scorecard
1HEURISTIC CAPACITY2rides/day 5e6 → ~60 start QPS avg; city peaks 5–10x3drivers active * 1Hz GPS → millions/s if naive → MUST sample/batch4geo queries: H3 cell + k-ring → rank ETA → offer top N5match QPS is usually << GPS ingest QPS — size the location pipe first67REPORTED ANCHORS8Uber H3: hex hierarchy, uniform neighbors (2018 blog)9Uber push platform: >70k QPS class gRPC streaming (2020 blog)10DoorDash: optimization + ML scoring for dispatch (eng blog)1112CORRECTNESS CORE13trip state machine + CAS accept → single assignment14parallel offers with revoke losers15payment idempotency keys; auth hold → capture1617SURGE18per-cell supply/demand on interval; smooth; cap; show upfront1920MOCK 45m SELF-SCORE (1-5 each)21framing | capacity-with-why | data model | dispatch CAS22tradeoffs | failure/ops | communication23FAIL if double-dispatch possible even with pretty multi-region2425SENIOR TELLS (track-wide)26number in 60s; NFRs; hot key; R/W asymmetry; failure+cost;27what NOT to build; what pages; evolution at 10x2829AI PIVOT30same framework on edges; ml-system-design for model gutsCould you run the 45-min marketplace mock end-to-end and self-score without freezing on dispatch races?
Takeaways
- Marketplace SD = geo supply index + atomic match + ETA/surge + payments idempotency.
- Budget location updates; cell indexes beat scans.
- Two-sided consistency is the correctness heart (CAS/claim).
- Mock rubric rewards process and dispatch depth over logo density.
- AI-native pivots → same framework, companion `ml-system-design` for ML/LLM guts.
Track complete: primitives → framework → senior layer → shortener → feed → chat → rate limit/search → marketplace. Rehearse mocks aloud; keep numbers decision-linked; label production figures reported vs heuristic.
Sources
Free to read · better with Enzo
Learn it with Enzo
Save your progress, answer the checkpoints, and let Enzo quiz you on what you just read.