Case study
Design Uber-style Ride Matching
Match riders to nearby drivers in seconds using geospatial indexes and real-time location streams.
geospatialmatchingreal-time
Requirements
- Functional: request ride; match driver; track trip; fare estimate; payment
- Real-time driver location on map; ETA updates
- Surge pricing during high demand
- Non-functional: match p99 < 15 sec; location update every 3 sec
- Scale: 50M riders; 5M active drivers; 10M rides/day
Back-of-envelope Estimation
| Metric | Calculation | Result |
|---|---|---|
| Rides/day | 10M | Given |
| Match QPS | 10M / 86,400 | ~115/sec avg; ~500/sec peak |
| Location updates | 5M drivers / 3 sec | ~1.7M updates/sec |
| Location write bandwidth | 1.7M × 50 B | ~85 MB/sec |
| Geospatial queries | ~500/sec match × 20 cell lookups | ~10K geo queries/sec |
API Design
| Endpoint | Description |
|---|---|
| POST /v1/rides | { pickup, dropoff, rideType } → { rideId, estimate } |
| WS /v1/drivers/location | Stream { lat, lng, heading } every 3 sec |
| GET /v1/rides/{id} | Status: matching | en_route | in_progress | completed |
| POST /v1/rides/{id}/cancel | Cancel with fee rules |
| GET /v1/surge?lat=&lng= | Current surge multiplier for cell |
Data Model
| Entity | Storage |
|---|---|
| drivers_live | driver_id → { lat, lng, status, geohash } — Redis GEO |
| rides | ride_id, rider_id, driver_id, status, pickup, dropoff, fare — SQL |
| surge_cells | geohash_prefix → multiplier — Redis, updated every 1 min |
| location_history | driver_id, ts, lat, lng — Kafka → time-series DB |
High-level Design
Rider ──► Ride Service ──► Surge check ──► Matching Service
│
Query Redis GEO (drivers in 2km radius)
│
Rank by ETA (routing API) ──► notify top 3 drivers
│
First accept ──► assign ──► trip state machineDeep Dive: Geospatial Index
| Structure | Use case |
|---|---|
| Geohash grid | Partition drivers into cells; query adjacent cells |
| QuadTree | Dynamic density; good for urban hotspots |
| Redis GEOADD/GEORADIUS | Simple; sub-ms radius queries |
Update driver location in Redis on each heartbeat. Matching queries 9 neighboring geohash cells (~1 km precision). Filter to available drivers only.
Deep Dive: Surge Pricing
Every minute, compute supply/demand ratio per geohash-5 cell: demand = pending requests; supply = idle drivers. Multiplier = f(ratio), capped at 3×. Cached in Redis; riders see estimate before confirm.
Failure Modes & Monitoring
| SLO | Target |
|---|---|
| Match time p99 | < 15 sec |
| Location freshness | < 5 sec stale max on map |
| Trip state consistency | Strong for billing transitions |
- Split-brain driver assignment — use distributed lock on driver_id during match
- Monitor: unmatched ride rate, driver accept rate, location pipeline lag