Repository navigation
Bucket the Darling viewer's File I/O and memory clerk trend charts (#4234) - #4329
Merged
Merged
Conversation
Both reads used to return every collection (measured 85,010 rows at 7 days for one store on the File I/O tab). Groups now by TrendBudget.Chart-sized date_bin buckets, same sizing as the MCP trend reads: latency averages summed stall-ms over summed read/write count deltas per bucket, throughput sums byte deltas over summed interval seconds. A bucket holding exactly one physical collection (rated or not) keeps that collection's own raw timestamp instead of the bucket_start grid line, matching WaitTrendsSql's pattern from PR #4304. Also pulls in the InternalsVisibleTo grant and ViewerNameListCache dependency shared with that PR's pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
The clerk trend read (up to 20 clerks) returned every collection; it now buckets as a gauge (average memory_mb per bucket), same sizing as the MCP trend reads and the File I/O reads just bucketed. A bucket holding exactly one physical collection keeps that collection's own raw timestamp, matching WaitTrendsSql. DistinctMemoryClerkTypesSql (the clerk picker's population read) now sits behind ViewerNameListCache, same as DistinctWaitTypesSql in PR #4304, so the full-window DISTINCT runs at most once per 15 minutes instead of on every auto-refresh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
#4234) Updates the existing SQL-text assertions that the bucketing rewrite made stale (GROUP BY/ORDER BY shape, the rated-CTE column names, the CAST(memory_mb...) literal), and adds the ruling's four required tests per read: a bucket-width source check, a live 7-day-window row-budget cap, live point equality with the old shape when every bucket holds one collection (seeded off the minute grid at :37 seconds), and for the memory clerk picker, a live proof that a second call inside 15 minutes answers from cache and that a window end past the TTL misses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…ton test The singleton test's t1..t3 are 5 minutes apart, but the byte delta was calibrated for a 60-second interval (copied from the other throughput test, which is 60-second spaced) -- it read 0.2 MB/s instead of the intended 1.0. Caught by actually running the new test. 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 17:29
erikdarlingdata
enabled auto-merge (squash)
September 25, 2026 17:29
This was referenced Sep 25, 2026
erikdarlingdata
added a commit
that referenced
this pull request
Sep 25, 2026
* Bucket the Darling viewer's TempDB file I/O trend (#4234) TempDbFileIoTrendSql returned one row per (collection_time, file_name) for a tempdb file, unbounded on the window end, unlike the File I/O tab's own reads (#4329) and Lite's already-bucketed twin (#4340). A 7-day window at 1-minute cadence for 4 files measured 40,324 rows on a throwaway rig seed; the same seed through the new date_bin bucket (auto-picked to 10 minutes for that window) measures 4,036. GetTempDbFileIoTrendAsync now takes (serverId, startUtc, endUtc) like the other bucketed trend reads, computes its bucket width from TrendBuckets.AutoMinutes, and stamps points at their own raw collection time instead of the bucket grid when every returned bucket is a singleton. The #3540 "no delta knowable" marker is carried into a rated CTE (nulled, not filtered) so collection_count still counts an unrated row and a bucket is never mistaken for a true singleton. LoadTempDbAsync passes endUtc through and drops the now-redundant client-side post-filter on the file-I/O series.
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 File I/O tab and Memory tab's clerk chart read every collection in the
window instead of bucketing, the same problem #4234 already fixed for Wait Stats and Perfmon in
PR #4304. Per #4234's body, one production store returned 85,010 File I/O rows for a 7-day window.
The memory clerk trend has the same shape (up to 20 clerks, one row per collection each).
What changes
Following PR #4304's pattern (
WaitTrendsSql,ViewerNameListCache) exactly, per the ruling atissuecomment-5834669118:
ViewerDataService.FileIo.cs-FileIoLatencyTrendSqlandFileIoThroughputTrendSqlnowgroup into
date_binbuckets sized byTrendBuckets.AutoMinutes(windowMinutes, 1, TrendBudget.Chart.AutoPoints), the same sizing the MCP trend reads use. Latency averages summedstall-ms over summed read/write count deltas per bucket; throughput sums byte deltas over summed
interval-seconds (time-weighted, never an average of per-collection rates). The existing top-10-files
limit is unchanged. A bucket holding exactly one physical collection (rated or not — matching
WaitTrendsSql's documented rule) is stamped at that collection's own rawcollection_timeinsteadof the bucket's
date_bingrid line.ratedCTE now carries the Measurement-layer campaign: the delta honesty contract (11 findings, one keystone) #3540 "interval 0 is unknowable" rule (previously aWHEREfilter that dropped the row outright) as a per-row
CASEnull-out, so an unrated row still countstoward
collection_countfor singleton detection but never contributes to a bucket's sums. A newHAVING COUNT(rated_reads) > 0/HAVING COUNT(rated_seconds) > 0preserves the pre-bucket "absent,not 0.00" answer for a bucket where every row was unrated.
ViewerDataService.Memory.cs-MemoryClerkTrendsSql(up to 20 clerks) now buckets as a gauge(
AVG(memory_mb)per bucket, no rate math — a clerk's memory_mb is a point-in-time reading, not anaccumulating counter), with the same singleton-stamping rule.
DistinctMemoryClerkTypesSql(theclerk picker's population read) now sits behind
ViewerNameListCache(
GetDistinctMemoryClerkTypesAsyncgained an optionalnowUtcclock seam, defaultnullso everyexisting 3-arg call site is unaffected), same as
DistinctWaitTypesSqlin PR WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304: a full-windowDISTINCTruns at most once per 15 minutes per (server, window length), and a hit also requires thecached window's end to sit within the TTL of the requested end.
MemoryGrantTrendSql,MemoryGrantChartDataSql,LatestMemoryStatsSql, andMemoryPressureEventsSqlare untouched, per the brief's scope.PerformanceMonitor.Common.csprojgained thePerformanceMonitor.Darling.ViewerInternalsVisibleTogrant and
Darling/PerformanceMonitor.Darling.Viewer/ViewerNameListCache.cswas added — both copiedverbatim from PR WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304's pattern before that PR merged into
devout from under this branch (see"Timing note" below); after merging
origin/dev, both turned out byte-identical to what WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304 landed,so the merge picked them up cleanly with no conflict.
ViewerServerTab.FileIo.cs,ViewerServerTab.Memory.cs,CorrelatedTimelineLanesControl.xaml.cs) consume the same record shapes unchanged; none assume afixed point spacing, and the two chart renderers already do their own per-series
GroupBy+OrderBy(CollectionTime), so the SQL's new series-firstORDER BY(mirroringWaitTrendsSql,instead of the old collection-time-first order) needed no renderer changes.
Timing note
origin/devdidn't have PR #4304 merged when this branch was created, so the branch started bycopying
ViewerNameListCache.csand the csproj edit from that PR's branch by hand. #4304 merged todevpartway through this lane's work; mergingorigin/devafterward picked up the real filescleanly (byte-identical, no conflict) and also brought in the real
ViewerTrendBucketingReviewTests.csthis brief pointed at.
Test plan
Ran from
Darling.Tests.exe(MTP), against a throwaway PostgreSQL 18 + TimescaleDB rig(
postgresql.confforced to UTC to match CI),darlingtestdatabase, after mergingorigin/dev:Darling/Darling.Tests/Darling.Tests.csprojbuilds with 0 Warning(s), 0 Error(s).ViewerFileIoBlockingSqlTests,ViewerFileIoBlockingLivePostgresTests,ViewerMemorySqlTests,ViewerMemoryPickerLogicTests,ViewerMemoryLivePostgresTests,ViewerTrendBucketWidthSqlTests,ViewerNameListCacheTests,ViewerTrendBucketingLiveTests,DocCommentHygieneTests- 149/149 pass, 0 failed.ViewerFileIoBlockingSqlTests/ViewerMemorySqlTeststhat the bucketing rewrite made stale (GROUP BY/ORDER BYshape, therated-CTE column names, theCAST(memory_mb...)literal).row-budget cap; live point equality with the old shape when every bucket holds one collection,
seeded off the minute grid at :37 seconds; for the clerk picker, a live proof that a second call
inside 15 minutes answers from cache and a window end past the TTL misses even within the
wall-clock TTL).
origin/dev's (pre-this-PR)ViewerDataService.FileIo.csandViewerDataService.Memory.cs,rebuilt, and re-ran just the two new 7-day-budget-cap tests - both failed (too many rows) as
expected, confirming they're real regression pins. The two singleton point-equality tests
passed even against old code too, which is expected and consistent with
WaitTrendsSql's owntest suite: old code always returned raw unbucketed points, so it trivially satisfies "raw
timestamps unchanged" - that test's value is catching a future regression that buckets without
singleton detection, not distinguishing old from new. Restored both files to the bucketed version
afterward and re-ran the full 149 to confirm.
calibrated for a 60-second collection interval, but its three seeded points are 5 minutes apart -
it read 0.2 MB/s instead of the intended 1.0. Fixed in a follow-up commit once I actually ran it.
CHANGELOG entry
SECTION: Changed
ENTRY:
File I/O tab's Latency and Throughput charts and the Memory tab's clerk trend used to fetch every
raw collection in the selected window (measured 85,010 rows for one store's File I/O tab over 7
days); they now bucket server-side to a fixed point budget per line, with the same time bucketing the
MCP trend reads use. A window small enough that every bucket holds one collection still renders the exact
same points at their original timestamps. The memory clerk picker's name list is now cached for 15
minutes instead of re-querying on every auto-refresh.
REF:
[Bucket the Darling viewer's File I/O and memory clerk trend charts (#4234) #4329]: Bucket the Darling viewer's File I/O and memory clerk trend charts (#4234) #4329
🤖 Generated with Claude Code
https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