Free preview

Matching and Trip Management

In one line: these three components are described in a few sentences each, and they contain the design's most conspicuous gap — how a driver is actually chosen, and how two riders are prevented from being given the same one.

The three services

ServiceDetail
Request vehicleThe rider contacts this service to request a ride and adds the drop-off location. It communicates with the find driver service to book a vehicle and retrieve its details using the location manager service
Find driverFinds the driver who can complete the trip. It sends the selected driver's information and trip details back to the request vehicle service, and contacts the trip manager to manage trip information
Trip managerManages all trip-related tasks. It creates a trip record in the database and stores all the trip information

Request vehicle and find driver are the same operation split in two

Read the descriptions carefully and it is hard to say what request vehicle does that find driver does not. It receives the request, calls find driver, and returns the result.

There is a defensible reason for the split, and it is the same one Lesson 4 gave for the service decomposition generally — lifetime and shape of work:

Request vehicle is rider-facing and session-oriented. It holds the rider's connection through the "Waiting for driver" state, which may last tens of seconds across several matching attempts.

Find driver is stateless and computational. Given a location and constraints, produce a candidate. It can be called repeatedly by the rejection loop without holding anything.

That separation matters because the rejection loop from Lesson 4 iterates: request vehicle owns the loop and the rider's wait; find driver answers one question per iteration.

A service that holds a user's attention should be separate from a service that does the work, because they fail differently and scale differently. Still, in an interview it is worth saying you would merge them unless the loop ownership justifies the hop — an extra network call inside a latency-critical loop is not free.

What "finds the driver who can complete the trip" leaves out

The matching algorithm is the product, and it gets one sentence

"The find driver service finds the driver who can complete the trip."

That sentence contains the entire matching problem. Lesson 2 flagged that the requirements mention ranking once and drop it; this is where the omission becomes concrete.

Here is what a real matcher decides, none of which the design specifies:

By what criterion? "Nearest" is the obvious answer and it is wrong. The nearest driver by straight-line distance may be across a river, pointed the wrong way on a one-way street, or stuck behind a level crossing. You match on ETA, not distance — and the ETA service from Lesson 9 already exists to compute exactly that. Using it for matching rather than only for display is the single biggest improvement available.

One at a time, or a batch? Matching each request greedily on arrival produces a worse global assignment than batching requests over a few seconds and solving the assignment problem across them. Consider two riders and two drivers where greedy matching sends driver A to rider 1 — leaving driver B a long way from rider 2 — when the swapped assignment serves both faster.

With what fairness constraints? A matcher that always prefers highly-rated drivers starves everyone else, and drivers leave the platform. Idle time, acceptance rate, and earnings balance are real inputs.

Looking only at idle drivers? A driver two minutes from finishing a nearby trip may beat an idle driver ten minutes away. Real systems match against soon-to-be-available supply, not just current supply.

The design's own open question — "what if two drivers are at the same distance?" — dissolves once you accept that distance was never the criterion. You break the tie on ETA, then on idle time and acceptance rate.

Say this in an interview. Matching is where ride-hailing companies actually compete, and treating it as a nearest-neighbour lookup skips the part that matters.

Nothing prevents the same driver being offered to two riders

This is the gap that bites in production and the design does not mention it.

Two riders in the same neighbourhood request simultaneously. Both matching attempts query the quadtree, both see the same idle driver, both send an offer. The driver accepts one. The other rider's match silently evaporates and they restart the loop having already waited.

Worse, if the design allows an offer to be considered a match before acceptance, you could create two trip records for one driver.

The fix is a soft lock — a short-lived reservation on the driver, held while the offer is outstanding:

1. Select a driver candidate
2. ATOMICALLY mark them RESERVED with a short TTL
3. Send the offer
4a. Accept  -> transition to MATCHED, clear the reservation
4b. Reject or timeout -> clear the reservation, driver returns to the pool

The TTL is what makes it safe. A hard lock would strand a driver forever if the offering service crashed; a lease expires on its own. Same reasoning as task scheduling's leases on task assignment.

