Driver APIs
Update driver status -> AVAILABLE, RESERVED, ON TRIP
POST /drivers/{id}/status
request: {
status
}
Location Update
POST /drivers/{id}/location
req:{
lat,
long,
timestamp
}
Accept ride
POST /rides/{id}/accept
Rider APIs
Request Ride/trip
POST /rides
req:{
riderId,
pickup: {
lat,
long
}
dest:{
lat,
long
}
}
Get ride status
GET /rides/{id}
Update ride status ->Requested, Matched, In progress, Completed, Cancelled
POST /ride/{id}/status
API Gateway
Acts as the entry point for all client requests.
Responsibilities:
Distributes traffic across multiple instances of all services to improve scalability and availability.
Rider Service
Driver service
Location Service
Trip Service
Payment Service
Dispatch Service
Geo Index Service
Websocket gateway
Kafka
PostreSQl
Redis
We use hybrid apporach for Database, using SQL -> PostreSQL for stroing strong consisten data where transactrions matter like Users, Drivers, Trips etc and NOSqL -> redis for stroing geo index locations of driver
Riders
id
name
rating
Drivers
id
name
status -> AVAILABLE, RESERVED, ON TRIP
vehicle type
rating
Trip
id
riderId
driverId
pickup_lat
pickup_long
dest_lat
dest_long
status -> Requested, Matched, In progress, Completed, Cancelled
UserPaymentMethods
id
userId
method_id -> id from payment provider
providerId
Payments
id
tripid
amount
status -> PENDING, SUCCESS, FAILEd
provider_id
Geo Index -> stored in Redis
structure:
Driver -> cell mapping
eg:
der334s -> driver1, driver2, driver 3
Driver latest location (comes from the api)
driver1 -> lat, long, timestamp
We will now deep dive into these topics
This is Uber's hardest problem.
User requests ride.
Trip Service:
Trip
status = REQUESTED
Publishes:
TripRequested
to Kafka.
Consumes:
TripRequested
Gets nearby drivers from Redis.
Score:
Score =
ETA Weight
Distance Weight
Acceptance Rate Weight
Driver Rating Weight
Produces:
Top 20 Drivers
Instead of:
Offer one driver
Wait 5 sec
Offer next
which is slow,
Use:
Batch dispatch
Offer to:
Top 3 drivers
simultaneously.
First acceptance wins.
This is exactly the kind of optimization seniors discuss.
Driver A accepts.
Driver B accepts 100ms later.
Need atomic reservation.
UPDATE Drivers
SET status='RESERVED'
WHERE id=123
AND status='AVAILABLE';
Check:
rows_updated == 1
Only one request succeeds.
Everyone else loses.
No distributed lock required.
REQUESTED
|
DISPATCHING
|
MATCHED
|
IN_PROGRESS
|
COMPLETED
Driver:
AVAILABLE
|
RESERVED
|
ON_TRIP
|
AVAILABLE
Why Not SQL?
Bad:
SELECT *
FROM Drivers
WHERE distance < 2km
Millions of drivers.
Doesn't scale.
GeoHash
Convert:
lat,long
into
geo cell
Example:
dr5ru
Redis:
dr5ru
-> driver1
-> driver2
-> driver3
Expanding Search
No drivers?
Search:
Cell
+
Neighbor Cells
+
Larger Radius
Downtown Toronto:
5000 drivers
in same cell.
One Redis shard becomes hot.
Solution:
Dynamic GeoHash Precision
Busy areas:
smaller cells
Sparse areas:
larger cells
This is a very strong senior signal.
200K updates/sec.
Driver App:
Location Update
every 5 sec.
Location Service
Stateless.
Publishes:
LocationUpdated
to Kafka.
GeoIndex Service
Consumes.
Updates Redis.
Because Redis is not source of truth.
Kafka is.
If Redis dies:
Replay Kafka
Rebuild index.
This is a very strong senior point.
Driver App
GPS Update
↓
Kafka
↓
WebSocket Gateway
↓
Rider
WebSocket Gateway consumes:
LocationUpdated
events.
If driver has active trip:
push location
to rider.
Problem:
Location 100
Location 101
Location 102
Network delay.
Receive:
100
102
101
Wrong ordering.
Solution
Include:
sequence_number
timestamp
Store:
latest sequence seen
Discard older updates.
-> Driver Disconnect
Heartbeat missing:
30 sec
Mark offline.
Remove from matching pool.
-> Dispatch Service Crash
Dispatch state stored in DB.
Kafka retains event.
New instance resumes.
Redis cluster.
Primary
+
Replicas
Automatic failover.
If full Redis loss:
Replay Kafka location stream
Rebuild index.
Replication Factor:
3
Leader election.