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/rides → CustomerRideService.createRide()
- A
Riderow is created withstatus = REQUESTED. RideMatchingService.findAndNotifyNearbyDrivers(ride)is called asynchronously via@Async("taskExecutor"). The HTTP response returns immediately withrideIdandstatus = SEARCHING.
2 — Driver search (async)¶
Inside findAndNotifyNearbyDrivers(), running on the AsyncTimer-* thread pool:
- Sets
ride.status = SEARCHING. - 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.
- Filters candidates:
driver.activeVehicle != nullANDactiveVehicle.serviceType == ride.serviceType. - Sends a
RideOfferDTOto each matched driver via/topic/driver/{driverId}/requests. - Starts the 30-second non-blocking acceptance timer.
3 — Acceptance timer¶
Implemented with Reactor Mono.delay(30s):
- An
AtomicBooleankeyed byrideIdtracks whether the timer is still active. - If the ride is still
SEARCHINGafter 30 seconds → status set toCANCELLED→ 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/accept → DriverRideService.assignRide()
ride.driver = driver,ride.status = ACCEPTED.cancelAsyncTimer(rideId)— stops the 30s timer.- Customer notified:
/topic/customer/{customerId}/ride-status→{ status: "ASSIGNED", driverId }. - 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-status → CANCELLED, 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.