A dispatch system has two jobs that pull against each other: absorb a constant stream of driver location updates, and answer a matching query quickly enough that a rider does not give up waiting. The client's platform did both against the same tables, so every busy period made matching slower exactly when it mattered most. ELIVTECH separated the write path from the read path and rebuilt matching, fare calculation and trip lifecycle around that split.
The client operates a ride-hailing service across several cities with a driver fleet of independent contractors. Its original platform wrote every driver location ping directly into the transactional database and ran matching as a distance calculation across that same table, which meant peak hours degraded both. Fares were computed at the end of a trip from a route the system had not verified, producing disputes it had no evidence to resolve. Driver earnings were reconciled by hand each cycle. ELIVTECH rebuilt the platform with a dedicated ingest path for location telemetry, geospatial matching served from Elasticsearch, trip lifecycle modelled as explicit state transitions on Laravel, and settlement derived from the trip record rather than recalculated afterwards.
At a glance
The challenge
Location writes and matching reads competing for the same database at exactly the same moments.
Telemetry writing into the transactional store
Every driver sent a position update on a short interval, and each one became a write against tables that matching also had to read. During peak hours the write volume and the read volume were both at their highest, so lock contention slowed the queries the business most depended on. Adding database capacity moved the ceiling without changing the shape of the problem.
Matching by distance calculation
Finding a nearby driver meant computing distance across candidate rows in application code. The work grew with fleet size rather than with the number of drivers actually near the rider, so matching became slower precisely as the service became busier and the rider waited longer for a worse answer.
Fares computed from an unverified route
The final fare was calculated at trip end from data the platform had not independently recorded. When a rider disputed a charge or a driver disputed an earning, there was no authoritative trip trace to consult, so support resolved cases by judgement and both parties learned not to trust the number.
Trip status inferred, not recorded
Trip progress was derived from whichever timestamp columns had been populated. Edge cases such as a driver cancelling after arrival, a rider not appearing, or a trip abandoned mid-route left records in states nothing in the code had anticipated, and those trips required manual correction before they could be settled.
Driver settlement reconciled by hand
Earnings were assembled each cycle by exporting trips, applying incentive rules in spreadsheets and adjusting for disputes. The process was slow, hard to audit and impossible for a driver to verify independently, which made every payment query an investigation rather than a lookup.
No visibility into supply and demand
Operations could not see where demand was building relative to available drivers until complaints arrived. Decisions about incentives and coverage were made retrospectively from reports rather than from a live view, so the response always trailed the situation it was addressing.
Our solution
Separate the write path from the read path, then make every trip a recorded sequence rather than an inference.
Load Analysis and Dispatch Design
Five weeks profiling real traffic to separate what is genuinely transactional from what is telemetry. Location updates are high-volume, individually disposable and only ever read as the latest value, which makes them a poor fit for the transactional store. That distinction drove the architecture: a dedicated ingest path for position data, and a matching index that answers proximity questions without touching trip tables.
Telemetry Ingest and Geospatial Matching
A Node.js ingest service accepts driver position updates over persistent connections and writes current state into Elasticsearch geo point fields, keeping only the latest position per driver alongside availability and vehicle class. Matching became a bounded geo query filtered by eligibility, so the work scales with drivers near the rider rather than with the size of the fleet.
Trip Lifecycle and Verified Fares
Trips were modelled on Laravel as an explicit state machine with named states and permitted transitions, so cancellation after arrival and rider no-show became defined outcomes rather than unhandled cases. The route is recorded as a sampled trace during the trip, and the fare is computed from that trace, giving both parties the same evidence when a charge is questioned.
Settlement and Live Operations View
Earnings are derived from completed trip records with incentive rules applied as configuration, so a driver can see how a figure was reached without contacting support. Operations gained a live view of demand against available supply per zone, built from the same telemetry stream, with the whole platform running on autoscaling AWS infrastructure defined in code.
Technology stack
Results
Measured across peak periods once all cities were serving from the new dispatch path.
Before and after: platform engineering measures
- Driver position updates written into transactional tables
- Matching computed distance across candidate rows in code
- Matching slowed as the fleet and the peak grew together
- Fares calculated at trip end from an unverified route
- Trip progress inferred from populated timestamp columns
- Driver earnings assembled in spreadsheets each cycle
- Supply and demand assessed retrospectively from reports
- Telemetry ingested on a dedicated path, current state only
- Matching served by bounded geo queries with eligibility filters
- Matching cost scales with nearby drivers, not fleet size
- Fares computed from a sampled route trace recorded during the trip
- Trip lifecycle recorded as explicit, permitted transitions
- Settlement derived from trip records with rules as configuration
- Live supply and demand view per zone from the telemetry stream
Project timeline
Load Analysis and Dispatch Design
Traffic profiling to separate telemetry from transactional load, trip state machine definition, fare and incentive rule documentation, and the target architecture for ingest, matching and settlement.
Telemetry Ingest and Matching Index
Node.js ingest service on persistent connections, Elasticsearch geo point index holding latest driver state, eligibility filtering by vehicle class and availability, and bounded geo query tuning against real fleet distributions.
Trip Lifecycle and Fare Engine
Laravel state machine with transition guards, append-only trip event log, sampled route trace capture, fare calculation from the recorded trace, and surge and waiting rules expressed as configuration rather than code.
Settlement and Operations Tooling
Earnings derivation from completed trips, incentive rule engine, driver-facing earnings breakdown, dispute handling against the trip trace, and the live supply and demand view for operations built on the telemetry stream.
Load Testing and City Rollout
Load testing shaped to real peak curves, fault injection on the ingest path, monitoring on match latency and index lag, then a city-by-city rollout with the previous dispatch retained as fallback until each was stable.
Key takeaways
What shaped the engineering decisions
- Telemetry is not transactional data: Scaling the database was rejected because it treats a modelling problem as a capacity problem. Location pings are disposable and only the latest matters, so routing them to a separate store removed the contention rather than raising the ceiling on it.
- Let the index do the geometry: Distance calculation in application code scales with fleet size. A bounded geo query with eligibility filters scales with the drivers actually near the rider, which is the only population the answer depends on.
- Record the route, then price it: Computing a fare from data the platform never captured left disputes unresolvable. A sampled trace recorded during the trip gives rider, driver and support the same evidence and turns a disagreement into a shared reference.
- Model the awkward outcomes explicitly: Cancellation after arrival and rider no-show were the cases that produced unsettleable trips. Naming them as states with defined transitions removed the manual correction work they used to generate every cycle.
- Derive settlement, do not reassemble it: Spreadsheet reconciliation was slow and unverifiable. Deriving earnings from trip records with incentive rules held as configuration made a driver's earnings explainable without an investigation.
- Test the peak shape, not the average: Uniform load testing would have missed the interaction between ingest volume and match latency. Replaying real peak curves surfaced the contention that mattered while it was still cheap to fix.
Want results like these?
Let's discuss how ELIVTECH can drive measurable outcomes for your business.
Start your project