Design Uber's ride matching, real-time location tracking, driver dispatch, and surge pricing.
Published April 28, 2025
Functional: Request rides, Match driver, Real-time location tracking, Pricing (surge), ETA, Trip history Scale: 19M trips/day, 5M drivers, 100M users, location updates every 5s = 1M GPS updates/sec during peak
Find available drivers near a rider's location. The challenge: 1M+ location updates per second from moving vehicles.
Approach: Use Redis with geospatial commands (GEOADD, GEORADIUS).
// Driver updates location every 5 seconds
redis.geoadd("drivers:available", longitude, latitude, driverId);
// Find drivers within 2km of rider
List<GeoRadiusResponse> nearbyDrivers = redis.georadius(
"drivers:available",
riderLong, riderLat,
2.0, GeoUnit.KM,
GeoRadiusParam.geoRadiusParam()
.withDist() // include distance
.count(10) // max 10 drivers
.sortAscending() // nearest first
);
Redis Geospatial internally uses geohash — encodes lat/lng into a sortable string, enabling efficient proximity queries with sorted sets.
public DriverMatch findDriver(RideRequest request) {
List<Driver> nearby = locationService.getNearbyDrivers(
request.origin, 2.0 /* km */, 10 /* max */);
for (Driver driver : nearby) { // sorted by distance
// Try to lock driver (prevent double-booking)
if (redis.setnx("driver:locked:" + driver.id, request.id) == 1) {
redis.expire("driver:locked:" + driver.id, 30); // 30s lock
notifyDriver(driver, request);
return new DriverMatch(driver, request);
}
}
// No drivers available — return to queue for retry
return null;
}
Driver App → WebSocket → Location Service → Redis GEO
↓ also
Kafka (for trip tracking, analytics)
↓
Cassandra (trip history)
// Surge multiplier based on supply/demand
public double getSurgeMultiplier(GeoCell cell) {
double demand = getActiveRequests(cell); // riders requesting
double supply = getAvailableDrivers(cell); // idle drivers
double ratio = demand / supply;
if (ratio < 1.2) return 1.0; // normal pricing
if (ratio < 1.5) return 1.5;
if (ratio < 2.0) return 2.0;
return Math.min(ratio, 3.0); // cap at 3x
}
Geohash divides Earth into rectangular cells at various precisions:
Length 4: ~39km × 20km
Length 6: ~1.2km × 0.6km ← good for driver density
Length 8: ~38m × 19m ← precise driver location
Surge pricing uses length-5 cells (~5km) to aggregate supply/demand
trips: trip_id, rider_id, driver_id, origin, destination, status, fare, surge_multiplier, created_at
drivers: driver_id, name, license, rating, car_info, is_available
location_history: driver_id, lat, lng, timestamp (Cassandra time-series)
SETNX prevents two riders from being matched to the same driver.