diff --git a/hive-c0re/src/agent_config/resource_limits.rs b/hive-c0re/src/agent_config/resource_limits.rs index 31be2d69..c034ab79 100644 --- a/hive-c0re/src/agent_config/resource_limits.rs +++ b/hive-c0re/src/agent_config/resource_limits.rs @@ -70,20 +70,34 @@ pub fn read() -> BTreeMap { serde_json::from_str(&raw).unwrap_or_default() } -/// Look up one agent's overrides. Returns the all-`None` default when -/// the agent has no entry. -#[must_use] -pub fn limits_for(name: &str) -> AgentLimits { - read().get(name).cloned().unwrap_or_default() -} - /// Resolve the effective values for an agent, filling each unset field /// from the hive-wide default. This is the single place the fallback /// rule lives; `write_dropins` calls it and passes the result straight /// to systemd. +/// +/// Reads the override file. Use [`effective_from`] when resolving more +/// than one agent in a row. #[must_use] pub fn effective(name: &str, hive_cpu_quota: &str, hive_memory_max: &str) -> (String, String) { - resolve(&limits_for(name), hive_cpu_quota, hive_memory_max) + effective_from(&read(), name, hive_cpu_quota, hive_memory_max) +} + +/// [`effective`] against an already-loaded map — the multi-agent form. +/// +/// `container_view::build_all` renders every agent on each SSE scan, so +/// it loads the map once and calls this per agent rather than re-reading +/// the same small file N times per scan. +#[must_use] +pub fn effective_from( + limits: &BTreeMap, + name: &str, + hive_cpu_quota: &str, + hive_memory_max: &str, +) -> (String, String) { + match limits.get(name) { + Some(l) => resolve(l, hive_cpu_quota, hive_memory_max), + None => (hive_cpu_quota.to_owned(), hive_memory_max.to_owned()), + } } /// Pure core of [`effective`], split out so the fallback matrix is diff --git a/hive-c0re/src/container_view.rs b/hive-c0re/src/container_view.rs index f312020e..29fbe451 100644 --- a/hive-c0re/src/container_view.rs +++ b/hive-c0re/src/container_view.rs @@ -88,6 +88,10 @@ pub async fn build_all(hive: &crate::coordinator::HiveEnv) -> Vec // Empty / absent topology.json → every agent root-level (safe // degradation for fresh installs that haven't run sync_agents yet). let topology = crate::topology::read(); + // Same once-per-scan treatment as the topology map: the override file + // is read here and resolved per agent below, rather than re-read for + // every container on every SSE scan. + let limits = crate::resource_limits::read(); let mut out = Vec::new(); for c in &raw { let Some(logical) = c.strip_prefix(AGENT_PREFIX) else { @@ -123,7 +127,8 @@ pub async fn build_all(hive: &crate::coordinator::HiveEnv) -> Vec None }; let paused = Coordinator::is_paused(&logical); - let (cpu_quota, memory_max) = crate::resource_limits::effective( + let (cpu_quota, memory_max) = crate::resource_limits::effective_from( + &limits, logical.as_str(), &hive.agent_cpu_quota, &hive.agent_memory_max,