Repository navigation
WPF Availability Groups tab rebuilds every card on each 30 s refresh: 10 k realized elements (42 AGs, no virtualization) stall the UI thread ~2.5 s per tick #4238
Copy link
Copy link
Closed
Labels
client-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.Owned by the client-site agents (other laptop). Local sessions never pick these up.enhancementNew feature or requestNew feature or request
Description
Problem
The WPF Viewer's Availability Groups tab rebuilds its whole visual tree on every refresh, on the UI thread.
AvailabilityGroupsTab.Renderassigns a new list:AgCards.ItemsSource = groups.AgCardsis a plainItemsControlinside aScrollViewer. It doesn't virtualize, so every card is realized whether or not it's on screen.- Each card holds a replica
WrapPaneland a per-databaseDataGrid, capped atMaxHeight="260", which shows up to about 10 rows. - The shell's fleet timer (
NocRefreshIntervalSeconds, 30 s by default) callsRefreshAgAsyncwhile the tab is visible. So every tick tears down and rebuilds every card and every DataGrid, even when the topology hasn't changed, which is the normal case.
The store read is #4228. This issue covers what the viewer does with the rows once it has them.
Measured
Production SQL Server store A, 2026-09-25. The Viewer ran on the store host in an interactive session and was driven through UI Automation.
- The topology is 42 AGs, 84 replicas, 398 database rows.
- UI-thread responsiveness was probed with
SendMessageTimeout(WM_NULL)once a second.
| measure | AG tab visible | other main tabs |
|---|---|---|
| UI elements realized | 10,353 | 19–1,032 |
| longest UI-thread stall | 2,548 ms | ≤ 47 ms (every main tab and all 19 per-server tabs, ≤ 85 ms) |
| Viewer CPU per refresh | ~3.5–3.9 s (bursts at 1–3 s, 17–19 s and 44–45 s of a 45 s sample) | 0 at idle |
- When the tab is left open, the Viewer stops responding for about 2.5 s every 30 s. Clicks, scrolling and typing all queue behind the rebuild.
- It's the only surface in the Viewer that stalls the UI thread measurably.
- A settle-detection pass that walked the full automation tree never saw it settle within 90 s. That pass cost 100 s of CPU and grew the working set from 249 MB to 771 MB. Some of that is the walk itself, since realizing automation peers for 10 k elements is expensive. Screen readers and UI test tools pay the same cost.
Where (origin/dev)
Darling/PerformanceMonitor.Darling.Viewer/AvailabilityGroupsTab.xaml:CardScrollerScrollViewer~:41 and theAgCardsItemsControl~:42 (no virtualizing panel);- the replica
ItemsControl/WrapPanel~:70; - the per-database
DataGrid~:106.
Darling/PerformanceMonitor.Darling.Viewer/AvailabilityGroupsTab.xaml.cs:LoadAsync~:56 andRender~:80 (AgCards.ItemsSource = groups).Darling/PerformanceMonitor.Darling.Viewer/MainWindow.xaml.cs: the timer pathLoadVisibleTabAsyncintoRefreshAgAsync~:1104.
Fix shape
- Don't rebuild when nothing changed. Keep an
ObservableCollection<AgTopologyCard>keyed by AG identity and update each card's replica and database values in place. Add or remove cards only when the topology changes. At minimum, compare a digest of the new rows with the previous render and skipRenderwhen they match. - Virtualize the card list. Replace the
ItemsControl+ScrollViewerwith aListBoxorItemsControlusingVirtualizingStackPanel(VirtualizingPanel.IsVirtualizing="True",VirtualizationMode="Recycling",ScrollUnit="Pixel") andScrollViewer.CanContentScroll="True", so only cards on screen are realized. - Optionally, collapse healthy cards by default: show a one-line summary and expand to the DataGrid on demand. Only unhealthy AGs need the per-database grid open.
Pins:
- a Viewer test that two refreshes with identical rows leave the realized card containers as the same instances (no rebuild);
- a XAML pin that
AgCardsuses a virtualizing panel; - a rig timing with a 40+ AG seeded topology asserting the refresh's UI-thread stall stays under 100 ms.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
client-siteOwned by the client-site agents (other laptop). Local sessions never pick these up.Owned by the client-site agents (other laptop). Local sessions never pick these up.enhancementNew feature or requestNew feature or request
Activity
erikdarlingdata commentedon Sep 25, 2026
Ruling for the fix lane:
Render.VirtualizingStackPanelwith recycling and pixel scrolling, andScrollViewer.CanContentScroll="True".AgCardsuses a virtualizing panel;🤖 Generated with Claude Code
https://claude.ai/code/session_01TszxYhJJbTEh4LrZ56NYo3
erikdarlingdata commentedon Sep 25, 2026
Fixed by #4308, merged to dev (c88fa48).
AG tab render budget: replace the flaky 100ms wall-clock bar with a w…
AG tab refreshes cards in place and virtualizes the list (erikdarling…