From 465d68d5a70a0bbc56fbe2e5b142e8031e78917c Mon Sep 17 00:00:00 2001 From: atlas Date: Sat, 15 Aug 2026 18:46:35 +0200 Subject: [PATCH] fix(swarm-controller): bound the token request, and stop claiming a connection Both from argus's review on the PR, both non-blocking, both real. The HTTP client had no timeout, and it runs INSIDE the auth callback: a token endpoint that accepts the connection and then never answers would hang the callback and the connection attempt that invoked it, with no retry and nothing in the log to say why. That is the same hang class the status endpoint's connection-state check exists to prevent, one layer up. Failing fast lets async-nats back off and try again, which it already does well. And the startup log said "connected to the swarm queue" at a point where `retry_on_initial_connect` guarantees no connection has been established yet - so the journal would read "connected", then 503 "not connected" moments later, and a reader would rightly distrust the second line rather than the first. --- swarm-controller/src/main.rs | 9 ++++++++- swarm-controller/src/queue.rs | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/swarm-controller/src/main.rs b/swarm-controller/src/main.rs index 71459cbd..244a3e4e 100644 --- a/swarm-controller/src/main.rs +++ b/swarm-controller/src/main.rs @@ -306,7 +306,14 @@ async fn main() -> Result<()> { } Some(cfg) => match queue::connect(cfg).await { Ok(client) => { - tracing::info!("connected to the swarm queue"); + // NOT "connected": `retry_on_initial_connect` returns a client + // before any connection has been established, so claiming a + // connection here would put "connected to the swarm queue" in + // the journal moments before every request 503s with "not + // connected" — and a reader would rightly distrust the second + // line rather than the first. The connection's real state is + // reported by the status endpoint, which checks it per request. + tracing::info!("swarm queue configured; connecting in the background"); Some(Arc::new(status::StatusReader::new( client, status::StatusReader::stale_after_from_env(), diff --git a/swarm-controller/src/queue.rs b/swarm-controller/src/queue.rs index 1be9e846..6291f43d 100644 --- a/swarm-controller/src/queue.rs +++ b/swarm-controller/src/queue.rs @@ -135,7 +135,16 @@ async fn mint_token(http: &reqwest::Client, cfg: &QueueConfig) -> Result /// Connect to the swarm queue, minting a token for each connection attempt. pub async fn connect(cfg: QueueConfig) -> Result { - let http = reqwest::Client::new(); + // A timeout, because this client runs INSIDE the auth callback: a token + // endpoint that accepts the connection and then never answers would hang + // the callback, and with it the connection attempt that invoked it, with + // no retry and nothing in the log to say why. Failing fast lets + // `async-nats` do what it already does well — back off and try again. + // 10s is generous for a form POST to a local IdP. + let http = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(10)) + .build() + .context("building the token-endpoint HTTP client")?; let url = cfg.url.clone(); let client = async_nats::ConnectOptions::with_auth_callback(move |_nonce| {