perf(nodes): Connected Nodes refresh — parallel probes, instant list, 12s health timeout

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 <noreply@anthropic.com>
This commit is contained in:
archipelago 2026-07-22 19:44:31 -04:00
parent 8f4c32b93f
commit 770e3386f4
3 changed files with 27 additions and 15 deletions

View File

@ -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
{

View File

@ -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,
})
}

View File

@ -379,20 +379,28 @@ async function loadPeers() {
peers.value = peerList
observers.value = observerList
for (const p of [...peers.value, ...observers.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)
if (!hadPeers) {