CHAPTER 19
Design a Ride-Hailing / Proximity Service
Real-time location, geo-indexing, matching and trip state
Source: PDF pp. 56–57
Core PDF content
| Functional requirements | Non-functional requirements and scale |
|---|---|
| - Rider requests a ride from A to B; see ETA and price - Match with a nearby available driver; driver accepts or declines - Live tracking of the trip; payments on completion - Drivers send location continuously | - Match within a few seconds; location freshness < 5 s - Highly available; no double-booking of a driver - 10 M trips/day; 3 M drivers online sending location every 4 s - Global, with strong city-level locality |
| Quantity | Estimate | Design implication |
|---|---|---|
| Trips | 10 M/day = ~115/s (peak ~500/s) | Trip writes are modest |
| Location updates | 3 M drivers / 4 s = ~750,000 updates/s | The real scale problem: keep only the latest position in memory; stream history to Kafka |
| Nearby queries | One per ride request (+ ETA refreshes) | Spatial index must answer in single-digit ms |
| Location history | 750 k x ~50 B = ~3 TB/day | Data lake for analytics, not the serving path |
Deep dives p. 57
- Geo index: divide the world into cells (H3 hexagons or S2/geohash at ~1 km resolution). Keep cell_id -> set(driver_id, lat, lon, status, ts) in memory (Redis Cluster or a custom service), sharded by cell/city. A nearby query reads the rider's cell plus the surrounding ring(s) and expands until enough candidates are found. Drivers moving between cells are updated atomically; stale entries expire by TTL.
- Location ingestion: drivers send compact updates over a persistent connection (WebSocket/MQTT). Overwrite only the latest position; append history to Kafka asynchronously. Adaptive frequency (slower when idle, faster during a trip) cuts load.
- Matching: (1) find candidates in the ring; (2) rank by road ETA (not straight-line distance) from a routing engine, plus driver rating/acceptance rate; (3) send the offer to the best driver with a timeout (10-15 s), then the next. At scale, batch matching (solve an assignment problem for riders and drivers in a short window) beats greedy first-come matching.
- No double booking: the driver's state is a small state machine (available -> offered -> assigned -> on_trip). Transition with an atomic compare-and-set (UPDATE ... WHERE state = 'available', or a Redis Lua script); if it fails, try the next candidate. Offers expire so a driver who ignores one is released.
- Trip service: the trip state machine (requested, matched, arrived, started, completed, cancelled) lives in a strongly consistent store, sharded by trip_id/region; every transition is an event published via outbox.
- ETA and pricing: road graphs with contraction hierarchies or precomputed tiles; live traffic from the aggregated driver pings. Surge pricing = supply/demand ratio per cell, computed by a stream job over sliding windows and smoothed to avoid oscillation.
- Failure handling: if the geo-index shard fails, drivers re-announce within seconds (pings repopulate it); apps retry with backoff; regions operate independently (a city's data stays in its region).
Review questions for this chapter have not been written and reviewed yet. The chapter text above is the full source content.
Try it
References
System Design & AI System Design: The Complete Guide — 2026 Edition, pp. 56–57; figures 40.
Tip: use ← and → keys to move between chapters.