The atomic step needs a single point of truth. The hash table from Lesson 6 — or Redis, from Lesson 11 — is the natural home, using a compare-and-set so exactly one matcher wins.

Any system that assigns a scarce physical resource needs a reservation, not just a selection. Selection tells you what you want; reservation is what stops someone else taking it.

The trip manager

The trip manager service manages all the trip-related tasks. It creates a trip record in the database and stores all the trip information.

This is the owner of the state machine, and that is why it is separate

Lesson 2 observed that seven of the nine functional requirements are transitions in one lifecycle. The trip manager is what owns that lifecycle.

REQUESTED -> MATCHED -> ACCEPTED -> ARRIVED -> IN_PROGRESS -> COMPLETED -> PAID

Its real job is not "storing trip information" — it is being the single authority on what state a trip is in, which gives it three responsibilities the description does not name:

Rejecting invalid transitions. endTrip on a trip in MATCHED is nonsense. A trip that never started cannot complete.

Making transitions idempotent. Lesson 5 noted that a retried endTrip must not charge twice. The state machine is the defence: COMPLETED is terminal, so a second call succeeds silently.

Being the design both parties read. Lesson 2's strong-consistency requirement lands here specifically. Rider and driver must not disagree, and they agree by both deriving their view from one authoritative record — which is why Lesson 10 puts in-progress trips in MySQL rather than Cassandra.

When two parties must agree about a long-lived process, give the process an owner. Distributing the state machine across the services that trigger its transitions is how you get a rider whose app says "arriving" and a driver whose app says "cancelled."

The trip record is also the billing record and the audit record

Worth noticing what else depends on this one row.

Billing. Lesson 12's payment service computes the fare from trip data — duration, distance, vehicle type.

Fraud detection. Lesson 13's RADAR examines trips for inflated time or distance, so the trip record plus its route trace is the evidence.

Dispute resolution. "The driver took a long route" is settled by the stored trace.

That gives the trip record a property most records in this course do not have: it is legally and financially consequential, and it must be immutable once completed. A completed trip that can still be edited is a fraud vector and an accounting problem.

Which is exactly why Lesson 10 moves completed trips to Cassandra and keeps only in-progress ones in MySQL. In-progress trips are mutable state needing consistency; completed trips are immutable facts needing durability and scale. Different requirements, different stores — and the transition between them happens at COMPLETED.

One driver, one request at a time

Matching has a correctness requirement that is easy to state and easy to get wrong: a driver must never be offered two rides simultaneously, and a rider must not be matched to a driver already committed elsewhere.

An application-level check fails for the usual reason: the gap between reading the driver's status and writing it is exactly where a second instance slips through. More instances widen the window.

A distributed lock keyed on the driver id closes it. Acquire before sending the offer; if the acquisition fails, that driver is already engaged and the matcher moves on.

The TTL is the part that matters most, and it should equal the acceptance window. A driver who never responds — phone in a pocket, app backgrounded — would otherwise stay locked forever, silently removed from the supply pool. With a TTL the lock expires on its own, and the driver becomes matchable again with no cleanup process and no operator intervention.

The honest cost: this puts an in-memory store on the critical path of every match, so its availability becomes the matcher's availability. Worth naming, along with the fallback — if the lock service is unreachable you either stop matching or accept occasional double-offers, and which you choose is a product decision.

Key takeaway

Request vehicle and find driver split by lifetime — one holds the rider's wait through the rejection loop, the other answers one stateless question per iteration. The matching algorithm gets one sentence and is the product: match on ETA rather than distance, batch requests to solve the assignment problem rather than matching greedily, include fairness and soon-to-be-available supply, and note the "two drivers equidistant" question dissolves because distance was never the criterion. The design's sharpest gap is that nothing stops one driver being offered to two riders — that needs a soft lock with a TTL, atomic in Redis, because any system assigning a scarce physical resource needs reservation, not just selection. And the trip manager owns the state machine, which is what makes transitions idempotent and what makes both parties agree.

Next: computing the ETA.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue