Skip to content

Query Store has no total-CPU measure in the compose catalog — peak and per-execution average only, though the corrected rollups already hold the product-sums #2732

Closed

Description

@erikdarlingdata

Exercising the MCP custom-view surface against a fleet store where Query Store is the headline dataset, the first question anyone asks of Query Store — "what consumed the most CPU over this window" — turned out not to be expressible in the compose catalog at all.

Observed

Query Store has no total-CPU measure — only qs_max_cpu_us (peak) and qs_avg_cpu_us (per-execution ratio). For 'what consumed the most CPU', QS is unusable, which is a surprising hole given QS is this box's headline dataset.

Mechanism

The catalog's whole query_store_stats roster is five measures (Darling/PerformanceMonitor.Darling.Service/Compose/MeasureCatalog.cs:1053-1093):

  • qs_executions — SUM of execution_count (the only summable one)
  • qs_max_duration_us / qs_max_cpu_us — gauges over the per-interval peaks (MAX)
  • qs_avg_duration_us / qs_avg_cpu_us — MeasureRatioMode.Weighted ratios, SUM(avg * execution_count) / SUM(execution_count)

There is no total-duration or total-CPU measure, so a ranked panel (topN + ORDER BY value DESC) over Query Store can rank by executions, by a peak, or by a per-execution average — never by what a query group actually consumed. A per-execution average ranks a query that ran once at 30s over one that burned an hour of CPU at 200ms a call, which is the opposite of the question.

The bitter part is that every ingredient already exists:

  • The raw rows carry avg_cpu_time_us + execution_count per interval (PerformanceMonitor.Collectors/QueryStoreCollector.cs:1622-1626), so total CPU per interval is exactly the product — the numerator the Weighted ratio already compiles (Darling/PerformanceMonitor.Darling.Service/Compose/ComposeCompiler.cs:470-471): SUM(f.avg_cpu_time_us * f.execution_count) minus the denominator.
  • The corrected rollups already materialize the product-sums as columns: cpu_us_weighted_sum / duration_us_weighted_sum in query_store_stats_corrected_hourly / _daily (Darling/PerformanceMonitor.Darling.Storage/TimescaleSupport.cs:1335-1383), and ComposeCaggValueMapper already knows the column names (Compose/ComposeCaggValueMapper.cs:166-172).

So the gap is purely catalog/expression plumbing — no migration, no collector change, no backfill.

For the record, the fixed-function surface doesn't cover it either: get_query_store_top ranks by total duration (SUM(execution_count) * AVG(avg_duration_us), Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingDataReader.cs:1031) and returns avg_cpu_time_ms, so total CPU is nowhere on the Query Store read surface.

Fix direction

Add qs_total_cpu_us / qs_total_duration_us as a small new measure shape — a "weighted sum" (the Weighted ratio's numerator, no denominator):

  • raw route: SUM(f.avg_cpu_time_us * f.execution_count) — one new arm beside the Weighted arm in BuildValueExpr
  • CAGG route: SUM(f.cpu_us_weighted_sum) — one new arm in ComposeCaggValueMapper (CanRemapQueryStore + BuildQueryStoreExpr), reusing QsWeightedSumColumn

Either as a new MeasureRatioMode (WeightedSum) or a new MeasureKind; whichever is chosen, ValidAggs is fixed like a ratio's (the aggregation is the definition), unit family duration, default unit probably s at these magnitudes. Time-series, ranked, and scalar modes then all work for free, and "top query_hash by total CPU, hourly" becomes a legal panel.

Activity

  1. erikdarlingdata commented on Sep 1, 2026

    @erikdarlingdata
    OwnerAuthor

    Fixed by #2738 — qs_total_cpu_us and qs_total_duration_us via a WeightedSum ratio mode; raw route product-sums, CAGG routes read the pre-materialized weighted sums. No migration. Merged to dev; ships in the next release.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions