Repository navigation
Bucket the viewer's query/procedure/execution-count trend charts server-side (#4234) - #4333
Merged
Merged
Conversation
…4234) Extends DurationTrendRouting.BuildBucketedRawTrendSql with a database-filter bool (mirroring BuildRawTrendSql) and a trailing collection_count column, and switches the viewer's QueryDurationTrendSql/ProcedureDurationTrendSql to it. ExecutionCountTrendSql is bucketed in place, following the same pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Ruling item 6's three required tests per read: a bucket-width source check, a live 7-day-window row-budget cap, and point equality with the pre-bucketing shape when every bucket holds exactly one collection. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…erved collection (#4234) Lane T2d bucketed the viewer's three raw-tier Performance Trends reads but left two things behind: the chart title still said "one point per collection" even when points were merged into wider buckets, and the "data begins" instant it disclosed was a bucket start rather than the first collection the store actually held. QueryTrendSeries now carries BucketMinutes (0 when every point is one collection). The bucketed reader reports it plus the first served collection's own timestamp separately from the plotted points, so DescribeCoverage's truncation math runs against a real collection instant, not a date_bin grid line. DescribeTrendCoverage names the bucket width on the raw arm when it merged collections. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…tartUtc (#4234) DescribeTrendCoverage_NamesTheTier_AndTheHeadOnlyWhenTruncated gains a 15-minute, a 1-minute and a 2-hour BucketMinutes case for the title's wording. ViewerQueryTrendBucketingLiveTests gains a live pin: a 7-day window whose seed starts 3 days in, off the minute grid, asserts BucketMinutes > 0, Truncated == true and EffectiveStartUtc equal to the seeded start exactly — not a date_bin bucket start. Reverting the ReadRoutedDurationTrendAsync fix locally and rerunning this test alone failed it (Assert.Equal, values differ), confirming the assertion exercises the fix; the revert was not committed. The existing singleton-bucket point-equality test now also asserts BucketMinutes == 0. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
erikdarlingdata
marked this pull request as ready for review
September 25, 2026 18:08
erikdarlingdata
enabled auto-merge (squash)
September 25, 2026 18:08
This was referenced Sep 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #4234.
Why
The WPF Darling viewer's Performance Trends tab read the query-duration, procedure-duration and
execution-count charts (
ViewerDataService.QueryTrends.cs) at the raw tier as one row per collection. A7-day chart on a 1-minute cadence could ship thousands of rows per series, the same defect PR #4304 already
fixed for the Wait Stats and Perfmon trend charts. This lane (T2d) applies the same bucketing to the
remaining raw-tier reads named in #4234:
QueryDurationTrendSql,ProcedureDurationTrendSqlandExecutionCountTrendSql. The hourly tier andQueryStoreDurationTrendSqlwere out of scope (alreadybounded).
What changes
DurationTrendRouting.BuildBucketedRawTrendSql(rawTable, withDatabaseFilter)(Storage): added awithDatabaseFilterbool, mirroringBuildRawTrendSql. With the filter,$4is the viewer's guardedtext[]database filter and the bucket width moves to$5; every MCP caller passesfalse, so their SQLtext and
$4width are byte-identical to before. Added a trailingCOUNT(*) AS collection_countcolumn(the builder already had
first_collection_time); trailing so the MCP tools, which read this statementpositionally, are unaffected.
DarlingTrendReader.cs: the two MCP callers (QueryDurationTrendSql,ProcedureDurationTrendSqlfields,~750/~995) now pass
withDatabaseFilter: falseexplicitly. Same SQL text as before, mechanical change tosatisfy the new required parameter.
ViewerDataService.QueryTrends.cs:QueryDurationTrendSql/ProcedureDurationTrendSqlnow callBuildBucketedRawTrendSql(table, withDatabaseFilter: true)instead of the old per-collectionQueryDurationTrendRawSql/ProcedureDurationTrendRawSqlwrappers (those wrappers are unchanged and still used directly byDarlingMcpTrendToolsTests, so nothing was deleted).ExecutionCountTrendSqlhas no shared builder (it is the only raw trend that projects one rate), so itis bucketed in place, following the same
raw→rated→ bucketed-SELECTshape, changed fromconst stringtostatic readonly stringbecause the interpolatedTrendBucketSql.OriginSqlreference is nota compile-time constant.
ReadBucketedDurationTrendAsync: the PR WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304 pattern (GetWaitStatsTrendsByTypesAsync) ported to"one series per call": computes the auto bucket width against
TrendBudget.Chart.AutoPoints, buffersevery row, and stamps each point at its bucket's own
first_collection_time(notbucket_start) onlywhen EVERY bucket the call returned held exactly one physical collection; a single merged bucket anywhere
keeps
bucket_startfor the whole series. A NULL rate (nothing rated in a bucket) is skipped, matchingthe existing
ReadTrendPointsAsyncconvention.ReadRoutedDurationTrendAsync(shared body of the two duration trends) andGetExecutionCountTrendAsyncnow branch: the RAW tier goes through
ReadBucketedDurationTrendAsync; the HOURLY tier is untouched(already bounded to one row per hour, needs no further bucketing).
QueryTrendSeries.Tier, thethree SQL fields' summaries,
ReadRoutedDurationTrendAsync's summary).DarlingMcpTrendToolsTests.RawDurationTrendSql_ReadsTheStoredInterval_ThreeState_AndTheRawConstsAreItsAliases:updated the equality/identity assertions that compared the viewer's query/procedure fields against the
old per-collection builder output to compare against the bucketed builder instead (both with the
argument's reason stated in the doc comment), and updated the two source-text pins on
DarlingTrendReader.cs's declarations for the newwithDatabaseFilter: falseargument.ViewerTrendRoutingPortTests.ExecutionCountTrend_RoutesWithItsSibling_AndReadsTheSharedRollupColumn:updated the raw-arm source pin from
ReadTrendPointsAsync(...)toReadBucketedDurationTrendAsync(...).ViewerQueryTrendBucketingReviewTests.cs(ruling item 6's three required tests, per read):ViewerQueryTrendBucketWidthSqlTests(no live store) pins that each of the three SQL texts carries abucket width (
date_bin(CAST($5 AS integer) ...) and thefirst_collection_time/collection_countcolumns;
ViewerQueryTrendBucketingLiveTests(gated onDARLING_TEST_PG) pins a 7-day window at≤
TrendBudget.Chart.AutoPointsrows per series, and point equality (timestamps and values, seeded offthe minute grid at
:37seconds) against the pre-bucketing shape when every bucket holds exactly onecollection.
I confirmed the new source-check tests fail on the pre-fix code by hand: before this change none of the
three SQL texts contained
date_binanywhere (the old per-collection reads had no bucketing at all), soAssert.Contains("date_bin(CAST($5 AS integer) ...", ...)fails onorigin/dev.Left
DurationTrendRouting.QueryDurationTrendRawSql/ProcedureDurationTrendRawSql(the per-collectionbuilder wrappers) in place, unused by the viewer now but still exercised directly by
DarlingMcpTrendToolsTests's cross-checks — removing them was outside this lane's scope.Follow-up (lane T2e)
Closed the gap the paragraph above used to flag:
DescribeTrendCoverage's raw arm now names the bucketwidth when points were merged (
Source: raw (one point per 15 minutes), and so on) instead of alwaysclaiming one point per collection, and the "data begins" instant
ReadRoutedDurationTrendAsync/GetExecutionCountTrendAsyncdisclose is now the first served collection's own timestamp(
first_collection_time) rather than thedate_binbucket start it landed in.CHANGELOG entry
SECTION: Changed
ENTRY:
REF:
[Bucket the viewer's query/procedure/execution-count trend charts server-side (#4234) #4333]: Bucket the viewer's query/procedure/execution-count trend charts server-side (#4234) #4333
Test plan
dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug— 0 Warning(s), 0 Error(s).ViewerQueryTrendBucketWidthSqlTests,ViewerQueryTrendBucketingLiveTests,DarlingMcpTrendToolsTests,ViewerTrendRoutingPortTests,*DurationTrend*,*TrendRouting*,*QueryTrend*,ViewerTrendBucketingReviewTests,McpPayloadContractCensusTests,DocCommentHygiene*,DatabaseFilterTests,ViewerQueriesRestTests— 242 tests, 0 failures, 0 errors(re-run after merging
origin/dev).dotnet build Darling/Darling.Tests/Darling.Tests.csproj -c Debug— 0 Warning(s), 0 Error(s), after this lane'schanges and again after merging
origin/dev.ViewerTrendRoutingPortTests, both classes inViewerQueryTrendBucketingReviewTests.cs,DarlingMcpTrendToolsTests,*QueryTrend*,*TrendRouting*,DocCommentHygiene*): 140 tests, 0 failures, 0 errors — run once before mergingorigin/devand once after, both clean.EffectiveStartUtclive assertion actually exercises the fix: revertedReadRoutedDurationTrendAsync'sfirstServedUtcback topoints[0].CollectionTime(the pre-fixshape), rebuilt, and ran
QueryDurationTrend_SevenDayWindow_SeedStartsThreeDaysIn_EffectiveStartUtcIsFirstCollection_NotABucketStartalone — 1 test, 1 failure (
Assert.Equal() Failure: Values differ), confirming it catches the bug.The revert was never committed; restored and rebuilt clean before the runs above.
Installer.Tests: intentionally not run (out of scope per lane orders).🤖 Generated with Claude Code
https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