feat(#2591): port hive-c0re job_queue onto the hive-jobq crate
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.
This commit is contained in:
parent
e58457da17
commit
a5c321a1a0
14 changed files with 1111 additions and 893 deletions
60
hive-c0re/src/job_queue/resource.rs
Normal file
60
hive-c0re/src/job_queue/resource.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//! 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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue