Skip to content

Ride Flow

A complete ride moves through seven possible statuses. This page documents each transition, who triggers it, and what side effects occur.


State machine

stateDiagram-v2
    direction LR

    [*] --> SEARCHING : Customer POSTs /api/rides
    SEARCHING --> ACCEPTED : Driver accepts (HTTP or WS)
    SEARCHING --> CANCELLED : 30s timer expires or customer cancels
    ACCEPTED --> ARRIVED : Driver POSTs /api/driver/arrived
    ACCEPTED --> CANCELLED : Customer cancels
    ARRIVED --> IN_PROGRESS : Driver POSTs /api/driver/start
    IN_PROGRESS --> COMPLETED : Driver POSTs /api/driver/complete

Status reference

Status Set by Description
SEARCHING RideMatchingService Async matching started; drivers being notified
ACCEPTED DriverRideService.assignRide() Driver accepted; 30s timer cancelled
ARRIVED DriverRideService.updateRideStatusToArrived() Driver at pickup location
IN_PROGRESS DriverRideService.updateRideStatusToInProgress() Customer on board; location streaming active
COMPLETED DriverRideService.updateRideStatusToCompleted() Ride finished; rating prompts sent
CANCELLED Timer expiry / customer / driver conditions Ride terminated

Step-by-step walkthrough

1 — Customer requests a ride

POST /api/ridesCustomerRideService.createRide()

  1. A Ride row is created with status = REQUESTED.
  2. RideMatchingService.findAndNotifyNearbyDrivers(ride) is called asynchronously via @Async("taskExecutor"). The HTTP response returns immediately with rideId and status = SEARCHING.

2 — Driver search (async)

Inside findAndNotifyNearbyDrivers(), running on the AsyncTimer-* thread pool:

  1. Sets ride.status = SEARCHING.
  2. Queries Redis Geo starting at a 3 km radius, expanding by 1 km increments up to 10 km until at least 3 eligible drivers are found.
  3. Filters candidates: driver.activeVehicle != null AND activeVehicle.serviceType == ride.serviceType.
  4. Sends a RideOfferDTO to each matched driver via /topic/driver/{driverId}/requests.
  5. Starts the 30-second non-blocking acceptance timer.

3 — Acceptance timer

Implemented with Reactor Mono.delay(30s):

  • An AtomicBoolean keyed by rideId tracks whether the timer is still active.
  • If the ride is still SEARCHING after 30 seconds → status set to CANCELLED → customer notified at /topic/customer/{customerId}/ride-status.
  • If a driver accepts within 30 seconds → cancelAsyncTimer(rideId) flips the flag and removes it from the map. The Mono pipeline checks the flag before acting.

4 — Driver accepts

POST /api/driver/accept or STOMP /app/driver/acceptDriverRideService.assignRide()

  1. ride.driver = driver, ride.status = ACCEPTED.
  2. cancelAsyncTimer(rideId) — stops the 30s timer.
  3. Customer notified: /topic/customer/{customerId}/ride-status{ status: "ASSIGNED", driverId }.
  4. All other notified drivers told the ride is gone: /topic/ride/{rideId}/expired.

5 — Driver arrives

POST /api/driver/arrived → status → ARRIVED. Customer notified at /topic/customer/{customerId}/ride-status.

6 — Ride starts

POST /api/driver/start → status → IN_PROGRESS. From this point, every call to POST /redis/driver/location checks for an active IN_PROGRESS ride and pushes the driver's coordinates to /topic/customer/{customerId}/driver-location.

7 — Ride completes

POST /api/driver/complete → status → COMPLETED.

  • Customer and driver both receive: /topic/*/ride-status{ status: "COMPLETED" }
  • Both receive a rating prompt: /topic/*/ride-rating-prompt
  • Either party can then call POST /api/rides/{rideId}/rate.

Customer cancellation

DELETE /api/rides/{rideId} is only permitted in SEARCHING or ACCEPTED status.

Current status Action
SEARCHING cancelAsyncTimer(rideId) called, status → CANCELLED
ACCEPTED Driver notified via /topic/driver/{driverId}/ride-statusCANCELLED, status → CANCELLED
Any other 409 Conflict

Fare estimation

Fare is estimated at ride creation via FareService.estimateFare(). The estimate is valid for 3 minutes and uses a Haversine distance calculation.

Service type Base Per km Per min
STANDARD / POOL $3.50 $1.50 $0.18
XL $6.00 $1.80 $0.25
PREMIUM $10.00 $2.50 $0.35

A static surge multiplier of 1.2× is applied. Dynamic surge (based on demand density near pickup) is implemented but currently disabled.