Convoy — Fleet Re-Optimization on Exasol Personal
A fleet re-optimization demo built entirely on the local, no-cloud Exasol Personal Local Starter Kit. Closing a road triggers a real in-database re-optimization UDF; a trained demand model predicts ride demand per zone and drives proactive rebalancing; live vehicle telematics come from MongoDB through an Exasol Virtual Schema.
Local-First Database
Runs entirely on Exasol Personal — no cloud account, no BucketFS, nothing to provision.
Live MongoDB Federation
Vehicle position lives in MongoDB and is federated into Exasol through a Virtual Schema, not copied or synced.
In-Database Re-Optimization
Closing a road calls a real Python UDF inside Exasol — a genuine recompute, not a canned response.
ML Demand Forecasting
An XGBoost model trained on real NYC TLC trip data predicts rides/hour per zone.
Live Map & Routing
Leaflet frontend with actual OSRM road-following routes for every reassigned vehicle.
Schema-Scaffolded Dashboard
A dash-server analytics app introspected from the live schema, then hand-finished with real KPIs and insights.
Two databases, each doing the job it's good at.
MongoDB owns live vehicle position and takes the write churn of telematics updates every few seconds. Exasol Personal owns the analytical and operational side \u2014 zones, assignments, run history, and the re-optimization UDF itself \u2014 reading vehicle position from Mongo through a Virtual Schema rather than duplicating it.
Predict demand. Close a road. Watch it reroute.
Trade-offs made on purpose, not by default.
Hungarian algorithm over OR-Tools — a constraint, not a preference
The stock Python 3.12 Script Language Container that ships with Exasol Personal doesn't include OR-Tools, and Personal has no BucketFS to upload a custom one. scipy.optimize.linear_sum_assignment does the same assignment job within that real constraint.
MongoDB as the source of truth for position, not Exasol
Vehicle telematics update every few seconds. Rather than writing that churn into Exasol directly, MongoDB owns live position and Exasol reads it through a Virtual Schema — the analytical database stays analytical.
Recompute time is only ever persisted once
Each /disrupt call writes its recompute_seconds to CONVOY.RUNS. That's the only place that number is stored — outside the API response itself, there's no second copy anywhere in the system.
Dashboard generated, then hand-finished, not fully automated
The analytics tab started from app_scaffold_from_schema introspecting CONVOY.ASSIGNMENTS, then was hand-written from there — the scaffold set the shape, it didn't ship as the final product.
Want to see it run against a live disruption?
Happy to walk through the re-optimization UDF, the Mongo federation, or the demand model in detail.