Skip to content

Signals page + MQL query engine + ClickHouse refactor#201

Open
BK1031 wants to merge 3 commits into
mainfrom
bk1031/dashboard-refactor
Open

Signals page + MQL query engine + ClickHouse refactor#201
BK1031 wants to merge 3 commits into
mainfrom
bk1031/dashboard-refactor

Conversation

@BK1031

@BK1031 BK1031 commented Jun 10, 2026

Copy link
Copy Markdown
Contributor
  • Rebuild the query service against ClickHouse for telemetry reads (Postgres still owns operational tables); drop pandas/numpy/pyarrow, add clickhouse-connect
  • Introduce MQL v0.2, a method-chain query language: avg(value).where(name in ("a", "b")).by(name).every(10s) with aggregators, wildcard filters, multi-value OR, and rollups
  • Add /signals dashboard page: Datadog-style query builder (chips for aggregator/field/filters/group/rollup), bar/line/area chart-type toggle, click-and-drag brush selection, render-safety guard, collected-signals table with fuzzy search and sortable columns
  • Reorganize sidebar: Debug moves above Settings, new Signals entry, remove Query and Trips
  • Fix vehicle upsert: catch Postgres duplicate-key (SQLSTATE 23505) via ON CONFLICT clause instead of MySQL's "Duplicate entry" string match
  • Wire-format timestamps now always UTC-tagged (...Z) so the browser doesn't parse them as local time

BK1031 added 3 commits June 10, 2026 10:03
CreateVehicle previously caught duplicate-key errors with strings.Contains
of "Duplicate entry" — MySQL's wording. The repo runs Postgres, which
reports "duplicate key value violates unique constraint" (SQLSTATE 23505),
so the upsert fallback never fired and every edit surfaced the constraint
violation to the user.

Replaced the catch-and-retry with clause.OnConflict on the primary key —
same pattern already used in vehicle/service/config.go.
Telemetry now lives in ClickHouse (DateTime64-keyed signal table); the
query service still owns the Postgres-backed signal_definition / token
tables but reads bulk data through clickhouse-connect.

Backend layout:
- database/clickhouse.py initializes the HTTP client alongside Postgres
- service/signals.py wraps the signal table with distinct-name listing
  and bucketed counts using toStartOfInterval + WITH FILL for zero-filled
  axes
- service/query_lang.py + service/query_exec.py implement MQL v0.2 — a
  method-chain query language:
    avg(value).where(name in ("a", "b")).by(name).every(10s)
  Aggregators: count/sum/avg/min/max/last/p50/p95/p99/stddev. Filters
  collapse same-column equalities to OR; wildcard `*` in values becomes
  LIKE. Rollup intervals from 1s to 1d.
- routes/signals.py: GET /query/signals + /query/signals/counts
- routes/query_run.py: POST /query/run for the new MQL endpoint
- service/auth_guard.py: shared bearer-token dependency

Removed: old query/log/trip/vehicle/model files that were tied to the
prior Postgres-only design. Dropped pandas/numpy/pyarrow from deps in
favor of ClickHouse-side aggregation; added clickhouse-connect.

Wire-format timestamps are always UTC-tagged ISO 8601 (`...Z` suffix) so
the browser doesn't parse them as local time.
New Signals page (/signals) is the chart-driven analog of Datadog's
metrics explorer:

- TimeframePicker — pill displays the absolute range with the preset
  label as muted annotation, click opens an editable input that accepts
  shortcuts (`1h`, `30m`, `2d`) or absolute ranges in the form
  `YYYY-MM-DD HH:MM - YYYY-MM-DD HH:MM` (local time).
- QueryBuilder — Datadog-style chips for aggregator / field / filters /
  group-by / rollup. Filter values support `*` wildcards (LIKE under the
  hood) and same-column chips show an OR separator since the semantics
  union. Serialized live preview emits MQL v0.2 method-chain syntax.
- QueryChart — recharts-backed bar/line/area with chart-type toggle,
  top-K + "other" rollup so 400-series queries don't blow up the legend,
  click-and-drag brush selection to set custom timeframes, and a
  safety-rail confirmation panel when bucketCount × seriesCount exceeds
  20k (SVG render budget on most browsers).
- Collected-signals table (deliberately unbounded by the timeframe) with
  fuzzy search via fuse.js, sortable columns, hover tooltips for absolute
  timestamps.

Sidebar reorganized: Debug moved into the bottom group above Settings,
Signals added under Dashboard, Query and Trips entries removed.

Removed: pages/query, pages/trips, components/query, components/trips,
models/trip, dead useTrip store slot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant