Skip to content

[HDX-5202] Fix multi-series metric ORDER BY on expression group-bys - #3013

Open
wrn14897 wants to merge 1 commit into
mainfrom
warren/HDX-5202-multiseries-metric-orderby
Open

[HDX-5202] Fix multi-series metric ORDER BY on expression group-bys#3013
wrn14897 wants to merge 1 commit into
mainfrom
warren/HDX-5202-multiseries-metric-orderby

Conversation

@wrn14897

Copy link
Copy Markdown
Member

Summary

Multi-series metric tiles grouped by an expression (e.g. ResourceAttributes['service.name'], concat(ResourceAttributes['host.name'], if(...))) failed to render with:

Unknown expression or function identifier `ResourceAttributes` in scope SELECT ... GROUP BY ALL ORDER BY ...

Root cause: a multi-series metric chart renders as N per-series subqueries composed via UNION ALL + pivot. Group-by columns pass through the outer statement via SELECT * EXCEPT (...) + GROUP BY ALL, deliberately un-renamed (consumers such as the Kubernetes dashboard look rows up by ClickHouse's derived names like arrayElement(ResourceAttributes, 'service.name'), which can't be reproduced node-side). convertToTableChartConfig defaults a table's orderBy to the raw groupBy text, so the outer ORDER BY re-referenced expressions over source columns that no longer exist in that scope.

Fix (renderMultiSeriesMetricChartConfig): ORDER BY items that repeat a group-by expression verbatim now sort through internal __hdx_sort_<n> companion columns:

  • each scalar (gauge/sum) branch projects the expression once more under the companion alias, appended to its group-by list (the expression is already a grouping key, so semantics are unchanged); histogram branches NULL-pad the slots
  • the companions are excluded from the outer projection via * EXCEPT, so output columns and meta are byte-for-byte unchanged
  • the outer ORDER BY references them as any(_hdx_sort) (valid post-GROUP BY ALL; deterministic because the companion duplicates a grouping key)
  • a matched group-by entry with a user alias sorts through the quoted alias instead; plain column references and everything else render exactly as before (queries that worked before produce identical SQL)

How to test on Vercel preview

N/A — non-UI change (query generation in common-utils).

Testing

  • Unit tests + snapshot mirroring the broken tile (map access + concat/if group-by, table default orderBy = groupBy text), structured sort items, alias rewrite, histogram padding, plain-column passthrough
  • Integration tests against real ClickHouse: raw-expression sorts (string + structured), derived column names preserved in meta, mixed gauge+histogram branches
  • Rendered the exact broken tile config ("Top Pods by Event Loop Pressure", dashboard 69176214128f1e5fc4c968ad) with the fix and executed the SQL against the private instance: 175 rows returned, ordered by service/pod, output column names unchanged
  • make ci-lint, make ci-unit, yarn ci:int (common-utils) all pass

References

…es metric queries (HDX-5202)

A multi-series metric table grouped by an expression (e.g.
ResourceAttributes['service.name']) failed with "Unknown expression or
function identifier ResourceAttributes": convertToTableChartConfig
defaults orderBy to the raw groupBy text, but the composed outer query
can't evaluate those expressions — the source columns don't exist there
and the passthrough columns carry ClickHouse-derived names that can't be
reproduced node-side.

Matched sort items now render through internal __hdx_sort_<n> companion
columns projected by each scalar branch (NULL-padded in histogram
branches), excluded from the output via * EXCEPT, and referenced from
the outer ORDER BY as an aggregate. Group-by entries with a user alias
sort through the alias; plain column references are left untouched.
Output columns and meta are unchanged.
@wrn14897 wrn14897 added the ai-generated AI-generated content; review carefully before merging. label Aug 27, 2026
@changeset-bot

changeset-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 713af49

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/common-utils Patch
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

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

@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 27, 2026 6:52pm
hyperdx-storybook Ready Ready Preview Aug 27, 2026 6:52pm

Request Review

@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes multi-series metric ordering when a raw expression group-by is no longer resolvable in the composed query’s outer scope.

  • Adds internal companion sort columns for scalar branches and positional NULL padding for histogram branches.
  • Rewrites matching ORDER BY items to companion aggregates or explicit group aliases.
  • Adds unit, snapshot, and ClickHouse integration coverage for expression ordering and mixed metric branches.

Confidence Score: 4/5

The share-of-total expression-group-by path still fails and should be fixed before merging.

The new companion rewrite repairs ordinary multi-series queries, but its window-projection guard sends share-of-total ratios back through the raw outer ORDER BY that cannot resolve source-dependent expressions.

Files Needing Attention: packages/common-utils/src/core/renderChartConfig.ts

Important Files Changed

