Repository navigation
Conversation
…y's routing engine Order distance/time came from the straight-line 'calculate' provider because DISTANCE_MATRIX_PROVIDER defaults to it and the routing setting is only read by the console map. Resolve the provider from an explicit DISTANCE_MATRIX_PROVIDER, then the company's routing engine when the server can call it (private OSRM host, Google key), then 'calculate'. Fall back to the straight-line estimate with a warning when a road provider fails instead of storing 0, cache road results for 24h and stop caching empty ones. Co-Authored-By: Claude <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## release/v0.6.73 #406 +/- ##
===================================================
Coverage 100.00% 100.00%
- Complexity 13585 13613 +28
===================================================
Files 634 635 +1
Lines 49308 49373 +65
===================================================
+ Hits 49308 49373 +65
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
roncodes
marked this pull request as draft
October 15, 2026 07:58
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Fleetbase Cloud customer in Australia reported a ~30 km run (Sunshine → Melton, VIC) showing 24,980 m / 1,800 s. Those are the numbers the
calculateprovider produces: straight-line metres, and a time ofround(m / 100) × 7.2 s, a flat ~50 km/h. The customer asked when road distance is coming.Root cause
server/config/fleetops.phpdefaultsdistance_matrix.providertoenv('DISTANCE_MATRIX_PROVIDER', 'calculate'), and Cloud does not set the variable. So every order's distance and time come fromUtils::calculateDrivingDistanceAndTime(Vincenty great-circle), not a road router.display_engine: OSRM by default, or Google, or an extension engine such as Valhalla) is only read by the console (addon/services/route-engine.js) to draw the route on the map. No server code reads it. As a result the map shows a road route while the order stores a straight-line distance.routes: []) or a Google error orZERO_RESULTSelement was stored on the order as 0 m / 0 s.fleetops:update-estimationsalways passedconfig('fleetops.distance_matrix.provider')explicitly, so it would have ignored any per-company choice anyway.#383 (no longer measuring from a driver's 0,0 placeholder) is correct and unchanged. This PR is about which provider measures the distance.
Design
One small resolver,
Support/Routing/DistanceMatrixProviderResolver.Utils::getDrivingDistanceAndTimeuses it when the caller does not passprovider:DISTANCE_MATRIX_PROVIDERwhen it is set. Self-hosters who chose a provider keep exactly that provider for every company.calculateacts as a platform kill switch. The config default changes from'calculate'tonullso "unset" can be detected.Utils::distanceMatrix(multi-stop service quotes) still falls back tocalculate, so it behaves exactly as before.display_engine→routing_display_engine→router→osrm, the same chainSettingController::getRoutingSettingsand the console use). It is used only if the server can serve it responsibly:osrmneedsfleetops.osrm.hostto point somewhere other than the public demo serverrouter.project-osrm.org. Server-side order estimates are never sent there implicitly; an operator who setsDISTANCE_MATRIX_PROVIDER=osrmcan still opt in.googleneeds a Maps API key (services.google_maps.api_key, thenGOOGLE_MAPS_API_KEY).calculate.The company comes from
$options['company_uuid'], falling back to the session company.Order::setDistanceAndTimepasses$this->company_uuid, so queued listeners (HandleOrderCreated,HandleOrderReady) and the cron, which have no session, resolve the right company.fleetops:update-estimationsnow passes a provider only when--provideris given; otherwise each order resolves its own company's provider.Fallback: a road provider that throws, or returns no route (distance or time ≤ 0), now gives the straight-line estimate plus a
Log::warning('Road distance unavailable, using the straight-line estimate instead.', {provider, origin, destination, error}). An order never stores 0 for two distinct points. Identical points return 0 without calling a provider.Caching: road results are cached in Redis for 24 h (
Utils::DISTANCE_MATRIX_CACHE_TTL). Before this PR they were cached with no expiry, and empty results were cached too. Empty results are no longer cached, so a failed lookup is retried next time. The Google key is now read through config beforeenv(), matchingGoogleRoutesTrackingProvider.Side effect:
ServiceRate::pointQuoteandGET /v1/orders/{id}/distance-and-timealso callgetDrivingDistanceAndTime, so they follow the session company's provider too. That keeps them consistent with the stored order distance. Before, that endpoint could overwrite a stored road distance with a straight-line one. Multi-stop quotes (Utils::distanceMatrix) are unchanged.Fleetbase Cloud configuration needed
For road distance on Cloud after this PR:
DISTANCE_MATRIX_PROVIDERmust stay unset. If it is set, it overrides every company's routing engine.OSRM_HOSTto a self-hosted OSRM instance whose extract covers the customers' regions (Australia here). With the demo host (the docker-compose default) these companies stay oncalculate.GOOGLE_MAPS_API_KEYmust be set, and the key's project must have the Distance Matrix API enabled. Google has classed that API as Legacy since March 2025, and new projects can no longer enable it, so check the Cloud project.OSRM_HOSTis private.Would configuration alone, without this PR, fix the customer? Mostly yes.
DISTANCE_MATRIX_PROVIDER=osrmwith a privateOSRM_HOST, or=googlewith an enabled key, would make new orders measure road distance on the current code. But it applies one provider to every tenant and ignores their routing setting. Any timeout, quota error or missing route would store 0 m / 0 s on the order. Results would also be cached in Redis forever, including empties. This PR makes the per-company path safe and the failure mode benign.Cost and rate limits
HandleOrderCreated), one when ready (HandleOrderReady), one per edit, plus one every 10 minutes while it is started and unfinished (fleetops:update-estimations). In that last case the origin is the driver's live position, so the cache rarely hits. A one-hour delivery is roughly 2 + 6 = 8 lookups.DISTANCE_MATRIX_PROVIDER=google.router.project-osrm.org): its usage policy is at most 1 request per second with no heavy use. A whole Cloud fleet calling from one IP would get throttled or blocked, which is why the company path refuses it. A self-hosted OSRM has no per-call cost and no rate limit beyond its own capacity.Existing orders
No migration.
fleetops:update-estimationsruns every 10 minutes and will recompute started, unfinished orders created in the last 2 days with the new provider. It does not touch orders that are created but not started, or completed or cancelled ones, and its origin is the driver's current position (remaining distance, not total). Unstarted orders pick up road distance at their nextsetDistanceAndTimetrigger (ready, edit, payload change). Completed orders keep their straight-line values unless they are backfilled separately.Tests
server/tests/Unit/Support/DistanceMatrixProviderResolverTest.phpcovers: explicit env wins andcalculateas a kill switch; company OSRM, Google and legacyrouterkeys; unsaved settings default to OSRM; the Google key requirement; Valhalla falling through; the public demo host refused in four spellings (and allowed when set explicitly); an unreadable settings store; the Sunshine → Melton OSRM call with its URL and the 24 h cache TTL; the session-company fallback; OSRMNoRoute, a Google timeout and a GoogleZERO_RESULTSelement each falling back to the estimate with a warning and no cache write; same-point andcalculatelookups making no HTTP call.OrderPayloadConfigAndDistanceTest:Order::setDistanceAndTime()with no session company stores the OSRM road distance, then keeps the straight-line estimate (not 0) when OSRM finds no route.CommandContractsTest: the command without--providercallssetDistanceAndTime([])and logsUsing provider: company routing settings.