From ac6f1e9294febbd66c2137f1079f37fe90c93865 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Apr 2026 20:37:54 -0400 Subject: [PATCH] unconscious: move health refresh outside lock too refresh_health() was doing Store::load() + compute_graph_health() while holding the Unconscious lock, causing 12 second stalls. Split into needs_health_refresh() (quick check) and set_health() (quick store), with the slow I/O happening outside the lock. Co-Authored-By: Proof of Concept --- src/mind/mod.rs | 8 ++++++++ src/mind/unconscious.rs | 24 ++++++++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/mind/mod.rs b/src/mind/mod.rs index 440be05..dbc6a6d 100644 --- a/src/mind/mod.rs +++ b/src/mind/mod.rs @@ -346,6 +346,14 @@ impl Mind { s.unc_idle = true; } loop { + // Phase 0: health check outside lock (slow I/O) + let needs_health = unc.lock().await.needs_health_refresh(); + if needs_health { + if let Ok(store) = crate::store::Store::load() { + let health = crate::subconscious::daemon::compute_graph_health(&store); + unc.lock().await.set_health(health); + } + } // Phase 1: quick work under lock let to_spawn = { let mut guard = unc.lock().await; diff --git a/src/mind/unconscious.rs b/src/mind/unconscious.rs index aa0932d..b6a2eac 100644 --- a/src/mind/unconscious.rs +++ b/src/mind/unconscious.rs @@ -166,25 +166,21 @@ impl Unconscious { }).collect() } - fn refresh_health(&mut self) { - let store = match crate::store::Store::load() { - Ok(s) => s, - Err(_) => return, - }; - self.graph_health = Some(crate::subconscious::daemon::compute_graph_health(&store)); + /// Check if health refresh is due (quick check, no I/O). + pub fn needs_health_refresh(&self) -> bool { + self.last_health_check + .map(|t| t.elapsed() > std::time::Duration::from_secs(600)) + .unwrap_or(true) + } + + /// Store computed health (quick, just assignment). + pub fn set_health(&mut self, health: crate::subconscious::daemon::GraphHealth) { + self.graph_health = Some(health); self.last_health_check = Some(Instant::now()); } /// Reap finished agents (quick, hold lock briefly). pub fn reap_finished(&mut self) { - // Periodic graph health refresh (also on first call) - if self.last_health_check - .map(|t| t.elapsed() > std::time::Duration::from_secs(600)) - .unwrap_or(true) - { - self.refresh_health(); - } - for agent in &mut self.agents { if agent.handle.as_ref().is_some_and(|h| h.is_finished()) { let handle = agent.handle.take().unwrap();