fix(desktop): quiesce renderer polling while hidden (#3677) - #5490
Conversation
Pause nonessential renderer timers and query polling when the document is hidden or unfocused, then refresh promptly when it returns. Preserve relay watchdog and presence heartbeat activity. Co-authored-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
|
Reviewing on Wes's behalf. The timer quiescing is well-targeted overall, but the reminder detector cannot be tied to document focus. That changes a user-facing reliability contract rather than merely removing background work.
Please keep the reminder due-time mechanism active while backgrounded, or move it to a background-capable/native scheduler that preserves on-time delivery. If CPU is the concern, a single 30-second reminder timer is not equivalent to cosmetic tickers/query backstops and should be among the deliberately ungated semantics (similar to presence/watchdog). Please also add coverage for a reminder crossing |
Separate true document visibility from app focus so visible UI clocks stay live while network polls still quiesce on blur. Keep reminder delivery and huddle pipeline hot-start active in the background, and make query focus setup idempotent. Co-authored-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Suppress the focus-return refetch stampede introduced by #5490: raise staleTime to 5 minutes on the two expensive focus-refetch families (channels get_channels, home-feed) so a focus return within that window serves cache instead of refetching, while the focused 60s/30s polling cadence and #5490 blur quiescence are unchanged. Focus returns with genuinely old data still refetch. Co-authored-by: Meeseeks <2e96988f190ed1bd3c568760103aa4cadb2bc6195b832e252c984392c89039bd@buzz.block.builderlab.xyz> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
…overy * origin/main: fix(link-preview): reliably render previews sent right after they resolve (#5245) fix(link-preview): restore Buzz entity link cards (#5494) chore(release): release Buzz Desktop version 0.5.9 (#5521) feat(cli): add --visibility flag to channels update (#5119) Polish desktop onboarding flow (#5310) fix(desktop): quiesce renderer polling while hidden (#3677) (#5490) fix(channels): restore member invitations to private channels (#5493) perf(ci): experiment with sccache for relay builds (#5224) fix(desktop): bound nine unbounded localStorage stores (#5454) feat(desktop): time-based sweep for stale localStorage caches (#5453) ci(release): gate OSS desktop auto-update promotion (#5398) fix(release): pin desktop PR operations to block/buzz (#5212) fix(search): surface exact short profile names (#5480) Reduce repeated ACP session context (#5423) feat(desktop): NIP-AM agent-usage backend — P2 emission/transport/archive + P4a aggregation/D6 (#4000) fix(desktop): resolve overlapping member mentions (#5225) Signed-off-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz> Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com>
Suppress the focus-return refetch stampede introduced by #5490: raise staleTime to 5 minutes on the two expensive focus-refetch families (channels get_channels, home-feed) so a focus return within that window serves cache instead of refetching, while the focused 60s/30s polling cadence and #5490 blur quiescence are unchanged. Focus returns with genuinely old data still refetch. Co-authored-by: Meeseeks <2e96988f190ed1bd3c568760103aa4cadb2bc6195b832e252c984392c89039bd@buzz.block.builderlab.xyz> Co-authored-by: Wes <wesbillman@users.noreply.github.com> Signed-off-by: Wes <wesbillman@users.noreply.github.com>
…home-feed (#5535) Fixes the 0.5.9 sluggishness Wes reported in app-slowness-mac (channel list slow, content slow). ## Problem #5490 (shipped in 0.5.9) flipped ~20 query sites to `refetchOnWindowFocus: true` and wired TanStack focusManager to app focus. Instrumented at that exact commit: regaining focus after >60s away fires **7 query fetches within 2ms**, including `get_channels`, which settles at **~3.6s** (production probe: median 3.2s at 1,133 channels — 8 serial round-trips, 1,133-filter last-message batch). In 0.5.8 this burst was zero by configuration. Net: a burst of fetch/parse contention exactly when the user returns to the app. Relay ruled out: v0.2.1 small reads are 2–4ms upstream; nothing in v0.2.0..v0.2.1 degrades the query path. The O(N) `get_channels` design is a pre-existing issue (June analysis) — this PR fixes the new stampede that made it user-visible. ## Fix Raise `staleTime` to 5 minutes on the two expensive focus-refetch families — `channels` and `home-feed` — so a focus return inside that window serves cache instead of refetching. `refetchOnWindowFocus: true` only refetches stale queries, so genuinely old data still refreshes on return. Unchanged: focused polling cadence (60s channels / 30s home-feed; interval refetches ignore staleTime), #5490 blur quiescence (no changes to `useDocumentVisible.ts`/`queryClient.ts`), all push-style invalidation paths (`invalidateQueries` bypasses staleTime), and channels cold-start revalidate (`initialDataUpdatedAt: 0`). ## Validation - New regression test `desktop/src/features/home/focusRefetchPolicy.test.mjs` (4/4): fresh focus return → 0 fetches; stale → 1; polling constants locked. - Pre-push gate at the reviewed tree: desktop-check, desktop-typecheck, full desktop-test **4588/4588**. - Independent adversarial review (Beth): APPROVE at tree `4e2546ec` — verified fresh-skip/stale-refetch against query-core 5.100.14 source, polling-cadence via browser-simulated probe, side-effect sweep of all invalidation paths clean. Sole CHANGE was commit trailers, fixed by amend (tree unchanged). ## Known residual Focus returns after >5min still fire the full burst including the ~3.2–3.6s `get_channels`. This cuts stampede frequency, not magnitude — the O(N) `get_channels` relay path (RESEARCH/GET_CHANNELS_SLOWNESS.md) is the follow-up that fixes magnitude. Diagnosis: Summer (focus profiling) + Morty (relay probe); implemented by Meeseeks; reviewed by Beth; integrated by Rick. Thread: app-slowness-mac e78fad29380d9a0974c9d673910450994a228781ddce133a8cedbd90504d95be. Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Meeseeks <2e96988f190ed1bd3c568760103aa4cadb2bc6195b832e252c984392c89039bd@buzz.block.builderlab.xyz>
Bring in main's runtime.rs mesh acp_model wire translation so local checks and CI both run on the merged tree. Clean auto-merge; the PR's fail-closed spawn gating and main's model translation touch disjoint regions of spawn_agent_child. * origin/main: (24 commits) Improve desktop search scoping (#5306) Add glass appearance and cohesive settings (#5478) Add Send to channel for thread messages (#5305) Fix macOS attachment picker lifecycle and allow inert HTML downloads (#5569) fix(desktop): preserve fresh channel timelines (#5577) fix(desktop): suppress fresh focus-return refetches for channels and home-feed (#5535) chore: mesh upgrade, clean up legacy special case code, simplify model selection for mesh (#5289) fix(desktop): preserve theme when opening communities (#5266) fix(link-preview): resolve YouTube videos through oEmbed (#5520) fix(buzz-agent): harden Databricks OAuth token cache and callback (#5534) fix(link-preview): reliably render previews sent right after they resolve (#5245) fix(link-preview): restore Buzz entity link cards (#5494) chore(release): release Buzz Desktop version 0.5.9 (#5521) feat(cli): add --visibility flag to channels update (#5119) Polish desktop onboarding flow (#5310) fix(desktop): quiesce renderer polling while hidden (#3677) (#5490) fix(channels): restore member invitations to private channels (#5493) perf(ci): experiment with sccache for relay builds (#5224) fix(desktop): bound nine unbounded localStorage stores (#5454) feat(desktop): time-based sweep for stale localStorage caches (#5453) ... Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz> Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Fixes #3677.
Problem
The renderer never quiesces: recurring timers, query polling, and re-render tickers run at full rate whether the window is visible, hidden, or minimized. Measured on a live installed app: 27.5% mean renderer CPU visible vs 28.7% hidden (60×1s
pssamples of the WebContent process;sample(1)dominated byWebCore::timerFired/ThreadTimers, microtask checkpoints, JSON parsing, style matching). Matches all three reproductions in #3677 (macOS prerelease, Linux/WebKitGTK A/B/A minimize test, stable macOS).Per-timer instrumentation (dev build, wrapped
setInterval/setTimeout/rAF) attributed the recurring work:useNow60 fires/min, 40 active TanStack refetch intervals, agent-turn pruning 12/min, auto-restart ticks, huddle/reminder polls — none visibility-gated.Fix (two-tier gating, standard mechanisms only)
Two separate signals in
desktop/src/shared/lib/useDocumentVisible.ts, because they mean different things and (see residuals) are delivered differently on macOS:useDocumentVisible— true Page Visibility only (document.visibilityState). Gates local UI work that must keep running on a visible-but-unfocused window:useNowrelative clocks, agent-turn pruning, huddle bar state/model-status polling, auto-restart tick. Hidden ⇒ paused;useNowsnaps to freshDate.now()on return.useAppFocused— visible ANDdocument.hasFocus(). Gates network refetch polling only (useFocusedRefetchInterval, ~15 query families: forum/home/agents/channels/templates/emoji/user-status/projects/workflows/persona-catalog/pulse/presence-list). TanStack'sfocusManageris wired to this signal (idempotent, single install) withrefetchOnWindowFocus: true, so stale queries refresh promptly on return. Deliberate side effect, documented in code: query retries pause on blur; mutations and the presence heartbeat (retry: 0) are unaffected.reminderNotificationPoll.tswith regression test), huddle pipeline hot-start (check_pipeline_hotstartsurvives backgrounding for the duration of a huddle), relay stall watchdog, presence heartbeat. Live WebSocket delivery untouched throughout.Validation
useNow 60 / prune 12 / watchdog 6 / query 4 / auto-restart 4 / low-rate huddle/reminder/presence); visible+blurred: query polls 0, UI clocks continue (useNow 60 / prune 12), reminders 2, presence live; truly hidden: only watchdog 6, reminders 2, presence ~2 — everything else 0. Return restored visible+focused, selection preserved, queries refreshed.visibilityState === "hidden"— they reliably produced focus loss. The CPU-dominant quiescence path on macOS is therefore the focus gate; the visibility gate is exercised fully on platforms that report hidden (e.g. WebKitGTK minimize per the Linux repro).useNowhidden-pause + fresh-snap on return, focus-gated interval pause/resume-with-refresh, reminder delivery while hidden+unfocused (5 new, plus primitive wiring tests).1237548d1.Known residuals
hiddenon app-hide/minimize, hidden-only consumers (useNow, prune, huddle UI polls) may keep ticking on macOS when the app is hidden. These are cheap local timers; the expensive network polling still quiesces via focus loss, which is what the measured 28% CPU was attributed to. If the residual local-timer cost proves measurable, the follow-up is bridging Tauri window hidden/minimized events into the visibility signal.