Fix: Group By on ratio charts#2538
Conversation
🦋 Changeset detectedLatest commit: 8043b34 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🟡 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
|
Greptile SummaryThis PR fixes grouped ratio charts and metric Group By autocomplete. The main changes are:
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (12): Last reviewed commit: "Merge branch 'main' into jordansimonovsk..." | Re-trigger Greptile |
E2E Test Results✅ All tests passed • 238 passed • 3 skipped • 1558s
Tests ran across 4 shards in parallel. |
Deep Review✅ No critical issues found. No P0/P1 ship-blockers. Ungrouped ratios and the merge/dedup path are well-tested, and the grouped-ratio fix is sound for the common (time-series, string group dimension) case. The items below are recommended fixes and nits — the flagship P2 is a silently-wrong-numbers edge case reachable only through the new opt-in share-of-total mode. 🟡 P2 -- recommended
🔵 P3 nitpicks (9)
Reviewers (8): correctness, adversarial, kieran-typescript, testing, maintainability, performance, api-contract, project-standards. Testing gaps:
|
|
Closes HDX-2555 |
| const _meta = resultSet.meta; | ||
| export const computeResultSetRatio = ( | ||
| resultSet: ResponseJSON<any>, | ||
| operands?: { numeratorName: string; denominatorName: string }, |
There was a problem hiding this comment.
Do we need the optional here if the only caller is always passing the operator? Or is there an expected use-case in the near future which needs the inference behavior?
| // Share-of-total semantics: each group's denominator is the total of the | ||
| // denominator column across ALL groups in the same time bucket. So a grouped | ||
| // ratio shows each group's contribution to the overall ratio (the lines sum | ||
| // to the ungrouped value) rather than each group's own in-group rate. With no | ||
| // grouping there's one row per bucket, so the bucket total equals that row's | ||
| // denominator and the result is unchanged. |
There was a problem hiding this comment.
I'm curious why these semantics were chosen. My initial instinct would have been to apply the ratio within-group, for use-cases where for example you want a per-service error %. What is the motivation for this behavior instead?
There was a problem hiding this comment.
For some of the use cases I was testing, I kind of wanted to determine a share of total, though you make a good point about something like error rate per service. That's an oversight by me! I'm going to toggle this behaviour
There was a problem hiding this comment.
Added a ratioMode config field with two values:
per_group (default):each group divided by its own denominator. A per-service error %, which is what you'd expect. service: errors / totalshare_of_total (opt-in):each group's numerator over the per-bucket total across all groups, so the lines decompose the blended rate and sum to the ungrouped value. I find this particularly useful to see how much of a total error rate would be comprised by which service.
The default is per_group, so we're not imposing the share of total interpretation. In the editor it surfaces as a "Share of total" switch that only appears when you have a ratio and a Group By.
| {fields.length === 2 && | ||
| seriesReturnType === 'ratio' && | ||
| hasGroupBy && ( | ||
| <Switch |
There was a problem hiding this comment.
Having the option is nice, but this seems to have no effect for non-metric type sources (since for those sources, we calculate the ratio within-group only, in the database, see renderSelectList in renderChartConfig)
There was a problem hiding this comment.
the toggle is now gated on tableSource?.kind === SourceKind.Metric (in addition to seriesReturnType === 'ratio' and a Group By being set), so it only shows for the metric sources that fan out to per-series queries and get merged client-side. Left a comment in the code documenting why.
… ratios Grouped ratios had a single baked-in interpretation (share-of-total). Add a `ratioMode` config field so users pick the semantic: - per_group (default): each group divided by its own denominator (per-service error %), the intuitive "group by a ratio" meaning - share_of_total: each group's numerator over the per-bucket total across all groups, so the lines decompose the blended rate and sum to the ungrouped value Surfaces as a "Share of total" switch in the chart editor, shown only for a grouped ratio (no-op otherwise). Ungrouped ratios are identical under both modes, so no behavior change for existing charts. Also make `computeResultSetRatio`'s `operands` required and drop the numeric- column inference fallback — the only caller always passes operands explicitly; tests updated to match.
Only used within its own module; knip flags it as an unused export and the pre-commit hook (knip --no-config-hints) fails on it. Pre-existing on main; fixing here so commits on this branch can pass the hook.
ratioMode is consumed only in mergeResultSets, reached only when splitChartConfigs fans a config out to multiple queries — which happens solely for metric sources. Non-metric sources compute the ratio within-group in the DB (renderSelectList), so the toggle was a silent no-op there. Gate it on SourceKind.Metric.
63fd5ec to
46bbb9b
Compare
…rence Address PR review on the grouped-ratio merge: - share_of_total bucketed rows via inferTimestampColumn (first Date-typed column), so a Date/DateTime group-by dimension ordered ahead of the real time bucket was mistaken for it and the per-bucket denominator was summed over the wrong column. Resolve the bucket by the query builder's FIXED_TIME_BUCKET_EXPR_ALIAS, falling back to inference only when absent. - operandNames.filter(Boolean) compacted a positional array, so a split with no inferable numeric column shifted the surviving operand into the numerator and left the denominator undefined, throwing and failing the whole chart response. Read operands positionally and return the merged rows undivided when either is missing. Adds tests for both paths plus zero-bucket-total, missing-own-denominator, and the collision rename combined with a carried-through group dimension.
Problem
Ratio charts (As Ratio) couldn't be meaningfully grouped:
Root cause
Ratio runs the numerator and denominator as two separate queries and merges them. Two bugs:
Separately, metric sources have no single from.tableName (they fan out to per-type tables),input had no table to fetch attribute keys from.
Changes
packages/common-utils/src/clickhouse/index.ts
packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx
typing on metric ratio/multi-series charts — matching the per-series inputs. Non-metric sou
Why share-of-total
"Group by" applied to a ratio has two plausible meanings: each group's own rate (per-group)n to the overall rate (share-of-total). We chose share-of-total so a grouped error-rate
decomposes the blended rate — the lines add up to the number you see ungrouped — which matcdown the 22% by tenant" chart. (Per-group rates are still available by not using ratio mode,or via a future toggle if needed.)
Testing
Scope
Only affects metric ratio charts — the only config path that splits into the merge/ratio coatio or non-metric charts.