perf(net): one balance scan per poll, filtered opstatus, a guarded merge scope
The Core poll ran z_gettotalbalance twice (minconf 0 and 1), each a whole- wallet scan under dragonxd's cs_main. The spendable (minconf=1) call now runs only after the display call succeeded, and only when something is unconfirmed or pending or the exact total moved: about 95% of polls make one scan instead of two. A forced post-send balance can no longer be lost to a refused enqueue, and pending-send debits aren't re-applied each tick. z_getoperationstatus is filtered to our opids with its real argument form ([[id,...]]; the old ["id"] was rejected), falling back to the unfiltered call if a daemon refuses the filter. The Shield/Merge dialog's own polls are filtered and session-guarded too, and finally reach Done; the dead RPCClient wrappers are deleted. The Merge/Consolidate scope scan becomes a tested state machine: one scan at a time, failures latch behind a Retry button, only "node not ready" retries by itself (10/30/60 s, at most 3), Lite never scans. Its 120 s calls are maintenance calls: every main-thread disconnect aborts one in flight first, so a lifecycle action no longer freezes the UI behind it. An aborted merge that was already sent says it may still go through. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+22
-5
@@ -325,6 +325,7 @@ public:
|
||||
|
||||
void beginBackgroundStop(const char* reason) override
|
||||
{
|
||||
app_.abortMaintenanceCalls(); // a long call holds the curl lock disconnect() needs
|
||||
if (app_.rpc_ && app_.rpc_->isConnected()) app_.rpc_->disconnect();
|
||||
app_.onDisconnected(reason ? reason : "Daemon lifecycle");
|
||||
App& app = app_;
|
||||
@@ -1289,13 +1290,28 @@ void App::update()
|
||||
if (!rpc) return [this](){ opid_poll_in_progress_ = false; };
|
||||
json result;
|
||||
services::NetworkRefreshService::OperationStatusPollResult parsed;
|
||||
const auto filter = services::NetworkRefreshService::buildOperationFilterParams(opids);
|
||||
if (!filter) return [this](){ opid_poll_in_progress_ = false; };
|
||||
try {
|
||||
rpc::RPCClient::TraceScope trace("Send tab / Operation status");
|
||||
// No per-opid filter: this daemon rejects z_getoperationstatus(["opid"]) with
|
||||
// "JSON value is not an array as expected", which left completed sends stuck on
|
||||
// "Waiting for operation". The no-arg form returns ALL operations;
|
||||
// parseOperationStatusPoll() filters down to the opids we're tracking.
|
||||
result = rpc->call("z_getoperationstatus", json::array());
|
||||
// Filtered to our opids: the call takes cs_main + cs_wallet and builds a status object for
|
||||
// every op it reports, and the daemon retains finished ops, so the unfiltered form grew
|
||||
// with every send, shield and chat message. Its one argument is an ARRAY of ids,
|
||||
// [[id, ...]]; the old ["opid"] attempt failed with "JSON value is not an array as
|
||||
// expected" because params[0] was a string. If a daemon still answers the filter with an
|
||||
// error, fall back to the unfiltered form for this connection rather than wedge sends on
|
||||
// "Waiting for operation"; parseOperationStatusPoll() ignores ops we don't track.
|
||||
if (!opid_filter_unsupported_.load(std::memory_order_relaxed)) {
|
||||
try {
|
||||
result = rpc->call("z_getoperationstatus", *filter);
|
||||
} catch (const rpc::RpcError& e) {
|
||||
if (e.kind() == rpc::RpcFailureKind::Warmup) throw;
|
||||
result = rpc->call("z_getoperationstatus", json::array());
|
||||
opid_filter_unsupported_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
} else {
|
||||
result = rpc->call("z_getoperationstatus", json::array());
|
||||
}
|
||||
// Parse INSIDE the guard: a malformed/type-anomalous element must never abort the
|
||||
// poll and leave opid_poll_in_progress_ stuck true for the whole connected session.
|
||||
parsed = services::NetworkRefreshService::parseOperationStatusPoll(result, opids);
|
||||
@@ -7496,6 +7512,7 @@ void App::restartDaemon()
|
||||
|
||||
// Disconnect RPC so the loading overlay appears
|
||||
if (decision.disconnectRpc && rpc_ && rpc_->isConnected()) {
|
||||
abortMaintenanceCalls(); // a long call holds the curl lock disconnect() needs
|
||||
rpc_->disconnect();
|
||||
}
|
||||
onDisconnected("Daemon restart");
|
||||
|
||||
Reference in New Issue
Block a user