UberEats: Deconstructing a Big Service
One order, three sides, and a phone that can lose its connection.
Concept. UberEats must keep the diner, driver, and restaurant in agreement while the service runs across many machines. Uber's fulfillment platform uses a distributed SQL database for shared state. Its driver app saves unfinished actions on the phone.
Intuition. One burrito order changes several pieces of state. The database applies the related changes together and makes one driver assignment win. If the phone loses its connection, it saves the driver's action and sends it when the connection returns.
Figure: UberEats: shared state for three sidesA simplified model of a three-sided order. The diner, driver, and restaurant apps hold temporary state. A distributed transactional database coordinates the shared order, assignment, and inventory records. One transaction applies the related changes together. Green marks the persistent shared state; the apps hold only volatile state.
Figure 1. The client apps hold only temporary, volatile in-memory state. The green frame is the persistent shared state: a transactional database that coordinates the order, assignment, and inventory records across all three sides, so related changes apply together.
One order, three sides
A mobile app holds the state needed to draw its current screen. The order itself must outlast any one app, process, or machine. Uber's fulfillment platform stores that shared state in a distributed database.
Use one transaction to charge the diner, assign a driver, and decrement the restaurant's inventory. The database applies all three changes or none of them. When two drivers try to accept the same order, the database allows one update to finish and rejects the conflicting update.
Figure: One order. Three records. All or none.A simplified order transaction updates the payment, driver assignment, and inventory together. A successful order applies all three changes. A failed order leaves all three unchanged. The database also rejects a conflicting driver assignment so two competing updates cannot both finish successfully.
| DINER | DRIVER | RESTAURANT |
|---|---|---|
| balance charged | exactly one assigned | inventory decremented |
| COMMIT | ANY ONE FAILS |
|---|---|
| All three apply together. | None of them do. |
Figure 2. One transaction updates the payment, driver assignment, and inventory together. A successful order applies all three changes. A failed order leaves all three unchanged. Competing driver updates cannot both finish successfully.
Shared state across many machines
Uber reports that its Fulfillment platform handles billions of database transactions per day. It uses Google's Cloud Spanner, a distributed SQL database, to keep related fulfillment records consistent across many machines.
The applications ask for changes. The database stores the shared result. That separation lets a diner, driver, and restaurant see one order without trusting any single phone to hold the authoritative copy.
The disconnected edge
Figure: The disconnected edge: save now, send laterThe driver app saves an unfinished action and its latest state to disk when the phone loses its connection. After reconnecting, the app sends queued actions to the server and updates its local state if the server returns a different result. Green marks the shared server state.
Figure 3. The driver app saves unfinished actions to disk so they survive an app restart. It sends the queued actions after reconnecting and updates its local state to match the server.
A driver in a parking garage or a dead zone still needs to mark a pickup, complete a delivery, or update a status. Uber calls its solution Optimistic Mode. The app saves the action and its latest state to disk, updates the screen immediately, and sends the action to the server when the connection returns.
-
Save the action. The phone writes unfinished actions to disk so they survive an app restart.
-
Keep the workflow moving. The app shows the expected result without waiting for the network.
-
Send it later. When the connection returns, the app sends queued actions to the server.
-
Correct disagreements. If the server returns a different result, the app updates its local state.
Takeaway: Distributed transactions coordinate shared fulfillment state at Uber's scale, while saved actions let the driver app continue through short network failures.
Going deeper
Why Uber moved this work into the database
Uber's earlier fulfillment system spread coordination across application code and several storage systems. As delivery flows became more complex, that design became difficult to scale and debug.
Uber moved the shared fulfillment state to Cloud Spanner. The database handles related updates together while distributing the data across many machines.
Optional reading: the published systems
-
Uber's Fulfillment Platform. Why Uber moved fulfillment state to a distributed SQL database.
-
How Uber's Driver App Overcomes Network Lag. How the app saves actions to disk and reconciles them after reconnecting.
-
How Uber Serves Over 40 Million Reads Per Second. How Uber's Docstore combines distributed storage with an integrated cache.