[HDX-5077] Move multi-series metric merge computation to ClickHouse - #2859
[HDX-5077] Move multi-series metric merge computation to ClickHouse#2859wrn14897 wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: 9aa6819 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Greptile SummaryThe PR moves multi-series metric merging and ratio computation from application code into one composed ClickHouse query.
Confidence Score: 4/5The PR is not yet safe to merge because comma-bearing ClickHouse setting values still produce malformed composed SQL. The final SETTINGS clause is still reconstructed with an unstructured comma split even though setting values are arbitrary quoted strings, so valid comma-bearing values can break every multi-series metric query using that setting. Files Needing Attention: packages/common-utils/src/core/renderChartConfig.ts
|
| Filename | Overview |
|---|---|
| packages/common-utils/src/core/renderChartConfig.ts | Adds the composed multi-series SQL renderer; the previously reported SETTINGS parser defect remains present in the hoisting path. |
| packages/common-utils/src/clickhouse/index.ts | Simplifies chart execution to render and issue one query instead of splitting and merging result sets client-side. |
| packages/common-utils/src/core/builderToRawSql.ts | Aligns builder-to-raw-SQL conversion with the composed multi-series rendering path. |
| packages/common-utils/src/tests/renderChartConfig.test.ts | Adds broad SQL-shape coverage for pivoting, ratios, mixed metric classes, aliases, and SETTINGS hoisting. |
| packages/common-utils/src/tests/queryChartConfig.int.test.ts | Preserves integration-level result-shape and multi-series parity coverage against the new single-query implementation. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Config[Multi-series metric chart config] --> Branches[Render one SQL branch per series]
Branches --> Strip[Extract trailing SETTINGS]
Strip --> Union[Combine branches with UNION ALL]
Union --> Pivot[Group and pivot by series index]
Pivot --> Ratio[Compute ratio when configured]
Ratio --> Hoist[Append deduplicated SETTINGS]
Hoist --> CH[(ClickHouse)]
CH --> Result[Single merged chart result]
Reviews (5): Last reviewed commit: "fix: preserve ClickHouse-derived group c..." | Re-trigger Greptile
Deep Review✅ No critical issues found. No P0/P1 defects: the composed-query rewrite preserves the observable multi-series contract, dead node-side code is fully removed with no dangling importers, chSql parameter names are content-hashed so merging independently-rendered branch params is collision-safe, and the external API v2 🟡 P2 — recommended
🔵 P3 nitpicks (3)
Reviewers (8): correctness, adversarial, security, testing, maintainability, performance, api-contract, project-standards. Testing gaps:
Note: the correctness, adversarial, and kieran-typescript sub-agents did not return before synthesis; their focus areas (UNION column alignment, ratio math, alias-collision suffixing, recursion termination, type narrowing) were independently verified during merge and no additional defects surfaced. |
e8b8ac0 to
227416f
Compare
778c987 to
bd1656e
Compare
bd1656e to
75c8b8d
Compare
🟡 Tier 3 — StandardIntroduces new logic, modifies core functionality, or touches areas with non-trivial risk. Why this tier:
Review process: Full human review — logic, architecture, edge cases. Stats
|
E2E Test Results✅ All tests passed • 280 passed • 1 skipped • 1110s
Tests ran across 4 shards in parallel. |
…077)
A metric chart with N select items used to fan out into N ClickHouse
queries whose result sets were merged node/browser-side (mergeResultSets,
computeResultSetRatio). renderChartConfig now composes the same N
per-series branch queries into ONE statement: each branch is rendered
with internal aliases (__hdx_value, __hdx_group_{j}), tagged with its
series index, combined via UNION ALL, and pivoted back into one row per
(group values, time bucket) with anyOrNullIf so missing rows stay NULL
(a gap) instead of 0. Ratio charts (seriesReturnType 'ratio') and both
ratioMode variants now divide in SQL (coalesce/nullif; share_of_total
via a window sum over the bucket), resolving the long-standing TODOs to
generate union CTEs and compute the ratio on the db side.
- queryChartConfig renders once and queries once for every config;
mergeResultSets, computeResultSetRatio, computeRatio and their helpers
are deleted along with their unit suites (behavior is pinned by the
HDX-5076 queryChartConfig integration baseline, which passes unchanged)
- gauge/sum branches project group-by dimensions as individual columns
while histogram branches keep their Array "group" column; each class
pads the other's columns (NULL / []) so mixed-type charts stay
type-compatible without joining rows across classes (parity)
- same-alias series keep the __{splitIndex} suffix disambiguation, and
the suffix is still stripped from the ratio column label
- per-branch SETTINGS clauses are stripped and hoisted (deduped) to the
composed query
- sum 'increase' TopGroups ranking now strips group-by aliases (latent
bug: an aliased groupBy rendered AS "alias" inside tuple(...), a
syntax error; also required by the injected internal aliases)
- "View SQL" for multi-series metric charts now shows the full composed
query instead of only the first series
75c8b8d to
a035820
Compare
…c query The k8s dashboard e2e caught a regression in the composed multi-series query: group-by columns were re-aliased to their expression text (ResourceAttributes['k8s.namespace.name']), but consumers — the Kubernetes dashboard's row lookups and external-API clients — read them by the name a single-series query produces, which for un-aliased expressions is ClickHouse's DERIVED name (arrayElement(ResourceAttributes, 'k8s.namespace.name')). The rename blanked the namespace/pod cells. Derived names can't be reproduced node-side, so stop renaming entirely: - branches render their group-by untouched (natural names preserved) - gauge/sum wrappers use SELECT * and are ordered first in the UNION (UNION ALL matches columns by position and takes names from its first branch); histogram wrappers are explicit with NULL pads whose names are never referenced - the outer pivot passes group/bucket columns through with * EXCEPT (__hdx_value, __hdx_series_idx) and groups via GROUP BY ALL (ClickHouse >= 22.12) instead of naming them Adds two int regression tests pinning the contract: un-aliased expression group-bys keep the arrayElement(...) derived name, and user-aliased ones keep the alias.
E2E failure root cause + fix (9aa6819)The Shard 3 kubernetes e2e failures were a real regression: the composed query re-aliased group-by columns to their expression text ( Derived names can't be reproduced node-side, so the fix stops renaming group columns entirely:
Added two int regression tests pinning the contract (un-aliased expression group-bys keep the Local verification: 28/28 queryChartConfig int tests, 1756 unit tests, lint + ratchet clean. Letting CI confirm the k8s e2e. |
…#2871) ## Why The PR triage classifier has no critical-path coverage for `packages/common-utils` — the SQL rendering + execution engine that every chart, search, and alert query flows through. A substantial change there can silently alter query semantics product-wide, yet #2859 (664 prod lines moving multi-series metric merge computation into ClickHouse) tiered on generic size/cross-layer rules as **Tier 3**. ## What Adds a fourth critical band, `QUERY_ENGINE_CRITICAL_PATTERNS`: - `packages/common-utils/src/core/renderChartConfig.*` - `packages/common-utils/src/core/builderToRawSql.*` - `packages/common-utils/src/clickhouse/` using the same total-churn escalation mechanism as the infra band, but with a **150-line bar** (`QUERY_ENGINE_CRITICAL_MIN_LINES`). ### Why 150 and not infra's 30 Calibrated against 20 recent merged PRs touching these files (prod-only churn): | Bar | PRs that would flip to Tier 4 | |---|---| | ≥30 (infra bar) | 10/20 — including routine Tier 2 chart fixes (#2759, #2613, #2422, #2487) | | ≥150 | Only engine-level rewrites: #2246 (Increase aggFn), #2634 (builder→raw SQL), #2859 | These files are among the hottest in the repo — routine chart fixes graze them weekly — so the bar is set high enough that only changes warranting a domain expert escalate. ### Also - Fixed a stale test fixture path (`src/renderChartConfig.ts` → `src/core/renderChartConfig.ts`; the file moved after the test was written) - Updated Tier 4 description text and comment triggers/context lines ## Verification - `node --test .github/scripts/__tests__/pr-triage-classify.test.js` — 112 tests pass (10 new) - End-to-end replay of #2859's actual file list now yields Tier 4: > **Query rendering engine substantially modified** — 664 lines (bar: 150). Every chart, search, and alert query flows through this code No changeset: CI/internal tooling only.
Why
HDX-5077 — prerequisite for metric formulas (HDX-4938), which need all metric series as columns of one relation so a formula can render as a final SELECT projection.
A metric chart with N series used to fan out into N ClickHouse queries whose result sets were merged node/browser-side in
mergeResultSets/computeResultSetRatio. This resolves the long-standing TODOs ("generate union CTEs on the db side", "compute the ratio on the db side") by composing the per-series queries into one statement.Stacked on #2858 (HDX-5076 regression baseline) — that suite was written against the old node-side merge and passes unchanged against this implementation, which is the parity gate.
How
New
renderMultiSeriesMetricChartConfiginrenderChartConfig.ts: each per-series branch is rendered exactly as the single-series path would (own CTE scaffolding, own physical table per metric type), but with internal aliases (__hdx_value,__hdx_group_{j}); branches are tagged with their series index, combined viaUNION ALL, and pivoted back into one row per (group values, time bucket):anyOrNullIfkeeps missing rows NULL → rendered gap, never 0seriesReturnType: 'ratio') divides in SQL:coalesce(v0, 0) / nullif(v1, 0);ratioMode: 'share_of_total'divides bysum(v1) OVER (PARTITION BY __hdx_time_bucket)__{splitIndex}suffix; the suffix is stripped from the ratio label (parity)groupcolumn; each class pads the other's columns (NULL/[]) so mixed charts stay type-compatible without joining rows across classes (parity with the old merge, which never joined them)SETTINGSare stripped (extractSettingsClauseFromEnd) and hoisted deduped — ClickHouse rejects SETTINGS on non-final UNION branchesqueryChartConfigrenders once / queries once for every config;mergeResultSets,computeResultSetRatio,computeRatio+ helpers deleted along with their unit suites (semantics now pinned by the int baseline)increaseTopGroups ranking now strips group-by aliases (an aliased groupBy renderedAS "alias"insidetuple(...)— a syntax error; also required by the injected internal aliases), mirroring the strip inrenderSeriesLimitCteSide effects
Nullable(Float64)) — consumers resolve types viaconvertCHDataTypeToJSType, which already handles Nullable (asserted in the baseline suite)Attributes['host']vsarrayElement(Attributes, 'host')) — legends use group values, so this only affects table headersTest plan
make ci-lint(incl. ratchet) andmake ci-unitgreen repo-wide (5,550 tests)queryChartConfig(26/26) +sampleWeightedAggregations, apicheckAlerts(268/268, 90 snapshots unchanged), apirenderChartConfigmetric suite (99/99, 28 snapshots unchanged), MCPqueryTool(65/65)metadata.int.test.tsfailures reproduce identically on unmodifiedmain(environment-dependent, unrelated)Issues
#2680
Out of scope (follow-ups)
Formula schema/UI (HDX-5078/5079),
builderToRawSqlmulti-series support, external API v2 cross-source/charts/seriesfan-out, enabling chunking for metric charts.