From 69682a6afa67fe5bf3180c70441f1fda9277bb1d Mon Sep 17 00:00:00 2001 From: damocles Date: Thu, 18 Jun 2026 21:15:29 +0200 Subject: [PATCH] bound /api/state container rescan so introspection survives a saturated build backend --- hive-c0re/src/dashboard.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index 3790409c..64089084 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -501,7 +501,28 @@ async fn api_state(headers: HeaderMap, State(state): State) -> axum::J // produced; live clients converge via the matching // `ContainerStateChanged` / `ContainerRemoved` events the rescan // emits. - state.coord.rescan_containers_and_emit().await; + // + // Bound the rescan: it shells out (`nixos-container list` etc.), so a + // saturated/wedged build backend — e.g. hive-c0re mid-startup-sweep + // hammering slow `nixos-container update` subprocesses — can stall it + // long enough that `/api/state` hangs for the whole request (the + // ~minute-long /state reported in the field). On timeout we skip the + // fresh rescan and serve the last cached snapshot instead; live + // clients still converge via the SSE events a later successful rescan + // emits, and the next /state call retries the refresh. Introspection + // stays responsive regardless of the build backend's health. + if tokio::time::timeout( + std::time::Duration::from_secs(3), + state.coord.rescan_containers_and_emit(), + ) + .await + .is_err() + { + tracing::warn!( + "api_state: container rescan exceeded 3s (build backend likely saturated); \ + serving last cached snapshot" + ); + } let containers = state.coord.containers_snapshot().await; let any_stale = containers.iter().any(|c| c.needs_update); let transient_snapshot = state.coord.transient_snapshot();