Perf: move Lite background pipeline + full refresh off the UI thread (P1)#1110
Merged
Conversation
…(P1) Root cause (verified against the shipped DuckDB.NET.Data.dll: no async method overrides; Lite has 0 ConfigureAwait(false)): every "await ...Async()" DuckDB call completes synchronously on the calling thread, so all Lite data work runs on the WPF dispatcher. - Start CollectionBackgroundService off the UI thread (Task.Run around StartAsync). Pool threads have no SynchronizationContext, so the entire collection / checkpoint / archive pipeline now stays off the dispatcher — kills the per-minute collection jank and the multi-second-to-minutes freeze on archive/reset. Verified safe: the pipeline only touches DuckDB + the email/webhook AnalysisNotificationService; the UI reads data by polling DuckDB on its own timers, fully decoupled. - RefreshAllTabsAsync (first load / manual refresh / time-range change): wrap each of the ~36 queries in Task.Run so the fan-out runs on pool threads instead of serially on the dispatcher. Each query's ReaderWriterLockSlim read lock is acquired AND released inside one synchronous run, so thread affinity holds. Bonus: the WhenAll fan-out is now genuinely parallel. UI-update code after the awaits unchanged. Build: Lite builds clean (net10). Lite.Tests 428/429 (the 1 failure is a known flake — CredentialProfileTests touch the machine-global Windows Credential Manager and pass 13/13 in isolation; unrelated to these edits). Still to come on this effort (same Task.Run-wrap pattern): the 60s-timer sub-tab refreshes, picker charts, status timer, drill-down, slicer, grid-selection, connect; then a bounded read lock. Threading change — please validate on a running Lite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
First increment of the UI-responsiveness work (P1 of the perf effort), focused on the CRITICAL Lite finding: DuckDB.NET is synchronous (verified — the shipped DLL has no async overrides) and Lite has zero
ConfigureAwait(false), so all Lite DuckDB work — UI queries and the background collect/checkpoint/archive pipeline — runs on the WPF dispatcher thread.In this PR
Task.RunaroundCollectionBackgroundService.StartAsync. Pool threads have noSynchronizationContext, so the whole collection/checkpoint/archive pipeline stays off the dispatcher. Kills the per-minute collection jank and the multi-second-to-minutes freeze on archive/reset. Verified safe: the pipeline only touches DuckDB + the email/webhook notification service; the UI polls DuckDB on its own timers (decoupled).RefreshAllTabsAsyncoff the UI thread — the first-load / manual-refresh / time-range-change path wraps each of ~36 queries inTask.Run(correctReaderWriterLockSlimaffinity: each query acquires+releases its read lock inside one synchronous run). Bonus: the fan-out is now genuinely parallel.Still on this effort (same pattern, not deferred)
The 60s-timer sub-tab refreshes, picker charts, status timer, drill-down, slicer, grid-selection, connect; then a bounded read lock. Then the shared-UI crosshair/hover hot path, the Dashboard timer/throttle/SQL-aggregation chunk, and the leak cluster — both apps.
Validation
Lite.Tests428/429 — the 1 failure (CredentialProfileTests.SwitchToProfile_DeletesOrphanedPerServerSecret) is a known flake: those tests hit the machine-global Windows Credential Manager and pass 13/13 in isolation; unrelated to these edits.🤖 Generated with Claude Code