Skip to main content

The zero-server model

When you open Off The Pace, your browser downloads data and runs queries itself. There is no compute server waiting for your requests. The entire runtime is:
This architecture means latency is dominated by the initial Parquet download, not by a round-trip to a server. Once the files are in memory, every SQL query completes in milliseconds regardless of network conditions.

Cross-origin isolation (COOP / COEP)

DuckDB-Wasm requires a SharedArrayBuffer for its multi-threaded worker. Browsers only expose SharedArrayBuffer in a cross-origin-isolated context, which requires two HTTP response headers on every page:
The Firebase hosting config sets these on every HTML response. Any asset served from a different origin (GCS bucket, CDN) must also carry Cross-Origin-Resource-Policy: cross-origin, which the bucket’s CORS policy provides. Removing or weakening either header causes the DuckDB worker to silently fall back to a single-threaded mode where queries still work but run several times slower.

The runtime manifest

The app never hard-codes data file paths. Instead, a lightweight manifest JSON file is fetched at startup:
The manifest lists every registered Parquet table with its CDN path and a build timestamp. The client resolves paths at runtime, so a re-published data build is picked up immediately on next page load without a code deploy.

Cache busting

GCS edge caches have a default TTL of one hour. Parquet files are served with an appended ?v=<build_hash> query string baked into the manifest. When a new build is published, the manifest points to new ?v= URLs; the CDN serves fresh files even if the previous version is still cached under the old URL. The manifest itself is served with Cache-Control: no-cache so it is always re-fetched.

Data shape

The Parquet files on the CDN are the gold-mart tables produced by the dbt pipeline, exported verbatim from the warehouse. The tables the app reads at runtime include: DuckDB-Wasm registers each Parquet file as a virtual table, so every SQL query in the app runs against the same schema as the warehouse no translation layer.

ONNX inference

The XGBoost models (quantile regressors, cliff classifier, stint-life regressor) are exported to ONNX and bundled alongside the app. At warmup, ONNX Runtime Web loads the model into a WebAssembly session. Subsequent calls score a feature vector and return quantile predictions, cliff probability, and stint-life estimates in a single synchronous call no fetch, no server. The Degradation Simulator’s interactive sliders update predictions in under 5 ms on a mid-range device.

Source code and deployment

The app is a React + Vite single-page application hosted on Firebase Hosting. Code changes are deployed by CI; data changes (new Parquet builds) are published separately via scripts/publish_cdn.sh and take effect on next page load without a code deploy. The two cycles are deliberately decoupled so a broken data build does not require a code rollback and a new code deploy does not require a data rebuild.