From 939565b88ad52da70838618911aba68d5092b543 Mon Sep 17 00:00:00 2001 From: syoul Date: Sun, 26 Apr 2026 00:32:58 +0200 Subject: [PATCH] fix(poller): short-poll inbox instead of 30s long-poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 10s-after-healthy failure pattern was reproducing even with the connection pool disabled. Smoking gun: the inbox loop opens GET /messages?timeout=30 right after start_daemon returns, and every subsequent peers/routes call timed out exactly when our client-side reqwest timeout (10s) fired. Concluded mycelium 0.6.1's HTTP server serialises requests: while the long-poll connection is held, no other admin endpoint can respond. The sidecar process kept logging routes the whole time (seen in the in-app log buffer) — proof the daemon was alive, just unable to serve concurrent calls. Switch to short-poll: timeout=0 returns immediately, sleep 2s between iterations. Per-iteration server hold time is now millisecond-scale instead of 30s. --- src-tauri/src/poller.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/poller.rs b/src-tauri/src/poller.rs index ed0908a..e5d7189 100644 --- a/src-tauri/src/poller.rs +++ b/src-tauri/src/poller.rs @@ -11,7 +11,7 @@ use tracing::warn; const PEERS_INTERVAL: Duration = Duration::from_secs(3); const ROUTES_INTERVAL: Duration = Duration::from_secs(5); -const INBOX_LONG_POLL_SECS: u64 = 30; +const INBOX_INTERVAL: Duration = Duration::from_secs(2); const INBOX_RETRY_BACKOFF: Duration = Duration::from_secs(2); const INBOX_CAPACITY: usize = 200; @@ -121,18 +121,22 @@ fn spawn_inbox_loop( ) -> JoinHandle<()> { tokio::spawn(async move { loop { + tokio::time::sleep(INBOX_INTERVAL).await; let Some(client) = sidecar.client() else { break; }; - // Each iteration is a fresh long-poll. The daemon answers as - // soon as a message arrives, or returns an empty body / 204 - // when the timeout window elapses. - match client.pop_message(false, INBOX_LONG_POLL_SECS, None).await { + // Short-poll: timeout=0 returns immediately if no message. + // We previously used a 30s long-poll, but mycelium 0.6.1's + // HTTP server appears to serialise requests behind a single + // worker — holding the connection for 30s starved every + // other endpoint (peers, routes, admin) until our own + // 10s reqwest timeout kicked in. + match client.pop_message(false, 0, None).await { Ok(Some(msg)) => { me.push_inbox(msg.clone()); let _ = app.emit("messages://incoming", &msg); } - Ok(None) => {} // window expired, loop + Ok(None) => {} Err(e) => { warn!(error = %e, "inbox: pop_message failed"); tokio::time::sleep(INBOX_RETRY_BACKOFF).await;