[BUGFIX] StatChart: derive query mode from the spec instead of always using range#746
[BUGFIX] StatChart: derive query mode from the spec instead of always using range#746s3onghyun wants to merge 3 commits into
Conversation
… using range StatChart never sets queryOptions, so it always issues range queries. For a panel showing a single aggregate that is wasteful, and against backends that split range queries by interval it produces fragmented results on wide time ranges (perses#4277). perses#738 fixed that by declaring queryOptions: { mode: 'instant' } unconditionally and was reverted, because the sparkline draws the series itself and broke without range data. The same applies to the calculation: StatChartPanel computes it client-side over seriesData, so mean/sum/min/max/first collapse onto a single point under an instant query. So the mode is derived from the spec instead: range when a sparkline is configured or when the calculation aggregates over the series, instant otherwise. This mirrors how the table plugin already picks its mode via getTablePanelQueryOptions. Defaults are unaffected — createInitialStatChartOptions enables the sparkline, so a freshly created stat panel still uses range. Signed-off-by: s3onghyun <s3onghyun@users.noreply.github.com>
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| import { CalculationType } from '@perses-dev/core'; |
There was a problem hiding this comment.
please avoid to use the package @perses-dev/core but @perses-dev/spec instead.
There was a problem hiding this comment.
Fixed in 3ab3dbe. I used @perses-dev/plugin-system rather than @perses-dev/spec — CalculationType is re-exported there, and it is what every other consumer in the repo uses, including stat-chart-model.ts right next to this test. That test file was the only place reaching into @perses-dev/core directly. Happy to switch to @perses-dev/spec if that is the direction you want these to move.
|
adding @Gladorme for the followup |
Review feedback: the test was the only file in the plugin reaching into @perses-dev/core directly. Every other consumer of CalculationType, including stat-chart-model.ts next to it, imports it from @perses-dev/plugin-system. Signed-off-by: s3onghyun <s3onghyun@users.noreply.github.com>
| * (`mean`, `sum`, `min`, `max`, `first`, `first-number`) is computed over the whole | ||
| * series in `StatChartPanel`, so it needs range data to be correct. | ||
| */ | ||
| const LATEST_POINT_CALCULATIONS: ReadonlySet<CalculationType> = new Set<CalculationType>(['last', 'last-number']); |
There was a problem hiding this comment.
I am not expert in that part but what is the difference between last and last-number.
If last number means last result of number type or something, could the range query and instance query give different results ?
There was a problem hiding this comment.
Great question — and you're right, they are not interchangeable here. last is literally the last value of the series; last-number is the last non-null numeric value ("Last numeric value"), so when the most recent point is null it walks back to an earlier one. An instant query only returns that latest point, so for last-number with a trailing gap it would give a different (or empty) result than the range query — exactly the divergence you spotted. So only last is instant-safe. Fixed in 5ec159a: last-number now uses range like the aggregating calculations, and the set is renamed INSTANT_SAFE_CALCULATIONS = {last} to make the reasoning explicit. Thanks for catching it.
|
LGTM, @Gladorme WDYT? |
Review feedback from @celian-garcia: last-number is 'Last numeric value', i.e. the last *non-null* number. When the most recent point is null it must fall back to an earlier point, which an instant query cannot see — so instant and range would disagree. Only 'last' is literally the latest value and reproduces exactly under an instant query. Renamed the set to INSTANT_SAFE_CALCULATIONS = {last}, moved last-number to the range group with a test asserting it. Signed-off-by: s3onghyun <s3onghyun@users.noreply.github.com>
|
Thanks @jgbernalp. Pushed one more fix from @celian-garcia's review (5ec159a): |
|
@s3onghyun So when sparkline is enabled the StatChart value will be incorrect? |
celian-garcia
left a comment
There was a problem hiding this comment.
A last question from me @s3onghyun if you edit live the sparkline checkbox will it update the options live ?
|
No — the value stays correct with the sparkline enabled. In that case the panel keeps requesting a range query (the sparkline needs the series to draw), and The only mode change this PR introduces is: sparkline off and calculation is So this is strictly a narrowing: fewer situations issue a range query, and only in situations where the range data was never consumed. The sparkline path is untouched. Your #738 broke because it forced instant on that path; this keys off the spec so it can't. |
|
@celian-garcia Yes, it updates live — I traced the path rather than assuming:
So: check/uncheck sparkline → spec changes → memo recomputes mode → queryKey changes → refetch. Same chain applies to changing the calculation. The |
|
@s3onghyun So when sparkline is ON and the panel stays on range, both the sparkline and the value can be wrong. The real fix is to decouple them: instant query for the value, range query for the sparkline. They have different data needs and shouldn't share a single query mode. |
|
You're right, and thanks for the detail — that's a failure mode I hadn't accounted for. If On the fix, I think it's worth separating what this PR can do from what the real fix needs:
So two honest options:
I'm fine with either — you and @Gladorme know the roadmap here better than I do. If it's (1), I'm happy to write up the follow-up issue with the |
|
Hey, by coincidence I started working on the same topic with a different approach in #752. Would love to hear your opinion on that :D |
|
Update: @SoWieMarkus opened #752, which adds a per-query instant flag to the Prometheus query spec (Grafana-style). That's a better layer than this PR — it lets the stat value use an instant query and the sparkline a range query as two independent queries, which is exactly the decoupling @tgitelman asked for and which So I'd suggest we go with #752 and close this. I'll hold off closing for a moment in case @Gladorme still wants the narrow 'instant when sparkline off + last' improvement as an interim, but my recommendation is to prefer #752. Happy to do the StatChart-side follow-up (issue the instant value query once the flag exists) and move my sparkline regression tests over. |
|
Closing in favour of #752, which the team is already iterating on. Its per-query instant flag is the right layer — it decouples the stat value (instant) from the sparkline (range) as independent queries, which is what @tgitelman's Loki Happy to pick up the StatChart-side follow-up once #752 lands (have the stat panel issue an instant query for its value, keep range for the sparkline), and to move my sparkline regression tests over so #738's failure stays covered. Thanks all for the thorough review — the discussion made the right design clearer. |
Description
Follow-up to #738 (reverted) and perses/perses#4277.
StatChartdeclares noqueryOptions, so it always issues range queries. For a panel that renders one aggregate value that is wasteful, and against backends that split range queries by interval (Loki is the reported case) it fragments the result over wide time ranges.#738 addressed this with
queryOptions: { mode: 'instant' }, and was reverted because the sparkline draws the series itself and breaks without range data. As @Gladorme put it there, "Stat chart need to support both instant and range".There is a second consumer of the series besides the sparkline:
StatChartPanelcomputes the calculation client-side viacalculateValue(calculation, seriesData), somean/sum/min/max/first/first-numberwould silently collapse onto the single point an instant query returns. Onlylast/last-numberare safe.So this derives the mode from the spec rather than hard-coding it:
wired as
queryOptions: getStatChartQueryOptions, mirroring how the table plugin already selects its mode throughgetTablePanelQueryOptions.spec.sparkline === undefinedis the disabled state — the settings editor toggles it withdraft.sparkline = checked ? {} : undefinedand reads it back as!!value.sparkline.No change to defaults.
createInitialStatChartOptions()setssparkline: {}, so a freshly created stat panel keeps using range queries. Instant is only requested once a user turns the sparkline off and leaves the calculation onlast/last-number— which is exactly the case where the series is unused.Testing
Added
stat-chart-model.test.tscovering the mode matrix, including an explicit regression case for the sparkline (the reason #738 was reverted) and one for each range-dependent calculation.type-checkandlintpass locally. I could not run jest locally —jest.config.tsimports../jest.sharedand that fails to resolve on Node 23 (ERR_MODULE_NOT_FOUND) for untouched plugins too, so it looks environmental rather than related to this change; relying on CI for the test run.