Skip to content

Top-N reads answer empty, not unavailable, when the collector ran and stored nothing (#6226) - #6232

Merged
erikdarlingdata merged 2 commits into
devfrom
fix/6226-mcp-empty-when-collector-ran
Oct 20, 2026
Merged

erikdarlingdata merged 2 commits into
devfrom
fix/6226-mcp-empty-when-collector-ran

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Oct 20, 2026 •

Copy link
Copy Markdown
Owner

Claude posting for Erik Darling.

Closes #6226.

Why

get_top_queries_by_cpu answered unavailable ("No query stats available for the specified time range.") for a window in which query_stats ran successfully every minute and stored 0 rows, because nothing executed since its watermark. By the status words the server defines, that is empty (we looked, and nothing ran); unavailable means "could have it, does not right now: check collection health". #6127 already fixed the case where snapshots were stored without deltas. This is the remaining case: the collector ran and found nothing.

What changes

One shared sentence, CollectorRunFacts.RanFoundNothing (PerformanceMonitor.Common), used by both products. It returns null unless the collector has a successful (status SUCCESS) run inside the window. The ranking's empty path now ends: not_collected (unchanged, still first), then the #6127 snapshot answer (unchanged), then, new, empty when the run count is above zero, then unavailable as before.

Answer for a window with runs: {"status":"empty","message":"query_stats ran 6 time(s) in the last 1 hour(s) and found no executed statements to store. This is not a collection gap: ...","hints":{"successful_runs_in_window":6}}. With a database filter the sentence claims only that database ("stored no executed statements for database 'X'"), since the runs may have stored rows for other databases.

Tools, Darling and Lite matching where the tool exists in both:

  • Changed, get_top_queries_by_cpu (Darling and Lite). Darling applies it on the raw tier only, as get_top_queries_by_cpu answers unavailable for a window in which query_stats stored rows #6127 does: a window the hourly rollup serves has its own rows and a run count says nothing about it.
  • Changed, get_query_store_top (Darling and Lite). Collector query_store; sits after the existing Query Store precondition and first-interval answers, before the final unavailable. Darling applies it only when the raw tier answered (no interval table or daily summary served) and the window is within the raw retention span, because collection_log outlives the raw rows and a longer window's runs prove nothing about the dropped part. Lite has no rollup tier, so no such guard.
  • Changed, get_pg_top_queries (Darling only; Lite has no PostgreSQL reads). Collector pg_statement_stats. It also needs two successful runs overall (the existing delta-baseline rule): the first run stores nothing by design, so one run is not yet a measurement. Same retention-span guard.
  • Already done, left alone: get_top_procedures_by_cpu (both). [BUG] MCP tool audit on 3.9.0-nightly.20261006.515: 23 defects (blocking, spills, FinOps, store, tool payloads) #5633 answers empty from the procedure_stats run history; this reuses its data path.
  • Left alone: the other "No ... available" reads (wait stats, CPU, memory, file I/O, TempDB, perfmon, latch and spinlock stats, memory grants, plan cache, scheduler, object and index stats, database sizes, server properties). They are not top-N rankings of "what executed since the watermark": each collector stores a row per sample, so an empty read there does mean a collection gap. I did not audit each collector's storage rule one by one; if one of them turns out to store only changed values, it needs this same rule.

Test plan

  • New behaviour tests, Darling live (TopReadsCollectorRanEmptyLiveTests, 6 tests) and Lite (McpTopReadsCollectorRanEmptyTests, 5 tests): a window with successful zero-row runs gives empty with the count; a window with only a failed run and an older successful run stays unavailable; the get_top_queries_by_cpu answers unavailable for a window in which query_stats stored rows #6127 snapshot answer is unchanged even when the collector ran; a database filter names only that database; get_query_store_top empty/unavailable, plus the retention-span guard (Darling); get_pg_top_queries stays unavailable on one run and goes empty on two.

  • Reverted the three source files to dev and re-ran the new classes: Darling 3 of 5 failed (the three empty cases), Lite 3 of 5 failed. The unchanged-behaviour cases passed on both.

  • Darling live run on a throwaway PostgreSQL store: TopReadsCollectorRanEmpty* + TopQueriesSnapshotOnlyWindow*: 8 of 8 pass. Then 1857 tests (Census, Ratchet, QueryStore, TopQueries, McpPageContract, the PG top-queries and statement classes, CollectorRunEmptyReason): 0 failed, 2 skipped.

  • Lite: Mcp, QueryStore, TopQueries, TopProcedures, CollectorRun, Census and Ratchet classes: 1245 run, 0 failed.

  • Both test projects build with 0 warnings from my change (Darling.Tests: 0 Warning(s)).

  • bash .github/scripts/pre-push.sh from PowerShell 7: Guard stages green (Darling 4446 run, 0 failed; Lite 1196 run, 0 failed). Its last line (one word of the gate's own wording changed):

    GATE STOP: selection is the whole suite (darling 2030, lite 600): run the touched classes; CI decides (over PRE_PUSH_MAX_CLASSES=400 in one project; no selected class ran; logs /tmp/pre-push.P6t3Nn)

    The gate cannot select fewer than 400 classes here, so the classes listed above are the ones I ran; CI decides the rest.

  • Not run: the full Darling and Lite suites; Installer.Tests.

Proposed CHANGELOG line

SECTION: Fixed

  • A quiet server's top-queries reads say "nothing ran" instead of "unavailable" ([Top-N reads answer empty, not unavailable, when the collector ran and stored nothing (#6226) #6232]) - get_top_queries_by_cpu, get_query_store_top and get_pg_top_queries answered unavailable (check collection health) for a window in which the collector ran successfully and stored no rows because nothing executed. They now answer empty with successful_runs_in_window, and keep unavailable for a window with no successful run. Darling and Lite match; the Darling reads apply it only where the raw tier holds the whole window.

For the maintainer to double-check

  • The Query Store and PostgreSQL arms go beyond the query_stats case in the issue, as the issue asked for the sibling reads. They are separate lines in the diff and easy to drop if you would rather ship query_stats alone.
  • The hourly-rollup route of get_top_queries_by_cpu still ends in unavailable on an empty page, by design (see above).

@erikdarlingdata erikdarlingdata added the owner:pm-worker-helper Laptop helper session: click-test fixes. Other sessions leave these alone. label Oct 20, 2026
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review October 20, 2026 15:27
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) October 20, 2026 15:27
@erikdarlingdata
erikdarlingdata merged commit f4e6d2c into dev Oct 20, 2026
52 of 57 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/6226-mcp-empty-when-collector-ran branch October 20, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

owner:pm-worker-helper Laptop helper session: click-test fixes. Other sessions leave these alone.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant