Skip to content

perf(ch): serve lastSeen query from signal_latest projection#297

Merged
zer0stars merged 2 commits into
mainfrom
fix/lastseen-projection-scan
Jun 11, 2026
Merged

perf(ch): serve lastSeen query from signal_latest projection#297
zer0stars merged 2 commits into
mainfrom
fix/lastseen-projection-scan

Conversation

@zer0stars

Copy link
Copy Markdown
Member

Problem

signalsLatest/signalsSnapshot with lastSeen selected runs a three-branch UNION ALL. Branches 1–2 (name IN (...) GROUP BY name) are served by the signal_latest_by_subject_source_name projection. Branch 3 (lastSeen) aggregates max(timestamp) with no GROUP BY — the projection matcher in prod does not serve it, so it scans the subject's entire signal partition. Cost scales with vehicle history, not with the request:

  • p50 ~2900 ms with the lastSeen branch vs ~380 ms without (100-sample prod bench)
  • Caused the multi-day platform slowdown when a fleet consumer polled snapshots ~7,500×/hr across ~374 vehicles

Follow-up to #284, which fixed branches 1–2 but left the lastSeen branch flat.

Fix

Reshape getLastSeenQuery to aggregate at the projection grain:

SELECT 'lastSeen' AS name, max(ts) AS ts, ...
FROM (SELECT max(timestamp) AS ts FROM signal WHERE subject = ? GROUP BY name)

Inner query matches the projection's GROUP BY subject, source, name dimensions (subject pinned, re-aggregated across source); outer collapses to one row. Response shape and lastSeen semantics (max over all names) unchanged.

Testing

  • TestGetLastSeenQuery — generated SQL shape, nil-args, source-filter placement
  • TestLatestQueriesUseProjection — all three latest-query branches run under force_optimize_projection = 1 against the migrated test container (errors if any branch can't be projection-served)
  • Full internal/service/ch suite + e2e TestSignalsLatest/TestApproximateLocation green; lint clean

Deploy verification

  • EXPLAIN indexes=1 on the lastSeen branch in prod should show ReadFromMergeTree (signal_latest_by_subject_source_name) and granule counts in the single digits
  • p50 of the three-branch query should drop to parity with the two-branch form (~380 ms) in the ClickHouse query log

The lastSeen UNION ALL branch aggregated max(timestamp) with no GROUP
BY, which the prod projection matcher does not serve -- every
signalsLatest/signalsSnapshot selecting lastSeen scanned the subject's
full signal history (p50 ~2900ms vs ~380ms for the name-filtered
branches, worse for high-history vehicles).

Wrap the aggregate so the inner query groups by name at the
signal_latest_by_subject_source_name projection grain; the outer query
re-aggregates to a single row. Result shape and semantics unchanged.

Refs #284
master install.sh currently downloads the .sbom.json asset in place of
the tarball, so checksum verification fails for every version and CI
breaks on install-tools. Fetch install.sh from the release tag and pin
the version instead of tracking latest/master.
@zer0stars zer0stars force-pushed the fix/lastseen-projection-scan branch from 262cc37 to 0718635 Compare June 11, 2026 16:53
@zer0stars zer0stars merged commit 98187b1 into main Jun 11, 2026
4 checks passed
@zer0stars zer0stars deleted the fix/lastseen-projection-scan branch June 11, 2026 16:54
zer0stars added a commit that referenced this pull request Jun 11, 2026
…les (#298)

* chore: bump model-garage for signal_latest + summary table migrations

Pinned to the feat/signal-latest-table branch head (pre-v1.0.13
pseudo-version); re-pin to the v1.0.13 tag once released.

* perf(ch): serve latest-state and summary queries from precomputed tables

signalsLatest/signalsSnapshot/lastSeen/availableSignals now read the
precomputed signal_latest table (model-garage 00010) instead of
fanning out across every part of the raw signal table. Location
fields read kind=1 rows, which pre-filter (0,0) fixes, so plain
argMax replaces argMaxIf. Supersedes the projection-matching shapes
from #284/#297.

dataSummary per-name count/first-seen/last-seen now read the
AggregatingMergeTree summary tables (model-garage 00011) instead of
aggregating the raw tables. Counts are over raw inserts, matching the
approximation of the previous non-FINAL count(*) reads.

* style(ch): alias latest location column value_location for UNION consistency

Scanning is positional so agg_location worked, but the non-location
branch of the latest UNION ALL aliases its zero tuple value_location;
matching aliases keep the result shape self-describing.

* chore: pin model-garage to v1.0.13 release tag
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