Skip to content

Commit d15e948

Browse files
Optimize MCP query tools for large databases (#826)
The three heaviest MCP tools (get_top_queries_by_cpu, get_top_procedures_by_cpu, get_query_store_top) timed out on large databases because they scanned every row, decompressed text on every row, and returned all results for client-side filtering. Dashboard: add new MCP-specific service methods using multi-phase temp tables — aggregate numerics first, rank TOP N, then hydrate text via OUTER APPLY for only the winners. DECOMPRESS now runs on ~N rows instead of the entire table. Lite: rewrite queries with CTE-based phasing — inner CTE aggregates numeric-only columns with LIMIT, outer query hydrates text via LEFT JOIN LATERAL for winners. Both: push database_name filter and TOP N into SQL instead of client-side filtering. Shared UI methods are untouched. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bffd7a7 commit d15e948

5 files changed

Lines changed: 789 additions & 116 deletions

File tree

Dashboard/Mcp/McpQueryTools.cs

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,13 @@ public static async Task<string> GetTopQueriesByCpu(
3838
var topError = McpHelpers.ValidateTop(top, "top");
3939
if (topError != null) return topError;
4040

41-
var rows = await resolved.Value.Service.GetQueryStatsAsync(hours_back);
41+
var rows = await resolved.Value.Service.GetQueryStatsForMcpAsync(hours_back, top, database_name, parallel_only, min_dop);
4242
if (rows.Count == 0)
4343
{
4444
return "No query stats available for the specified time range.";
4545
}
4646

47-
IEnumerable<QueryStatsItem> filtered = rows;
48-
if (!string.IsNullOrEmpty(database_name))
49-
filtered = filtered.Where(r => string.Equals(r.DatabaseName, database_name, StringComparison.OrdinalIgnoreCase));
50-
if (parallel_only || min_dop > 1)
51-
filtered = filtered.Where(r => r.MaxDop > 1 && r.MaxDop >= (min_dop > 1 ? min_dop : 2));
52-
53-
var result = filtered.Take(top).Select(r => new
47+
var result = rows.Select(r => new
5448
{
5549
database_name = r.DatabaseName,
5650
query_hash = r.QueryHash,
@@ -116,17 +110,13 @@ public static async Task<string> GetTopProceduresByCpu(
116110
var topError = McpHelpers.ValidateTop(top, "top");
117111
if (topError != null) return topError;
118112

119-
var rows = await resolved.Value.Service.GetProcedureStatsAsync(hours_back);
113+
var rows = await resolved.Value.Service.GetProcedureStatsForMcpAsync(hours_back, top, database_name);
120114
if (rows.Count == 0)
121115
{
122116
return "No procedure stats available for the specified time range.";
123117
}
124118

125-
IEnumerable<ProcedureStatsItem> filtered = rows;
126-
if (!string.IsNullOrEmpty(database_name))
127-
filtered = filtered.Where(r => string.Equals(r.DatabaseName, database_name, StringComparison.OrdinalIgnoreCase));
128-
129-
var result = filtered.Take(top).Select(r => new
119+
var result = rows.Select(r => new
130120
{
131121
database_name = r.DatabaseName,
132122
full_name = r.FullObjectName,
@@ -187,19 +177,13 @@ public static async Task<string> GetQueryStoreTop(
187177
var topError = McpHelpers.ValidateTop(top, "top");
188178
if (topError != null) return topError;
189179

190-
var rows = await resolved.Value.Service.GetQueryStoreDataAsync(hours_back);
180+
var rows = await resolved.Value.Service.GetQueryStoreDataForMcpAsync(hours_back, top, database_name, parallel_only, min_dop);
191181
if (rows.Count == 0)
192182
{
193183
return "No Query Store data available. Query Store may not be enabled on target databases.";
194184
}
195185

196-
IEnumerable<QueryStoreItem> filtered = rows;
197-
if (!string.IsNullOrEmpty(database_name))
198-
filtered = filtered.Where(r => string.Equals(r.DatabaseName, database_name, StringComparison.OrdinalIgnoreCase));
199-
if (parallel_only || min_dop > 1)
200-
filtered = filtered.Where(r => r.MaxDop > 1 && r.MaxDop >= (min_dop > 1 ? min_dop : 2));
201-
202-
var result = filtered.Take(top).Select(r => new
186+
var result = rows.Select(r => new
203187
{
204188
database_name = r.DatabaseName,
205189
query_id = r.QueryId,

0 commit comments

Comments
 (0)