//! The concrete resource + payload types the rebuild queue schedules over — //! the bridge from hive-c0re's [`NodeKind`] onto the domain-agnostic //! `hive-jobq` crate. `hive-jobq` is generic over a resource type //! `R: Clone + Eq + Hash` and a node payload `N`; here `R` is [`Resource`] and //! `N` is [`JobPayload`]. use hive_jobq::Dep; use super::model::NodeKind; /// The two resource classes the queue gates concurrency on, as the crate's /// generic resource type `R`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Resource { /// One of the `buildSlots` permits, held by a nix-heavy node for its /// duration. Capacity is `services.hyperhive.c0re.buildSlots` (default 1), /// set on the [`hive_jobq::resources::ResourceTable`] at construction. BuildSlot, /// The per-agent lifecycle lease — globally exclusive per agent across all /// DAGs (unconfigured, so the crate's default capacity 1 applies). Held by /// a DAG's first container-affecting node for that agent and re-entered by /// the rest of that agent's subtree via the crate's recursive lock, so two /// DAGs never interleave container ops on one agent. Agent(String), } /// A schedulable node's payload — the crate's generic `N`. Carries the /// primitive operation and the agent it targets. The agent is per-node (a DAG /// spans agents), and the lease [`Resource::Agent`] is keyed on it. #[derive(Debug, Clone)] pub struct JobPayload { pub kind: NodeKind, pub agent: String, } impl JobPayload { /// The [`Dep::Resource`] edges this node must acquire to run, derived from /// its kind + agent: a build slot for nix-heavy kinds /// ([`NodeKind::needs_build_slot`]) and the agent lease for /// container-affecting kinds ([`NodeKind::needs_lease`]). Lease-exempt /// container ops (`Start` / `Stop`, fanned out by a lease-holding /// `Reconcile`) hold no lease of their own — they re-enter the ancestor's /// `Agent` lock through the crate's recursive re-entrancy. pub fn resource_deps(&self) -> Vec> { let mut deps = Vec::new(); if self.kind.needs_build_slot() { deps.push(Dep::Resource { name: Resource::BuildSlot, count: 1, }); } if self.kind.needs_lease() { deps.push(Dep::Resource { name: Resource::Agent(self.agent.clone()), count: 1, }); } deps } }