Replace the in-tree scheduler with the domain-agnostic hive-jobq crate (merged in #2615): parent-axis grouping + borrow/subtree-reservation resource model + roll-up completion (State::Finishing). Host adaptation: - NodeSpec gains an explicit `parent` axis; templates declare grouping + sibling ordering directly (deps order execution, parent groups a subtree whose resource the descendants borrow). - Rebuild is a nested two-root subtree: Prebuild (root, owns the build slot for the whole subtree, lease-exempt) -> StopForUpdate (child, owns the agent lease) -> Swap/PostSwap (children, borrow both); Reconcile is a separate top-level root (AfterAny Prebuild) so it survives the cancel- cascade of any failed step (recovery-start invariant) and converges to the persisted `wanted` on a fresh lease. This is the multi-root correction to the single-root-chain sketch: node0=root broke lease- exemption (hoisting the lease onto Prebuild) and recovery-reconcile (root failure cancels all children). - Spawn / perm-change / power-ops (stop/start/restart) group-rooted the same way; per-agent power-op subgraphs stay independent roots so a multi-agent DAG runs them concurrently, each on its own lease. - insert_group honours the explicit parent axis (no lease hoisting); the DAG terminal node deps AfterAny on every group root and runs once the whole op rolls up. Drop the old Graph::add_dep terminal wiring. 36/36 job_queue tests, full hive-c0re suite green, clippy --all-targets.
60 lines
2.4 KiB
Rust
60 lines
2.4 KiB
Rust
//! 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<Dep<Resource>> {
|
|
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
|
|
}
|
|
}
|