Filename Overview
packages/common-utils/src/core/renderChartConfig.ts Adds expression-aware companion sort columns, but excludes share-of-total ratios and leaves their raw expression ordering unresolved.
packages/common-utils/src/tests/renderChartConfig.test.ts Adds broad SQL-rendering coverage for expression sorts, aliases, histogram padding, and plain-column passthrough, but omits the share-of-total combination.
packages/common-utils/src/tests/queryChartConfig.int.test.ts Exercises expression sorting against ClickHouse, including structured sorts and mixed branches, without covering share-of-total expression ordering.
packages/common-utils/src/tests/snapshots/renderChartConfig.test.ts.snap Records the expected companion-column SQL for the newly covered table configuration.
.changeset/multiseries-metric-orderby.md Documents the user-visible query-generation fix and affected packages.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Multi-series chart config] --> B[Render per-series branches]
  B --> C[Project expression companions in scalar branches]
  B --> D[NULL-pad companion slots in histogram branches]
  C --> E[UNION ALL]
  D --> E
  E --> F[Pivot values and GROUP BY ALL]
  F --> G[ORDER BY companion aggregate]
  G --> H[ClickHouse result]
Loading

Fix all with Greploop Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "fix(common-utils): resolve expression gr..." | Re-trigger Greptile

Comment on lines +2676 to +2677
const rewrittenSort =
chartConfig.orderBy != null && hasScalarGroups && !usesWindowProjection

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Share ratio sorting breaks

When a share_of_total multi-series table orders by an expression group-by, this guard disables the companion-column rewrite and leaves the source-dependent expression in the outer ORDER BY, where columns such as ResourceAttributes are unavailable, causing ClickHouse to reject the query and the tile to fail.

Knowledge Base Used:

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 321 passed • 1 skipped • 1346s

Status Count
✅ Passed 321
❌ Failed 0
⚠️ Flaky 2
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@wrn14897
wrn14897 marked this pull request as ready for review August 27, 2026 19:07
@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Aug 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches authentication, tenancy data models, the public API or shipped database config — or substantially changes the query rendering engine, background tasks, the OTel pipeline, image build, or release CI.

Why this tier:

  • Query rendering engine substantially modified — 238 lines (bar: 150). Every chart, search, and alert query flows through this code:
    • packages/common-utils/src/core/renderChartConfig.ts

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 1
  • Production lines changed: 238 (+ 376 in test files, excluded from tier calculation)
  • Branch: warren/HDX-5202-multiseries-metric-orderby
  • Author: wrn14897

To override this classification, remove the review/tier-4 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@github-actions

Copy link
Copy Markdown
Contributor

Deep Review

This PR fixes a real, well-scoped bug: multi-series metric tables grouped by an expression failed to render because convertToTableChartConfig defaults orderBy to the raw group-by text, which the composed outer query can't evaluate. The fix routes matched sort items through internal __hdx_sort_<n> companion columns (projected in scalar branches, NULL-padded in histogram branches, excluded via * EXCEPT, referenced as any(\_hdx_sort`)). The mechanism is sound and well-tested for the common path. Branch-ordering safety (scalar branches ordered first so their column names win the UNION ALL`) and the sort-parsing regex fall-through for unmatched items were both verified as correct.

🟡 P2 — recommended

  • packages/common-utils/src/core/renderChartConfig.ts:2673 — The usesWindowProjection guard (hasScalarGroups && !usesWindowProjection) skips the companion rewrite for share_of_total ratio tables, so an expression group-by ordered by that expression falls back to renderSortSpecificationList at line 2945 and leaves the raw expression in the outer ORDER BY over the window-wrapper scope, where source columns like ResourceAttributes don't exist and ClickHouse rejects the query. This is a pre-existing failure the diff deliberately narrows around rather than a new regression, but it is the same class of bug the fix advertises and remains reachable via the table default orderBy = groupBy.
    • Fix: Extend the companion-column rewrite to the window-projection wrapper path, or add an explicit code-level marker and a test pinning the known limitation.
    • previous-comments, correctness
  • packages/common-utils/src/__tests__/renderChartConfig.test.ts:4219 — The new test block covers table-default, structured, aliased, histogram-padding, and plain-column cases, but no test exercises the share_of_total + expression group-by ORDER BY path that the guard excludes, so the unresolved failure above has no regression guard.
    • Fix: Add a test that renders a share_of_total multi-series table grouped and ordered by an expression and asserts the intended behavior.
🔵 P3 nitpicks (2)
  • packages/common-utils/src/core/renderChartConfig.ts:2679 — The typeof chartConfig.groupBy === 'string' ? splitAndTrimWithBracket(...) : chartConfig.groupBy!.map(...) normalization is duplicated between the rewrittenSort computation and the scalarBranchGroupBy construction.
    • Fix: Extract the group-by normalization into a single local helper and reuse it in both places.
  • packages/common-utils/src/core/renderChartConfig.ts:2426parseSortSpecificationItems parses SQL ORDER BY text with a regex; items carrying modifiers (NULLS FIRST/LAST, WITH FILL, COLLATE) fall through untouched, which is safe but undocumented as a supported-passthrough case.
    • Fix: Add a brief test asserting a modifier-bearing sort item passes through unchanged.

Reviewers (6): correctness, adversarial, testing, maintainability, previous-comments, kieran-typescript.

Testing gaps: No coverage for share_of_total + expression group-by ordering; no coverage for multiple mixed matched/unmatched ORDER BY items in one list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-generated AI-generated content; review carefully before merging. review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant