Repository navigation
Dashboard query comparison grids use the server's local time (#4313) - #4321
Merged
Merged
Conversation
…eset range (#4305) CorrelatedTimelineLanesControl.xaml.cs is a copy of Lite's control (SYNC WARNING), which had the identical bug PR #4309 (#4296) fixed: under a preset range, the comparison ghost line built its reference window from DateTime.UtcNow while the correlated lanes' own reads treat a supplied fromDate/toDate as server-local (collection_time defaults to SYSDATETIME() throughout install/*.sql). Mirrors that fix here: GetCurrentWindowServerLocal/GetOverviewComparisonRange, CurrentFrom threaded through RefreshAsync's timeShift and ComparisonLabel, and the caller (ResourceMetricsContent/ServerTab) switched to deriving the range fresh, per refresh, from the Compare dropdown's index rather than a value resolved once and reused across later auto-refreshes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
…ry-grid-comparison
Query Stats / Proc Stats / Query Store comparison grids built their preset-range current window from DateTime.UtcNow (ServerTab.xaml.cs's GetComparisonRange, QueryPerformanceContent.Comparison.cs's RefreshComparisonAsync) while the reads they feed (GetQueryStatsComparisonAsync, GetProcedureStatsComparisonAsync, GetQueryStoreComparisonAsync) filter collection_time, which is server-local (collect.query_stats/proc_stats/query_store_stats default it to SYSDATETIME()). Same bug family as #4305/#4317's Overview ghost line, on the two spots #4305 explicitly left for this issue. Also fixes a third spot #4305 didn't touch: CorrelatedTimelineLanesControl.xaml.cs's RefreshAsync baseline referenceTime fed SqlServerBaselineProvider.GetBaselineAsync's hour-of-day/day-of-week bucket match, also against collection_time, from the same raw-UTC fallback. All three now reuse #4305's tested GetCurrentWindowServerLocal/ GetOverviewComparisonRange statics instead of independently resampling UTC now. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
Conflicts in CorrelatedTimelineLanesControl.xaml.cs and ServerTab.xaml.cs came from #4317's squash merge: this branch already carried #4317's commit (4723bf9, byte-identical to the squash for these files) plus #4313's edits on the same lines. Resolved to this branch's version of both files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FVjn4PBJN71NQXdFo6ZxNQ
erikdarlingdata
marked this pull request as ready for review
September 25, 2026 16:45
erikdarlingdata
enabled auto-merge (squash)
September 25, 2026 16:45
erikdarlingdata
deleted the
fix/4313-dashboard-query-grid-comparison
branch
September 25, 2026 17:07
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.
Closes #4313.
Builds on #4317 (#4305), not yet merged: this branch is
fix/4313-dashboard-query-grid-comparison,created off
origin/fix/4305-dashboard-ghost-line-offsetat 4723bf9. Its diff against that basebranch is the 4 files below; the diff against
devwill also carry #4317's commits until #4317 merges.Why
Lane U3 (#4317) fixed the Dashboard Overview ghost line building its "current window" from
DateTime.UtcNowwhile its own reads treat a supplied range as server-local. #4317's commit messageexplicitly named this issue's two spots as out of its scope:
Confirmed both named spots against the reads they feed:
ServerTab.xaml.cs'sGetComparisonRange()(_globalToDate ?? DateTime.UtcNowunder a presetrange) feeds
PerformanceTab.SetComparisonRange(...), which reachesDatabaseService.QueryPerformance.Comparison.cs'sGetQueryStatsComparisonAsync,GetProcedureStatsComparisonAsyncandGetQueryStoreComparisonAsync-- all three filter oncollection_time.QueryPerformanceContent.Comparison.cs'sRefreshComparisonAsync()(_queryStatsToDate ?? DateTime.UtcNow) calls those same three methods directly.collect.query_stats,collect.proc_statsandcollect.query_store_statsall defaultcollection_timetoSYSDATETIME()-- server-local, not UTC.On a server not running in UTC, both spots picked a current window offset from the server's actual
current time by the server's UTC offset, so the "current" side of the comparison silently included or
excluded the wrong rows.
What changes
The two named spots, both now reuse #4305's tested, testable statics
(
CorrelatedTimelineLanesControl.GetOverviewComparisonRange/GetCurrentWindowServerLocal) insteadof independently resampling a raw UTC now:
ServerTab.xaml.cs'sGetComparisonRange()now callsGetOverviewComparisonRange(...)and mapsthe result to
(From, To).QueryPerformanceContent.Comparison.cs'sRefreshComparisonAsync()now callsGetCurrentWindowServerLocal(...)for its(currentStart, currentEnd).A third spot, found while checking every other
DateTime.UtcNowused as "server now" in theDashboard's comparison and Overview code (per brief step 2), fixed in the same PR:
CorrelatedTimelineLanesControl.xaml.cs'sRefreshAsynccomputedreferenceTime(baseline-bandlookup) the same buggy way:
fromDate ?? DateTime.UtcNow.AddHours(-hoursBack). That feedsSqlServerBaselineProvider.GetBaselineAsync, which buckets byanalysisTime.Hour/.DayOfWeekagainstcollection_time(also server-local) to find the matching baseline bucket. Samebug shape, same fix: now
GetCurrentWindowServerLocal(...).Start.Swept and ruled out (grepped every other
DateTime.UtcNowindeprecated/Dashboard/*.csandControls/*.cs, read each in context):ServerTab.xaml.cs/ServerTab.Refresh.cs's_refreshStartedUtctwo-minute auto-refresh throttle,and heatmap-hover debounces in
QueryPerformanceContent.Heatmap.cs/FinOpsContent.ObjectHeatmap.cs-- pure wall-clock timers, not server reads.
MainWindow.AlertEngine.cs's alert-cooldownnow-- deliberately UTC, with a comment explainingServerTimeHelper.ServerNowwould make cooldowns jump when the offset changes between tab switches.Left alone; it is correct as written.
RecommendationsContent.xaml.cs'sOpenInActiveQueries_Clickfallback -- documented as the "RAW UTCwindow" contract; the host converts. Left alone.
ResourceMetricsContent.xaml.cs:372already passesDateTime.UtcNowas an explicit parameter intoGetOverviewComparisonRange, which converts it internally -- the Dashboard: Overview ghost-line comparison likely has the same UTC-offset bug as Lite (#4296) #4305-fixed call shape, correct asis.
RecommendationsViewModel.cs:228already does the offset math inline(
DateTime.UtcNow.AddMinutes(_utcOffsetMinutes)) -- already server-local, just not routed throughServerTimeHelper; a style-only observation, not a behavior bug, so left alone.Checked that no other caller re-resolves a stale comparison range (brief step 2's other risk, the
kind #4305 also fixed for the ghost line): grepped
RefreshComparisonAsync|SetComparisonRangeacrossdeprecated/Dashboard. The only caller of either isCompareToCombo_SelectionChanged, so the gridshave no periodic-refresh path that would reuse a resolved range across ticks; the in-place fix is
complete on its own.
Checked custom ranges are already server-local, so left untouched, per
ServerTab.TimeRange.cs(
_globalFromDate = ServerTimeHelper.DisplayTimeToServerTime(...)) andServerTab.DrillDown.cs'sOnOpenActiveQueriesForFinding(explicit// UTC -> server-local (collection_time is stored in server-local time)conversion before assigning_globalFromDate/_globalToDate).Updated two comments that #4313's fix makes false:
ServerTab.xaml.cs'sCompareToCombo_SelectionChangedcomment (previously said the grids' bug was untouched/trackedseparately as #4313), and
GetOverviewComparisonRange's doc summary inCorrelatedTimelineLanesControl.xaml.cs(previously said neither #4305 nor this method touched thegrids).
Lite parity, deferred to a follow-up issue, not fixed here:
Lite/Controls/CorrelatedTimelineLanesControl.xaml.cscarries a SYNC WARNING that it mirrors theDashboard copy of this file. After merging
origin/dev(which turned out to already carry a separate,already-merged fix for Lite's ghost-line
timeShift/currentStart), one matching call site is stillopen there: line 189's baseline
referenceTime, same shape as the third spot this PR fixes. Filed as#4320 rather than fixed here,
because it needs
Lite.Tests(not in this lane's brief), and because a nearby comment in that samefile states Lite's Queries-tab comparison reads are correctly UTC-based by design (unlike the
Dashboard's), so Lite's fix needs its own confirmation of which reads are server-local before
generalizing #4313's fix shape to it. (I initially filed #4320 citing three open call sites, read
before merging
origin/dev; that merge turned out to already fix two of them, so I corrected the issueto the one still open.)
Test plan
deprecated/Dashboard.Tests/QueryComparisonWindowOffsetTests.cs(9 tests): the grids'current window on a server east of UTC (UTC+5) and west of UTC (UTC-7), under both a preset range
(asserts the window end is the server-local now, not the raw UTC now supplied) and a custom range
(asserts supplied bounds pass through verbatim); the same for
GetComparisonRange's three dropdownoptions (Yesterday / Last week / Same day last week); and a source-pin test asserting each of the
three fixed call sites now calls the shared server-local helper and not a bare
?? DateTime.UtcNowfallback.
GetComparisonRange()to itspre-fix
_globalToDate ?? DateTime.UtcNowbody, rebuilt, ran only that test -- it failed withGetComparisonRange still falls back to a raw UTC now under a preset range (#4313)-- then restoredthe fix and reran (passes).
dotnet build deprecated/Dashboard/Dashboard.csproj -c Debug: Build succeeded, 0 Warning(s), 0Error(s).
dotnet build deprecated/Dashboard.Tests/Dashboard.Tests.csproj -c Debug: Build succeeded, 0Warning(s), 0 Error(s).
origin/dev(clean, no conflicts; it touched AG topology, Darling query-heatmap tests andLite files, none of the four files this PR touches).
Dashboard.Tests.exesuite once, after theorigin/devmerge:Total: 838, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0.Installer.Tests(excluded by lane orders) and any live/manual Dashboard run against anon-UTC SQL Server (no monitored server touched; this lane's brief is source + unit tests only).
Merge note
The PR tender merged
origin/devinto this branch (11c33dd). #4317's squash merge conflicted with this branch's copy of #4317 inCorrelatedTimelineLanesControl.xaml.csandServerTab.xaml.cs. Both conflicts resolved to this branch's version, which already held all of #4317 (byte-identical for these files) plus #4313's edits.CHANGELOG entry
SECTION: Fixed
ENTRY:
REF:
[Dashboard query comparison grids use the server's local time (#4313) #4321]: Dashboard query comparison grids use the server's local time (#4313) #4321
[Dashboard Overview ghost line uses the server's local time under a preset range (#4305) #4317]: Dashboard Overview ghost line uses the server's local time under a preset range (#4305) #4317
🤖 Generated with Claude Code
https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3