Repository navigation
Add exact module filtering to Query Store MCP results - #4057
erikdarlingdata merged 8 commits into
Conversation
…match A module filter that matched nothing answered "empty" (the house word for a true negative) even when the window held no Query Store rows at all, where the filter is not the reason and the unfiltered read answers "unavailable". The Darling message also told the caller to read effective_start, which the status envelope did not carry. - Darling: decide on the window floor the tool already probes. No floor keeps the unfiltered "unavailable"; otherwise "empty" with effective_start, effective_hours_back and window_truncated as hints, plus a sentence when the raw tier did not reach the whole window. - Lite: the same decision through HasQueryStoreRowsInWindowAsync, a window-scoped twin of HasAnyQueryStoreStatAsync, asked only on this miss. - Live pin on Darling in McpFilterSemanticsLivePostgresTests (the A13 filter-in-the-query precedent): rank at top = 1, dedup of a re-collected interval, the miss's hints, and the empty-window "unavailable". Negative control: the old condition answers "empty" there. Lite pins the probe. - McpPayloadContractCensusTests rosters the miss as the second window-floor publisher in DarlingMcpDataTools.cs.
…d its docs match the code - Both heads go back to dev's text with one change, "Supports database and module filtering." (+11 chars, pinned deliberately). The ordering guidance lives in the parameter description, which is now 186 characters: the exact schema-qualified name, as get_top_procedures_by_cpu returns it in full_name, case-sensitive, applied before ranking, and ad-hoc statements are Adhoc. - McpToolsListBudget pins the new parameter on both SKUs. The total ceilings rise deliberately with a erikdarlingdata#4057 change-log line: Darling by the measured 246 bytes, Lite by the same 246 (the identical change through the shared schema code; Lite's runner cannot execute on the macOS rig, so CI confirms it). - DarlingDataReader: the floor probe's doc comment no longer says "filtered" (the probe is deliberately unfiltered), and QueryStoreTopSql's parameter list names $5 and $6.
|
Thanks for this. The filter is in the right place on both SKUs: after the interval dedup and before the ranking and the cap. A live check against PostgreSQL returned a cooler target at I pushed two commits to this branch (
|
AsOfWindowAnchorTests.EveryToolThatTakesTheAnchor_ActuallyUsesIt requires every LocalDataService read an anchored tool makes to pass asOfUtc by name, so a read that could take the anchor and is not given it cannot pass unseen. The probe was given windowEnd positionally, which answered for the right window but read as a dropped anchor. Named now. AsOfWindowAnchorTests: 30/30.
dev's erikdarlingdata#4065 (erikdarlingdata#3898) converted get_query_store_top to a two-tier description: a 422-char head, byte-identical on both SKUs, and the old text as the get_tool_guide tail. The four conflicts were that description and its budget pin on each SKU. - The head is taken from dev unchanged, so its pins (McpToolGuideHeads) and budget line stand as dev set them. - The module_name guidance goes in the tail, which tools/list does not serve: "Supports database filtering." becomes the module_name rules (exact, case-sensitive, schema-qualified, Adhoc / Unknown, after dedup and before ranking) and how a miss answers. - The tools/list total ceilings go back to dev's values on both SKUs, with this PR's change-log lines removed. After dev's conversions the Darling total measures 251,115 bytes against 329,494, so this PR no longer needs a raise. The only budget change left is the module_name parameter's pin line.
… reads HasQueryStoreRowsInWindowAsync sat beside HasAnyQueryStoreStatAsync in LocalDataService.QueryStats.cs, and its FROM v_query_store_stats made that file's row types read as able to reach Query Store. ConsumedTimestampFrameDisciplineTests resolves a render site's column through the relations its file reads, so three timestamp columns that query_stats and query_store_stats share in different clock frames stopped resolving: judged render sites fell from 43 to 40 (CI: build and Darling PG shard 0). The probe moves to LocalDataService.QueryStore.cs, which already reads the view; QueryStats.cs is back to dev byte for byte. The move is proved by bisect: with only QueryStats.cs from this PR the census judged 40, with it at dev 43. The QueryStore.cs source censuses do not move: 6 dedup partitions, 8 rn filters, no execution_count-first ORDER BY, 2 COALESCE buckets, and the probe says it is deliberately not deduped. ConsumedTimestampFrameDisciplineTests 17/17, AsOfWindowAnchorTests 30/30.
|
Merged current
The full Darling suite ran against live PostgreSQL at |
erikdarlingdata#4060's execution_type erikdarlingdata#4060 gave get_query_store_top an execution_type filter on the same read, row type, parameters and filtered-miss path. The two are combined: - SQL: execution_type stays $6, filtered in the dedup CTE as erikdarlingdata#4060 placed it; module_name becomes $7, still applied after the dedup and before the ranking. Rows carry execution_type and module_name; Darling's QueryStoreRow reader maps all 17 columns in projection order. Lite binds the outcome, then the module, after the database list. - Parameters, both SKUs: ..., as_of, execution_type, module_name (the parity theory, the web catalog and the dispatch follow). - A filtered miss takes erikdarlingdata#4060's test for both filters: the same read without the filters, scoped to the database. It replaces this PR's server-wide window floor on Darling and Lite's HasQueryStoreRowsInWindowAsync, which is removed; erikdarlingdata#4060's probe answers the same question, scoped to the database the read was. An execution_type-only miss keeps erikdarlingdata#4060's message word for word; a miss with module_name goes through the new shared McpHelpers.QueryStoreModuleEmpty, which names both filters, and on Darling still carries the served window as hints. - The tool-guide tails (after <<GUIDE>>, so the budgeted heads are dev's) describe both filters and the one miss rule. - Tests: the Darling live pin gains a module plus execution_type miss; Lite gains a database + outcome + module binding test, and loses the removed probe's test.
|
The full Darling suite against live PostgreSQL fails exactly the same tests at |
…4057) (#4084) TopQueries_ModuleFilterRunsAfterIntervalDedup had no [Fact] attribute, so xUnit never ran it and the Lite.Tests build warned xUnit1013. Adding the attribute makes it run. It passes. Claude-Session: https://claude.ai/code/session_014nCo36doEcUWapnXbJvY7L Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Add 48 buffered CHANGELOG entries in one splice Adds 48 entries and 48 link refs (#4057, #4077, #4079, #4081, #4082, #4083, #4084, #4085, #4086, #4087, #4088, #4089, #4090, #4091, #4092, #4093, #4095, #4096, #4099, #4100, #4101, #4103, #4105, #4106, #4107, #4108, #4109, #4110, #4111, #4113, #4114, #4115, #4116, #4117, #4118, #4119, #4120, #4121, #4122, #4123, #4124, #4125, #4126, #4127, #4131, #4133, #4136, #4141). Each PR's entry was buffered, and this lands every entry whose PR was merged on origin/dev when it ran. Entries found under a bare section line (none unless the old script ran again) move into the matching ### section, below the new entries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QQh8LczP1HGLXQYAx4oTZC * Changelog splice: add #4137's entry, which merged before the splice but was missed #4137 (pg_plan_capture reads csvlog) merged at 05:57Z with a CHANGELOG entry section in its body. It was neither spliced nor listed as needing no entry. Its entry goes under Fixed, right after #4136's, and #4136's last sentence now points to it instead of saying plan capture is unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUU29PuGg9TUFBsACgZg2K --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Problem
Query Store results are ranked globally, so callers cannot reliably investigate a specific stored-procedure module when its queries fall outside the top results. The response also omits the module attribution already present in collected data.
Value
Callers can retrieve exact module-scoped Query Store history without increasing broad result limits, while retaining query and plan identity for follow-up analysis.
Summary
Validation
CHANGELOG entry
get_query_store_toptakesmodule_name, so one stored procedure's Query Store history can be read without wideningtop([Add exact module filtering to Query Store MCP results #4057], contributed by @kendra-little) - the read ranked the whole window before capping it, so a procedure an investigation named but whose queries sat outside the top N could not be reached at all. The filter is part of the query on both SKUs. It matches the exact, case-sensitive, schema-qualified name the collector records (thefull_nameget_top_procedures_by_cpureturns,Adhocfor ad-hoc statements), and it applies after the Query Store aggregates double-count re-collected cumulative intervals - tier 2: interval identity + CAGG rebuild (tier 1 read-side dedup shipped in #1845) #1841 interval dedup and before the ranking and the cap. Every row now carriesmodule_name, beside theexecution_typeExpose Query Store execution outcomes in MCP results #4060 added, and the two filters compose. A filter that matches nothing answersemptyonly when the same read without the filters has rows, and the message names both filters; on Darling a module miss also carrieseffective_start,effective_hours_backandwindow_truncatedas hints. Otherwise the read answers as an unfiltered one would.