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.
|
// Wipe a secret (seed/private key) from the clipboard once its auto-clear delay elapses.
|
||||||
pumpSecretClipboardClear();
|
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
|
// 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
|
// 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)
|
// 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;
|
int daemon_wait_attempts_ = 0;
|
||||||
bool daemon_start_error_shown_ = false;
|
bool daemon_start_error_shown_ = false;
|
||||||
int daemon_last_seen_crashes_ = 0; // surface each new embedded-daemon crash reason once
|
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.
|
// Auto-clear for secrets copied to the clipboard. Only a hash of the copied secret is kept.
|
||||||
std::uint64_t clipboard_secret_hash_ = 0;
|
std::uint64_t clipboard_secret_hash_ = 0;
|
||||||
double clipboard_clear_deadline_ = 0.0;
|
double clipboard_clear_deadline_ = 0.0;
|
||||||
|
|||||||
@@ -664,7 +664,14 @@ App::RefreshIntervals App::getIntervalsForPage(ui::NavPage page)
|
|||||||
|
|
||||||
void App::applyRefreshPolicy(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
|
bool App::currentPageNeedsWalletDataRefresh() const
|
||||||
@@ -1159,10 +1166,13 @@ void App::refreshCoreData()
|
|||||||
? fast_rpc_.get() : rpc_.get();
|
? fast_rpc_.get() : rpc_.get();
|
||||||
if (!w || !rpc) return;
|
if (!w || !rpc) return;
|
||||||
ui::NavPage tracePage = current_page_;
|
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"));
|
AppRefreshRpcGateway refreshRpc(*rpc, traceSource(tracePage, "Core refresh"));
|
||||||
auto result = NetworkRefreshService::collectCoreRefreshResult(refreshRpc);
|
auto result = NetworkRefreshService::collectCoreRefreshResult(refreshRpc, includeBalance);
|
||||||
return [this, result]() {
|
return [this, result]() {
|
||||||
try {
|
try {
|
||||||
NetworkRefreshService::applyCoreRefreshResult(state_, result, std::time(nullptr));
|
NetworkRefreshService::applyCoreRefreshResult(state_, result, std::time(nullptr));
|
||||||
|
|||||||
@@ -284,18 +284,20 @@ NetworkRefreshService::CoreRefreshResult NetworkRefreshService::parseCoreRefresh
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefreshResult(RefreshRpcGateway& rpc)
|
NetworkRefreshService::CoreRefreshResult NetworkRefreshService::collectCoreRefreshResult(RefreshRpcGateway& rpc, bool includeBalance)
|
||||||
{
|
{
|
||||||
json totalBalance;
|
json totalBalance;
|
||||||
json blockInfo;
|
json blockInfo;
|
||||||
bool balanceOk = false;
|
bool balanceOk = false;
|
||||||
bool blockOk = false;
|
bool blockOk = false;
|
||||||
|
|
||||||
try {
|
if (includeBalance) {
|
||||||
totalBalance = rpc.call("z_gettotalbalance", json::array());
|
try {
|
||||||
balanceOk = true;
|
totalBalance = rpc.call("z_gettotalbalance", json::array());
|
||||||
} catch (const std::exception& e) {
|
balanceOk = true;
|
||||||
DEBUG_LOGF("Balance error: %s\n", e.what());
|
} catch (const std::exception& e) {
|
||||||
|
DEBUG_LOGF("Balance error: %s\n", e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -230,7 +230,10 @@ public:
|
|||||||
bool balanceOk,
|
bool balanceOk,
|
||||||
const nlohmann::json& blockInfo,
|
const nlohmann::json& blockInfo,
|
||||||
bool blockOk);
|
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,
|
static MiningRefreshResult parseMiningRefreshResult(const nlohmann::json& miningInfo,
|
||||||
bool miningOk,
|
bool miningOk,
|
||||||
const nlohmann::json& localHashrate,
|
const nlohmann::json& localHashrate,
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ public:
|
|||||||
static constexpr float kTxMaxAge = 15.0f;
|
static constexpr float kTxMaxAge = 15.0f;
|
||||||
static constexpr float kOpidPoll = 2.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);
|
static Intervals intervalsForPage(ui::NavPage page);
|
||||||
|
|
||||||
void applyPage(ui::NavPage page);
|
void applyPage(ui::NavPage page);
|
||||||
|
|||||||
Reference in New Issue
Block a user