Back to Engineering Notes

Preventing Multiple Drivers from Winning the Same Ride

July 2026·10 min read

In a traditional ride-hailing application, the first driver to accept a ride usually wins.

Our platform followed a different model.

Instead of immediately accepting requests, nearby drivers could submit offers. Riders would then review those offers and choose the one they preferred.

That marketplace experience introduced a different engineering challenge.

The difficult part wasn't collecting offers.

It was ensuring that once a rider selected one, every other possible outcome immediately became impossible.

The Problem

A rider may receive several offers within a short period of time.

From the user's perspective, selecting one offer should instantly assign the ride.

Behind the scenes, however, multiple things can happen almost simultaneously.

The rider may accidentally tap the confirmation button twice.

The mobile application may retry the same request because of a temporary network interruption.

Another server instance may already be processing the assignment.

Meanwhile, other drivers are still capable of submitting offers until the assignment is completed.

Without proper synchronization, the platform could produce inconsistent outcomes such as:

  • assigning multiple drivers
  • accepting an offer after another driver has already been selected
  • leaving rejected offers marked as pending
  • overwriting an existing ride assignment
  • producing conflicting notifications for riders and drivers

These issues rarely appear during development but become much more common under production traffic.

The goal was simple.

No matter how many concurrent requests arrived, every ride should end with exactly one assigned driver.


Assignment Is a Critical Operation

The actual database update is relatively small.

The important part is the state transition.

A ride begins in a state where multiple offers are valid.

Once the rider makes a decision, only one of those offers should remain valid while every other offer immediately becomes obsolete.

Instead of treating assignment as a normal update, the platform treats it as a critical section that only one execution path can enter at a time.

Decision 01
Protect the business transition instead of the database write

The important guarantee isn't that a document gets updated.

The guarantee is that a ride can only transition from waiting for offers to assigned once.

Protecting that business transition keeps every downstream operation consistent.


One Winner, Everyone Else Loses

Once an offer is selected, the rest of the marketplace must immediately reflect that decision.

The selected driver becomes responsible for the ride.

Every competing offer is marked as unavailable.

Drivers who submitted unsuccessful offers are notified that the ride has already been assigned.

From that moment onward, every future operation references the assigned driver rather than the collection of competing offers.

This keeps the remainder of the ride lifecycle significantly simpler.

Features such as navigation, messaging, OTP verification, payment processing, and ride completion no longer need to reason about competing offers because only one assignment can exist.


Multiple Layers of Protection

Concurrency bugs are expensive because they often appear only under heavy production traffic.

For that reason, the assignment process wasn't protected by a single mechanism.

The platform combines synchronization at the application layer with validation inside the database update itself.

Even if two requests somehow reach the assignment logic at nearly the same time, each layer independently verifies that the ride is still eligible to transition into its next state.

Rather than assuming previous checks remain valid, every critical step validates its own assumptions before continuing.

Decision 02
Independent validation makes concurrency more resilient

Distributed systems should not rely on a single safety mechanism.

Each critical operation validates that the ride is still in the expected state before applying changes, providing additional protection against race conditions and unexpected retries.


Keeping the Marketplace Consistent

Ride assignment doesn't only affect the ride itself.

Several other parts of the platform also depend on that decision.

Once a ride is assigned:

  • competing offers must be rejected
  • drivers must receive updated availability
  • rider notifications must reflect the chosen offer
  • the ride lifecycle begins
  • future driver offers must be ignored

Treating assignment as a single protected operation ensures every dependent system observes the same outcome.

No component can continue operating under the assumption that multiple offers are still active after the rider has already made a decision.


Why This Matters

Concurrency issues are difficult to detect because they rarely occur during normal testing.

Most of the time, everything appears to work correctly.

Problems only emerge when multiple users, retries, or server instances interact at exactly the wrong moment.

Designing for those situations from the beginning is significantly easier than recovering from inconsistent production data later.

By modeling ride assignment as a protected state transition rather than a simple database update, the platform guarantees a single outcome regardless of how many concurrent requests arrive.


Lessons Learned

Marketplace workflows naturally allow many possible outcomes.

Eventually, those possibilities must collapse into exactly one.

Ride assignment is that point.

Treating it as a critical state transition rather than an ordinary update made the dispatch pipeline predictable, simplified every downstream workflow, and ensured the platform could safely handle concurrent activity without sacrificing consistency.

ConcurrencyDistributed SystemsRedisSystem Design