From 770e3386f4e67b1ca755921f70efc9282de4c37c Mon Sep 17 00:00:00 2001 From: archipelago Date: Wed, 22 Jul 2026 19:44:31 -0400 Subject: [PATCH] =?UTF-8?q?perf(nodes):=20Connected=20Nodes=20refresh=20?= =?UTF-8?q?=E2=80=94=20parallel=20probes,=20instant=20list,=2012s=20health?= =?UTF-8?q?=20timeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reachability probes ran one at a time with a 30s Tor timeout each, so Refresh sat disabled for N-offline-peers × 30s. Now the list renders and the button re-enables immediately; probes run concurrently and fill the dots in as they land. Co-Authored-By: Claude Fable 5 --- core/archipelago/src/node_message.rs | 5 ++- neode-ui/src/api/rpc-client.ts | 3 +- .../src/views/web5/Web5ConnectedNodes.vue | 34 ++++++++++++------- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/core/archipelago/src/node_message.rs b/core/archipelago/src/node_message.rs index 1b5feeda..c08df91e 100644 --- a/core/archipelago/src/node_message.rs +++ b/core/archipelago/src/node_message.rs @@ -406,7 +406,10 @@ pub async fn check_peer_reachable(onion: &str, fips_npub: Option<&str>) -> Resul validate_onion(onion)?; match crate::fips::dial::PeerRequest::new(fips_npub, onion, "/health") .service(crate::settings::transport::PeerService::Messaging) - .timeout(std::time::Duration::from_secs(30)) + // 12s, not 30s: this is a liveness dot, not a data transfer. A Tor + // circuit that hasn't answered /health in 12s is "offline" for UI + // purposes; the old 30s made the Connected Nodes probes crawl. + .timeout(std::time::Duration::from_secs(12)) .send_get() .await { diff --git a/neode-ui/src/api/rpc-client.ts b/neode-ui/src/api/rpc-client.ts index 13d5b6b9..ee4b553d 100644 --- a/neode-ui/src/api/rpc-client.ts +++ b/neode-ui/src/api/rpc-client.ts @@ -447,7 +447,8 @@ class RPCClient { return this.call({ method: 'node-check-peer', params: { onion }, - timeout: 35000, + // Backend health-dial timeout is 12s — 15s here covers RPC overhead. + timeout: 15000, }) } diff --git a/neode-ui/src/views/web5/Web5ConnectedNodes.vue b/neode-ui/src/views/web5/Web5ConnectedNodes.vue index 9d4db73d..bb57e5aa 100644 --- a/neode-ui/src/views/web5/Web5ConnectedNodes.vue +++ b/neode-ui/src/views/web5/Web5ConnectedNodes.vue @@ -379,19 +379,27 @@ async function loadPeers() { peers.value = peerList observers.value = observerList - for (const p of [...peers.value, ...observers.value]) { - try { - const check = await rpcClient.checkPeerReachable(p.onion) - peerReachableLocal.value[p.onion] = check.reachable - } catch { - peerReachableLocal.value[p.onion] = false - } - } - writeConnectedNodesCache({ - peers: peers.value, - observers: observers.value, - peerReachable: peerReachableLocal.value, - connectionRequests: connectionRequests.value, + // The list is ready — render it and re-enable Refresh NOW. The + // reachability dots fill in as probes resolve. Before 2026-07-22 the + // probes ran ONE AT A TIME with a 30s Tor timeout each, so Refresh sat + // disabled for N-offline-peers × 30s ("takes ages"). + loadingPeers.value = false + void Promise.allSettled( + [...peers.value, ...observers.value].map(async (p) => { + try { + const check = await rpcClient.checkPeerReachable(p.onion) + peerReachableLocal.value[p.onion] = check.reachable + } catch { + peerReachableLocal.value[p.onion] = false + } + }), + ).then(() => { + writeConnectedNodesCache({ + peers: peers.value, + observers: observers.value, + peerReachable: peerReachableLocal.value, + connectionRequests: connectionRequests.value, + }) }) } catch (e) { if (import.meta.env.DEV) console.error('Failed to load peers:', e)