Your dashboard is a query.
Not a pipeline.
GROUP BY, HAVING and window functions run directly on the operational store — the rows your app just wrote. There is no export to a warehouse, so there is no copy to lag behind.
> POST /v1/tenants/:t/rows/orders
> { "sku": "s_412", "qty": 3, "price": 79, "country": "DE" }
committed ✓
> SELECT country, SUM(qty * price) AS revenue
FROM orders GROUP BY country
HAVING SUM(qty * price) > 10000
ORDER BY revenue DESC;
country revenue
US 128,940
DE 61,205 ← includes the row above
JP 44,310
same store · no export job · fresh at read time Every hop between the write and the chart is staleness you chose.
The conventional path to a live dashboard is a pipeline: copy the operational data somewhere analytical, then query the copy. Each stage adds a schedule, a schema, and a failure mode — and the sum of the schedules is how far behind your numbers run.
A batch job or CDC stream copies rows out of the operational database. It runs on a schedule — and everything downstream inherits that schedule.
the copy is born stale The copy lands in a warehouse with its own schema, its own types, its own failure modes. Schema drift breaks it quietly; backfills patch it loudly.
a second schema to keep true The dashboard answers questions about the state of the copy, not the state of the system. The gap between the two is the lag your users see.
lag = the age of the copy OriginChain removes the hop instead of speeding it up. The aggregate runs where the write landed — on the same hash-keyed substrate your application writes to — so the dashboard's question and the system's state are the same data.
The analytical SQL, on the operational rows.
Operational counters, live leaderboards, cohort splits, aggregate alerts — the queries behind them are three families of construct, and all three ship on the SQL surface today.
A streaming aggregate executor. Aggregates wrap any expression, not just bare columns — SUM(qty * price), AVG(score - baseline). HAVING composes with OR.
A streaming partition walk on top of the index scan — no spool, no extra storage. Live leaderboards and running totals are one clause, not a nightly job.
The planner splits conjuncts and routes each to the cheapest secondary index, so WHERE on indexed columns avoids full scans. EXPLAIN ANALYZE shows the chosen path.
Every aggregate plan: estimated vs actual, per node.
EXPLAIN ANALYZE returns the chosen
plan with estimated and actual row counts plus cost per node. When the two drift apart,
you've found the slow dashboard query in one response — without instrumenting the
dashboard itself.
Below, the planner pushed the country predicate down to a secondary index, so the aggregate reads the index range instead of scanning the table.
> EXPLAIN ANALYZE SELECT country, COUNT(*) … GROUP BY country;
group_aggregate (by country) est 5 · actual 5 · 23.1 ms
└─ index_scan orders(by_country) est 18.4k · actual 18.4k · 18.4 ms
point lookup < 5 ms · GROUP BY 10k < 50 ms No copy to lag. No sync to break.
Every shape — rows, vectors, full-text postings, graph edges — commits in one atomic write on a single substrate. For analytics that means the aggregate can never read a state where the copy hasn't caught up, because there is no copy: the write that just returned is in the next GROUP BY, by construction.
When polling isn't enough, /watch streams every write the instant it lands — a reactive feed for the counters and alerts that should push, not poll. The full picture is on the architecture page.
write path
app ── write ──▶ substrate ✓ committed
read paths — same substrate, no copy in between
dashboard ── GROUP BY / HAVING ──▶ substrate
alerts ── /watch stream ──▶ substrate
the warehouse detour this replaces:
app ─▶ db ─▶ export ─▶ transform ─▶ warehouse ─▶ dashboard Not Snowflake, not BigQuery. Multi-TB columnar scans across years of archives belong in a warehouse. Our sweet spot is operational analytics over live data — where the latency budget is milliseconds and the data is the state of your product right now.