fix(sync): stop large-wallet balance polling from starving block connection

On a fully-shielded (ac_private=1) chain, z_gettotalbalance is O(mapWallet) and
holds the daemon's cs_main for its whole duration — ~20s on a ~5k-tx wallet. The
Overview refresh polled it every ~2s (twice: minconf 0 and 1), so cs_main was
held almost continuously, starving the single block-connection thread: the node
connected blocks only in the gaps between polls and could fall further behind
the tip than it caught up (observed live: gap growing 58→100 blocks while the
GUI was open, one core pegged on GetFilteredNotes, 22 idle, ~17 B/s download).

Two hardening changes on top of the existing "skip balance while syncing" guard:

- Hysteresis: keep the low-impact sync profile (and balance suppression) for a
  short settle window after catching up, so a large-wallet scan can't
  immediately re-starve connection and bounce the node back into syncing. Armed
  only on the syncing→caught-up edge, so a wallet synced from the start is never
  throttled at connect (effectivelySyncing()).

- Adaptive balance cadence: time each z_gettotalbalance scan and require the
  next poll to wait at least (cost / 10%), so balance scanning never occupies
  more than ~10% of wall-clock. Cheap wallets are unaffected (the tab's Core
  timer stays the cadence); a ~20s scan backs off to ~200s. Wallet mutations
  (send/shield) force the next poll through so the user's own action updates the
  balance immediately (balanceRefreshDue()).

getblockchaininfo keeps its normal cadence throughout, so sync progress stays
live. Build + test_phase4 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 22:45:51 -05:00
co-authored by Claude Opus 4.8
parent 08cfeb0e08
commit 870793433b
5 changed files with 76 additions and 7 deletions
+3 -1
View File
@@ -919,7 +919,9 @@ void App::update()
// Re-apply the refresh cadence when sync starts/finishes: while syncing we throttle polling to
// a low-impact profile so RPC contention doesn't slow block download (see applyRefreshPolicy).
if (state_.sync.syncing != refresh_policy_syncing_) {
// effectivelySyncing() includes the post-sync settle window, so this also reverts to the normal
// per-tab cadence once that window elapses.
if (effectivelySyncing() != refresh_policy_syncing_) {
applyRefreshPolicy(current_page_);
}