Server time display reads each connection's own offset (E5) - #607
Merged
Merged
Conversation
…wide one (E5) TimeDisplayHelper.ServerUtcOffsetMinutes was one static, written by every connect. With the display set to Server, two sessions on servers in different time zones, or one session that reconnected, moved each other's Query Store grid, slicer and History times by the wrong server's offset. The static is gone. Each connect makes a ServerUtcOffset holder, the offset fetch fills that holder (not whatever the session holds when the answer lands), and every document opened on the connection keeps it: Query Store grid and its rows, slicer and ribbon, History and its rows, and the Overview. A reconnect makes a new holder; documents already open keep the old one. The conversion now takes the offset as an argument, so the compiler found every reader. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019n3G844aTidqrD6A6iMgza
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019n3G844aTidqrD6A6iMgza
…n on a change The box opened on Local whatever the setting or another grid had chosen, so it could say Local beside times shown in Server mode. It now opens on TimeDisplayHelper.Current. Changing the mode redrew the rows and the slicer but not the wait ribbon, which kept the old mode's labels and tips until a resize. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019n3G844aTidqrD6A6iMgza
erikdarlingdata
marked this pull request as ready for review
September 28, 2026 22:24
|
Reviewed the diff. I found nothing blocking.
|
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.
Fixes review finding E5. There is no issue to close.
What changed
The Server mode of the time display used one process-wide number for the server's offset from UTC.
TimeDisplayHelper.ServerUtcOffsetMinuteswas a static, and every connect wrote to it. With the display set to Server, two sessions on servers in different time zones shifted each other's Query Store times. So did one session that reconnected to another server. The grid, the slicer, and the History times all moved by the wrong server's offset.The static is gone. Each connect now makes a small holder,
ServerUtcOffset, with oneMinutesvalue. The offset query fills that holder. Every document opened on the connection keeps a reference to it. That covers:TimeDisplayHelper.Currentstays global, because it is the user's display preference.ConvertForDisplayandFormatForDisplaynow take the offset as an argument, with no default. The compiler found every reader. One of them was the slicer's conversion from typed time back to UTC, which read the static in the other direction.QueryStoreHistoryRowis a Core model, so it cannot see app types. It got aServerUtcOffset? ServerOffsetproperty, andServerUtcOffsetlives in Core next toTimeDisplayHelper. The History control sets the property on each row after a fetch. A row without a holder reads as UTC in Server mode.Five constructors take the holder as a required argument: the grid, History, the History window, the Overview, and
QueryStoreRow. A creator cannot leave it out. The two places that built a grid now shareQuerySessionControl.NewQueryStoreGrid, so the holder is passed in one place.How long a holder lives
ShowConnectionDialogAsyncmakes a new holder (BeginServerConnection) when a connect succeeds. It does this before the first await, so a document opened while the offset query runs already has its holder.The offset query takes its connection string and its holder as arguments. If the user reconnects before the answer arrives, the answer fills the connection that asked. Before, it wrote to shared state.
A reconnect makes a new holder. Documents that are already open keep the old one, because their data came from the old server. A History opened from an old grid after a reconnect gets that grid's holder, not the session's current one. The session already throws the Overview away on a reconnect, so the next Overview gets the new holder.
If the offset arrives after a document has drawn its times, that document shows offset 0 (UTC) while Server is on. Every reader takes the value each time it formats a time, so the document shows the real offset on its next redraw. A redraw happens when the user changes the time display box, fetches again, or resizes the slicer.
I did not add a change event. A change event needs each document to unsubscribe when it closes. Without that, a closed document stays in memory as long as the session. The query starts right after the connect, and a document draws times only after several more round trips, so the gap is small.
If the offset query fails, the holder stays at 0. Before this change, a failed query on a second connect left the previous server's offset in place.
Tests
The new class
ServerUtcOffsetPerConnectionTestshas 18 cases:To check that the tests fail without the fix, I put the old behavior back. The holder's setter wrote one static, and the conversion read that static and ignored its argument. Twelve of the 18 cases failed. Six passed:
I then restored the fix and all 18 passed.
Eight existing test files got the new holder argument in their constructor calls.
Also fixed: the time display box and the wait ribbon
The last commit fixes two small display problems next to this finding.
TimeDisplayHelper.Currentwas. If the setting or another grid had chosen Server, a new grid showed Server times under a box that said Local. The box now opens on the mode in effect. Opening a grid does not change the mode.Four new cases cover these, which makes 22 in the class.
AGridsTimeDisplayBoxOpensOnTheModeInEffectruns for Local, Utc and Server.RedrawingTheRibbonAfterAModeChangeShowsTheNewModechecks a bar's tip before and after a change. Without the two fixes, 3 of the 4 fail. The Local case passes either way, because the box used to open on Local.The grid's call to the ribbon redraw has no test of its own, because the ribbon only draws inside a laid-out, expanded wait stats panel. The test calls the same method directly.
Totals
dotnet build PlanViewer.sln -c Debugreports 0 warnings and 0 errors.Not done
🤖 Generated with Claude Code
https://claude.ai/code/session_019n3G844aTidqrD6A6iMgza