Skip to content

Bucket the Darling viewer's Overview CPU/wait/memory lanes server-side (#4234) - #4327

Merged
erikdarlingdata merged 6 commits into
devfrom
fix/4234-viewer-overview-trends-buckets
Sep 25, 2026
Merged

erikdarlingdata merged 6 commits into
devfrom
fix/4234-viewer-overview-trends-buckets

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Part of #4234.

Why

The Overview lanes' CPU, total-wait and memory reads each return one row per collection (or, for CPU, one row per ring-buffer sample) with no cap: 10,080 rows over 7 days for CPU, 9,840 for total-wait and memory, per the issue's own measurement. This buckets three of the five reads in T2b's brief to TrendBudget.Chart's width, the same sizing contract PR #4304 (T1, merged as c51b43f) applied to the Wait Stats and Perfmon charts.

What changes

Done, committed and pushed (T2b):

  • ViewerDataService.Cpu.cs (CpuUtilizationSql, GetCpuUtilizationAsync): bucketed. Both CPU columns are gauges (ruling item 2), averaged per bucket with no MAX/peak column (neither the CPU tab's own chart nor the Overview lane draws one). CpuUtilizationSample's two fields moved int -> double. The read gained an optional endUtc parameter (width sizing only); both real call sites now pass it.
  • ViewerDataService.OverviewLanes.cs (TotalWaitTrendSql/GetTotalWaitTrendAsync, MemoryTrendSql/GetMemoryTrendAsync): bucketed. Wait is a rate (summed delta over summed interval, generalizing WaitTrendsSql's per-type case to one aggregate series, seriesCount=1). Memory's four MB metrics are gauges, averaged, no peak.
  • Both bucketed reads implement ruling item 3: when EVERY bucket a call returns holds exactly one physical collection, every point is stamped at that collection's own raw time instead of the bucket grid.
  • Darling.Tests: updated the CPU and Overview-lanes SQL-text source checks and added bucket-width source checks (ruling item 6) for the Overview reads.
  • Decided, no code change: Overview's blocking and deadlock lanes. BlockingTrendSql/DeadlockTrendSql stay at their existing fixed one-minute DATE_TRUNC — the MCP twin of this family stays fixed at one minute too, with no ladder or cap, and T3a's measurement put the real 7-day row counts at 297 and 41 (sparse, event-driven rows, not a dense per-collection series). Flagged for T2a/T3b's Lite lane to mirror "leave it."

Done this pass (T2c — finishing the PR: ran the tests T2b wrote but never ran, fixed what broke, filled the gap):

  • Ran ViewerCpuTempDbLivePostgresTests and ViewerOverviewLanesLivePostgresTests against a real Postgres 18 + TimescaleDB rig for the first time. Both passed as committed, but two other test classes did not once actually exercised:
    • ViewerCpuTempDbSqlTests still pinned the pre-bucketing CPU SQL shape (DoesNotContain("AVG(")/GROUP BY, ORDER BY sample_time) against the now-bucketed CpuUtilizationSql — 2 of 4 tests failed. The Overview file's equivalent SQL tests had been updated correctly; the CPU file's had not. Updated them for the bucketed shape and added the ruling item 6 bucket-width source check the CPU read was still missing.
    • TimeHonestyRungTests.BothWindowedCpuReads_PreferTheStoredUtcInstant_AndKeepTheDeSkewAsTheFallback pins the viewer's and the MCP get_cpu_utilization's CPU-read projections byte-identical at fixed column positions. WPF trend charts never adopted the #3897/#3960 bucket contract: at the "Last 7 days" window one chart reads 85–170 k raw rows, and the pickers re-run a week-long DISTINCT every refresh #4234 nested the viewer's copy one indent level deeper inside a new raw CTE, which shifted every line 4 columns over — genuinely breaking the literal pin (not a bug in the bucketing itself). Changed the comparison to collapse whitespace runs instead of comparing fixed columns, and split the ORDER BY sample_time / $3 assertions per reader, since bucketing changed both for the viewer only (bucket ordinal instead of the raw column; $3 is now a bucket width, not absent).
  • Added the two missing live tests ruling item 6 asks for, for all three reads (CPU, total-wait, memory) — 6 new tests total: a 7-day window returns at most TrendBudget.Chart.AutoPoints rows (proves the cap actually bites: seeded at the real 1-per-minute cadence via a bulk generate_series insert so 10,080 rows doesn't cost a per-row round trip), and point equality with the old per-collection read when every bucket in a window holds exactly one physical collection, seeded off the minute grid (:37 seconds) so a fix that floors to the bucket grid line instead of the raw timestamp would fail it. Proved each of the 6 fails on the old shape: temporarily forced a fixed 1-minute bucket width for the budget tests, and forced every point to stamp at bucket_start instead of its raw timestamp for the singleton tests; both variants turned every corresponding test red, then were reverted (no net source diff from this proof).
  • Resolved the PerformanceMonitor.Common.csproj merge conflict against origin/dev (which landed the same InternalsVisibleTo for PerformanceMonitor.Darling.Viewer via WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304/T1, as this PR's own description predicted): kept one copy, combined the two comments so it names both the Wait Stats/Perfmon charts (already on dev) and this PR's Overview lanes/CPU chart.
  • Merged origin/dev twice (once at the start of this pass, once again after adding the new tests, since dev moved again in between) — both merges clean except the one csproj conflict above; rebuilt and re-ran the full required class set after each.

Not done: Performance Trends moved to lane T2d's PR.

Test plan

  • Darling/Darling.Tests/Darling.Tests.csproj builds with 0 Warning(s), 0 Error(s) after every commit (5 rebuilds this pass).
  • Live round-trip against a real Postgres 18 + TimescaleDB rig (DARLING_TEST_PG set, migrations applied) — run for the first time this pass.
  • Full required class set for this PR, all against the live rig where the class has a live component:
    • ViewerCpuTempDbSqlTests: 12 passed (2 fixed, 1 added, rest unchanged).
    • ViewerCpuTempDbLivePostgresTests: 6 passed (4 existing + 2 new).
    • ViewerOverviewLanesSqlTests: 9 passed (unchanged from T2b).
    • ViewerOverviewLanesLivePostgresTests: 7 passed (3 existing + 4 new).
    • ViewerTrendBucketWidthSqlTests: 3 passed.
    • ViewerNameListCacheTests: 5 passed.
    • ViewerTrendBucketingLiveTests: 6 passed.
    • TimeHonestyRungTests: 8 passed (1 fixed).
    • TimeHonestyRungLivePostgresTests: 1 passed.
    • DocCommentHygieneTests: 77 passed.
    • Total: 134 passed, 0 failed, 0 skipped.
  • Proved once (twice, actually — once for the budget cap, once for the singleton passthrough) that each of the 6 new tests fails on the old shape; see above.
  • git merge origin/dev, twice; clean except the one documented csproj conflict; rebuilt and re-ran the class set above after each.
  • Full suite: CI runs it, per the brief.

What the coordinator should double-check

  1. The blocking/deadlock "leave as is" call — a real design decision with Lite-parity consequences, made by T2b.
  2. The "per-execution average" vs. "same rate formula as MCP" conflict T2b flagged for Performance Trends — now lane T2d's decision to make, on their own PR.
  3. This PR now merges origin/dev cleanly as of this pass (merge commits included); the one real conflict (the PerformanceMonitor.Common.csproj InternalsVisibleTo comment, from WPF Wait Stats and Perfmon trend charts bucket server-side (#4234) #4304 landing the same entry) is resolved by keeping one copy with a combined comment.
  4. WPF trend charts never adopted the #3897/#3960 bucket contract: at the "Last 7 days" window one chart reads 85–170 k raw rows, and the pickers re-run a week-long DISTINCT every refresh #4234 must stay open until T2d's Performance Trends PR and any remaining parts (T2a/T3b's Lite mirror) land.

CHANGELOG entry

SECTION: Changed
ENTRY:

🤖 Generated with Claude Code

https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ

erikdarlingdata and others added 3 commits September 25, 2026 13:09
The CPU tab and Overview CPU lane both read every ring-buffer sample with no
cap (10,080 rows over a 7-day window, per the issue's own measurement). Buckets
to TrendBudget.Chart's width, averaging each gauge with a NULL other-process
value (SQL on Linux) coalesced to 0 before averaging so a bucket cannot drift
from what the old per-row "NULL reads as 0" rule showed. A bucket holding
exactly one physical sample is stamped at that sample's own raw time rather
than the bucket grid, so a normal-width chart is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
GetTotalWaitTrendAsync and GetMemoryTrendAsync each read one row per
collection with no cap (9,840 rows over a measured 7-day window). Buckets to
TrendBudget.Chart's width: the wait lane is a rate (summed delta over summed
interval, mirroring WaitTrendsSql's single-series case), the four memory
metrics are gauges (averaged per bucket). A bucket holding exactly one
physical collection is stamped at that collection's own raw time rather than
the bucket grid, so a normal-width chart is unchanged; both live round-trip
tests already exercise short windows that stay at 1-minute singleton buckets
and pass unmodified.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…ew-trends-buckets

# Conflicts:
#	PerformanceMonitor.Common/PerformanceMonitor.Common.csproj
erikdarlingdata and others added 3 commits September 25, 2026 13:31
ViewerCpuTempDbSqlTests still pinned the pre-bucketing CPU SQL shape
(no AVG/GROUP BY, ORDER BY sample_time) against the now-bucketed
CpuUtilizationSql; two tests failed. Updated them for the bucketed
shape and added the ruling item 6 bucket-width source check the CPU
read was missing (Overview's wait/memory reads already had theirs).

TimeHonestyRungTests.BothWindowedCpuReads_... pinned the viewer's and
MCP's CPU projections byte-identical at fixed column positions; #4234
nested the viewer's copy one indent level deeper inside a CTE, so the
comparison now collapses whitespace runs instead, and the ORDER BY /
$3 assertions split per reader since bucketing changed both (bucket
ordinal instead of sample_time; $3 is now a bucket width, not absent).

Ran ViewerCpuTempDbLivePostgresTests and ViewerOverviewLanesLivePostgresTests
(T2b wrote these but never ran them against a rig) plus every class in
the two files touched, DocCommentHygiene, ViewerTrendBucketingReviewTests,
*OverviewLane*, *CpuTempDb* and TimeHonestyRungTests: all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…ry (#4234)

Ruling item 6 asks for two live pins per bucketed read that the base
PR's tests didn't cover: a 7-day window returning at most
TrendBudget.Chart's row budget (proves the cap actually bites, not
just that the SQL text carries a width), and point equality with the
old per-collection read when every bucket in a window holds exactly
one physical collection, seeded off the minute grid (:37 seconds) so
a fix that floors to the bucket grid line instead of the raw
timestamp would fail it.

Added both for all three reads (CPU, total-wait, memory), plus bulk
generate_series seed helpers so the 7-day/10,080-row case doesn't pay
a per-row round trip. Proved each of the 6 new tests fails on the old
shape by temporarily forcing a fixed 1-minute bucket width (the
budget tests) and forcing every point to the bucket_start instead of
its raw timestamp (the singleton tests), confirming red, then
reverting — no net source diff, test-only commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 25, 2026 17:53
@erikdarlingdata
erikdarlingdata enabled auto-merge (squash) September 25, 2026 17:53
@erikdarlingdata
erikdarlingdata merged commit 19e0fb8 into dev Sep 25, 2026
21 of 22 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4234-viewer-overview-trends-buckets branch September 25, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant