Why this matters
- Ride-hailing, food delivery, and StreamHub's "join nearby watch party" all need sub-second nearest-neighbour lookup over millions of moving entities.
- Brute-force distance calculation against every driver is O(n) per request — unacceptable at Uber scale (millions of drivers online).
- Geohash grids or quad-trees partition space so you only scan a handful of cells, not the entire fleet.
Proximity service building blocks
- Location ingestion — drivers push GPS updates via WebSocket or UDP every 2–4 seconds.
- Spatial index — geohash prefix tree, Redis GEO, or custom quad-tree storing driver ID + coordinates.
- Matching engine — finds top-K nearest available drivers, applies business rules (rating, vehicle type).
- Dispatch queue — handles concurrent match requests; first-accept-wins or broadcast-to-top-3.
- ETA service — routing engine estimates pickup time for each candidate.
High-level architecture
Proximity / ride-matching
POST /v1/location/update
Authorization: Bearer <driver_token>
Content-Type: application/json
{
"driver_id": "drv_8f2a",
"lat": 12.9716,
"lng": 77.5946,
"heading": 142,
"timestamp_ms": 1740847200000
}
POST /v1/rides/request
Content-Type: application/json
{
"rider_id": "rdr_3c91",
"pickup": { "lat": 12.9700, "lng": 77.5900 },
"vehicle_type": "standard"
}
Geohash grid indexing
Geohash encodes lat/lng into a base-32 string. Shared prefixes mean geographic proximity.
import geohash2
lat, lng = 12.9716, 77.5946
hash_6 = geohash2.encode(lat, lng, precision=6) # "tdr1w3" — ~1.2 km cell
hash_7 = geohash2.encode(lat, lng, precision=7) # "tdr1w3y" — ~150 m cell
Store drivers in Redis sorted sets keyed by geohash prefix:
GEOADD drivers:tdr1w3 77.5946 12.9716 drv_8f2a
GEORADIUS drivers:tdr1w3 77.5900 12.9700 2 km ASC COUNT 10
Matching flow
| Aspect | Strategy | Trade-off |
|---|---|---|
| Broadcast to top-3 | Send ride offer to 3 nearest drivers | Fast match; may overload popular drivers |
| Serial dispatch | Offer to #1, wait 10s, then #2 | Fairer; slower average match time |
| Batch matching | Optimise all pending rides every 5s | Better global assignment; higher latency |
| Surge pricing | Raise price in high-demand cells | Balances supply/demand dynamically |
Broadcast to top-3
StrategySend ride offer to 3 nearest driversTrade-offFast match; may overload popular driversSerial dispatch
StrategyOffer to #1, wait 10s, then #2Trade-offFairer; slower average match timeBatch matching
StrategyOptimise all pending rides every 5sTrade-offBetter global assignment; higher latencySurge pricing
StrategyRaise price in high-demand cellsTrade-offBalances supply/demand dynamically
Uber uses a mix: serial dispatch with surge multipliers per geohash cell.
Handling location staleness
Drivers go offline, lose signal, or stop updating. A location older than 30 seconds is unreliable.
{
"driver_id": "drv_8f2a",
"lat": 12.9716,
"lng": 77.5946,
"updated_at": "2026-04-01T14:32:10Z",
"status": "available",
"ttl_seconds": 30
}
Background sweeper removes expired entries from the spatial index. On match, reject candidates whose updated_at exceeds the TTL.
Scale estimates
| Metric | Estimate |
|---|---|
| Active drivers globally | 5M peak |
| Location updates per second | 5M / 3s ≈ 1.7M writes/s |
| Match requests per second | ~50K peak |
| Geohash cells queried per match | 9 (cell + neighbours) |
| Redis memory per driver | ~100 bytes → 500 MB total |
Active drivers globally
Estimate5M peakLocation updates per second
Estimate5M / 3s ≈ 1.7M writes/sMatch requests per second
Estimate~50K peakGeohash cells queried per match
Estimate9 (cell + neighbours)Redis memory per driver
Estimate~100 bytes → 500 MB total
Shard the spatial index by city or geohash prefix to keep each Redis cluster under 1M ops/s.
WebSocket connection management
Drivers maintain persistent WebSocket connections to regional gateway servers. On disconnect, mark driver unavailable immediately.
# Connection gateway scaling (Kubernetes)
apiVersion: apps/v1
kind: Deployment
metadata:
name: location-gateway
spec:
replicas: 20
template:
spec:
containers:
- name: gateway
resources:
requests:
cpu: "2"
memory: 4Gi
env:
- name: MAX_CONNECTIONS_PER_POD
value: "50000"
Quick recall
Everything you need if you only revisit this box.
- Proximity = frequent location writes + fast spatial lookup, not full-table scans.
- Geohash prefixes or Redis GEO partition space into queryable cells.
- Match flow: query cell + neighbours → filter by status/TTL → rank by distance → dispatch.
- Stale locations (>30s) must be evicted; disconnected drivers marked unavailable instantly.
- Shard spatial indexes by region; 1.7M location writes/s requires horizontal partitioning.
Test yourself
Answer these before moving on — recall is what makes it stick.