perf(node): throttle RPC polling during sync so block download isn't slowed
The full-node wallet polled the daemon at the per-tab cadence regardless of sync state.
On the Peers/Network tab that meant getpeerinfo every 5s + core every 5s + a full
transaction scan on every new block — and blocks arrive fast during sync. Each of those
calls takes the daemon's cs_main lock, the same lock block connection needs, so the node
synced noticeably slower than on the lightweight Console tab (core 10s, no peer polling).
Make the refresh cadence sync-aware:
- RefreshScheduler::kSyncProfile {core 10s, transactions/addresses/peers disabled} is applied
to ALL tabs while state_.sync.syncing, and reverts to the per-tab profile when sync ends.
applyRefreshPolicy() picks the profile; update() re-applies it on the syncing<->synced
transition. This suppresses getpeerinfo and the per-block tx scan during sync (that data is
incomplete mid-sync anyway) — every tab now syncs as fast as Console.
- collectCoreRefreshResult(rpc, includeBalance): skip z_gettotalbalance (wallet lock + cs_main)
while syncing; only getblockchaininfo runs, which is also what drives sync-progress detection.
applyCoreRefreshResult already leaves the balance untouched when balanceOk is false.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -569,6 +569,12 @@ void App::update()
|
||||
// Wipe a secret (seed/private key) from the clipboard once its auto-clear delay elapses.
|
||||
pumpSecretClipboardClear();
|
||||
|
||||
// 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_) {
|
||||
applyRefreshPolicy(current_page_);
|
||||
}
|
||||
|
||||
// Full-node RPC refreshes gate on ACTUAL RPC connectivity, not state_.connected. In lite
|
||||
// builds state_.connected is the lite-wallet "online" proxy (true when a wallet is open, to
|
||||
// enable the wallet UI), but there is no RPC daemon — so RPC polls (mining/balance/peers/txs)
|
||||
|
||||
@@ -519,6 +519,7 @@ private:
|
||||
int daemon_wait_attempts_ = 0;
|
||||
bool daemon_start_error_shown_ = false;
|
||||
int daemon_last_seen_crashes_ = 0; // surface each new embedded-daemon crash reason once
|
||||
bool refresh_policy_syncing_ = false; // whether the sync-throttle refresh profile is active
|
||||
// Auto-clear for secrets copied to the clipboard. Only a hash of the copied secret is kept.
|
||||
std::uint64_t clipboard_secret_hash_ = 0;
|
||||
double clipboard_clear_deadline_ = 0.0;
|
||||
|
||||
@@ -664,7 +664,14 @@ App::RefreshIntervals App::getIntervalsForPage(ui::NavPage page)
|
||||
|
||||
void App::applyRefreshPolicy(ui::NavPage page)
|
||||
{
|
||||
network_refresh_.setIntervals(getIntervalsForPage(page));
|
||||
// While the daemon is syncing, override the per-tab cadence with the low-impact sync profile so
|
||||
// the wallet stops contending for the daemon's cs_main lock (frequent getpeerinfo / per-block
|
||||
// transaction scans / balance polls slow block connection). This makes every tab sync as fast
|
||||
// as the Console tab does today. Reverts to the per-tab profile once sync finishes.
|
||||
refresh_policy_syncing_ = state_.sync.syncing;
|
||||
network_refresh_.setIntervals(refresh_policy_syncing_
|
||||
? services::RefreshScheduler::kSyncProfile
|
||||
: getIntervalsForPage(page));
|
||||
}
|
||||
|
||||
bool App::currentPageNeedsWalletDataRefresh() const
|
||||
@@ -1159,10 +1166,13 @@ void App::refreshCoreData()
|
||||
? fast_rpc_.get() : rpc_.get();
|
||||
if (!w || !rpc) return;
|
||||
ui::NavPage tracePage = current_page_;
|
||||
// Skip the balance call while syncing (it's incomplete anyway and takes the wallet lock +
|
||||
// cs_main). Captured on the main thread to avoid reading state_ off the worker thread.
|
||||
const bool includeBalance = !state_.sync.syncing;
|
||||
|
||||
auto enqueued = network_refresh_.enqueue(services::NetworkRefreshService::Job::Core, *w, [this, rpc, tracePage]() -> rpc::RPCWorker::MainCb {
|
||||
auto enqueued = network_refresh_.enqueue(services::NetworkRefreshService::Job::Core, *w, [this, rpc, tracePage, includeBalance]() -> rpc::RPCWorker::MainCb {
|
||||
AppRefreshRpcGateway refreshRpc(*rpc, traceSource(tracePage, "Core refresh"));
|
||||
auto result = NetworkRefreshService::collectCoreRefreshResult(refreshRpc);
|
||||
auto result = NetworkRefreshService::collectCoreRefreshResult(refreshRpc, includeBalance);
|
||||
return [this, result]() {
|
||||
try {
|
||||
NetworkRefreshService::applyCoreRefreshResult(state_, result, std::time(nullptr));
|
||||
|
||||
@@ -284,18 +284,20 @@ NetworkRefreshService::CoreRefreshResult NetworkRefreshService::parseCoreRefresh
|
||||
return result;
|
||||
}
|
||||
|
||||
NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefreshResult(RefreshRpcGateway& rpc)
|
||||
NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefreshResult(RefreshRpcGateway& rpc, bool includeBalance)
|
||||
{
|
||||
json totalBalance;
|
||||
json blockInfo;
|
||||
bool balanceOk = false;
|
||||
bool blockOk = false;
|
||||
|
||||
try {
|
||||
totalBalance = rpc.call("z_gettotalbalance", json::array());
|
||||
balanceOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Balance error: %s\n", e.what());
|
||||
if (includeBalance) {
|
||||
try {
|
||||
totalBalance = rpc.call("z_gettotalbalance", json::array());
|
||||
balanceOk = true;
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_LOGF("Balance error: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -230,7 +230,10 @@ public:
|
||||
bool balanceOk,
|
||||
const nlohmann::json& blockInfo,
|
||||
bool blockOk);
|
||||
static CoreRefreshResult collectCoreRefreshResult(RefreshRpcGateway& rpc);
|
||||
// includeBalance=false skips z_gettotalbalance (which takes the wallet lock + cs_main) and only
|
||||
// fetches getblockchaininfo — used while syncing, where the balance is incomplete anyway and the
|
||||
// wallet should minimise lock contention with block connection.
|
||||
static CoreRefreshResult collectCoreRefreshResult(RefreshRpcGateway& rpc, bool includeBalance = true);
|
||||
static MiningRefreshResult parseMiningRefreshResult(const nlohmann::json& miningInfo,
|
||||
bool miningOk,
|
||||
const nlohmann::json& localHashrate,
|
||||
|
||||
@@ -34,6 +34,14 @@ public:
|
||||
static constexpr float kTxMaxAge = 15.0f;
|
||||
static constexpr float kOpidPoll = 2.0f;
|
||||
|
||||
// Low-impact polling profile applied while the daemon is SYNCING, regardless of the active tab.
|
||||
// Only a slow progress poll runs (core, 10s); transactions/addresses/peers are disabled (0).
|
||||
// Frequent getpeerinfo, per-block transaction scans, and balance polls all contend for the
|
||||
// daemon's cs_main lock and measurably slow block connection during sync — this is exactly why
|
||||
// the lightweight Console tab syncs faster than the Peers tab. Reverts to the per-tab profile
|
||||
// once sync completes. (Tx/address/balance data is incomplete mid-sync anyway.)
|
||||
static constexpr Intervals kSyncProfile{10.0f, 0.0f, 0.0f, 0.0f};
|
||||
|
||||
static Intervals intervalsForPage(ui::NavPage page);
|
||||
|
||||
void applyPage(ui::NavPage page);
|
||||
|
||||
Reference in New Issue
Block a user