job_queue: declare a node's resources where the node is constructed
Resources were derived from the node's kind: `templates::node` called `NodeKind::resource_deps()`, which fanned out to `needs_build_slot` / `needs_lease` / `needs_meta_window`. That made the requirement a property of the *kind*, so a kind that happened to run under an ancestor already holding the resource could get away with declaring nothing. Three did. `Start`, `Stop` and `PostSwap` appear in none of the three predicates, and that was only safe because one construction site fans them out from inside a lease-holding `Reconcile` — a fact about today's DAG shape, not about the nodes. Each of the 41 construction sites now says what it holds. `Start` / `Stop` / `PostSwap` declare the agent lease; per the contract that is a re-entrant borrow, which a new test pins rather than argues. `running_transients` reads the node's declared deps instead of re-deriving from the kind. That closes the blank-pill gap: the pill went blank during container start, stop and the post-swap tail because the declaration was missing, not because the filter was wrong. The deleted predicates carried the only written record of three design decisions; each moved to the `Resource` variant it constrains rather than dying with its function.
This commit is contained in:
parent
1aa88463a0
commit
10dbdb444d
9 changed files with 256 additions and 177 deletions
|
|
@ -9,7 +9,7 @@
|
|||
//! - [`model::NodeKind`] **is** the crate payload `N` directly — each variant
|
||||
//! carries the agent it targets ([`NodeKind::agent`]); the two resource
|
||||
//! classes are [`resource::Resource`] (`BuildSlot` node-held, `Agent` lease
|
||||
//! subtree-held), derived per node by [`NodeKind::resource_deps`];
|
||||
//! subtree-held), declared per node at its construction site;
|
||||
//! - a **DAG is a single container node** ([`NodeKind::Dag`], `parent = None`)
|
||||
//! carrying the group's metadata, with the work nodes hung under it as
|
||||
//! its subtree (the **parent axis** groups; `deps` order). So the container's
|
||||
|
|
@ -436,10 +436,13 @@ impl JobQueue {
|
|||
/// `Stop` pill are both pills; only one means a vanished container is
|
||||
/// expected.
|
||||
///
|
||||
/// By design, `Start` / `Stop` / `PostSwap` run inside a lease-holding
|
||||
/// ancestor and re-declare nothing, so they light no pill; closing that is
|
||||
/// the resources-where-constructed work, not this function. An agent's lease
|
||||
/// is cap-1, so at most one entry per agent.
|
||||
/// Read off the node's **declared** resource edges, not off its kind. Those
|
||||
/// are the same thing now that every construction site states what it holds,
|
||||
/// and the distinction is the whole point: `Start` / `Stop` / `PostSwap` run
|
||||
/// inside a lease-holding ancestor, and while the declaration was derived
|
||||
/// from the kind they re-declared nothing and lit no pill. Asking the node
|
||||
/// what it holds cannot go stale that way. An agent's lease is cap-1, so at
|
||||
/// most one entry per agent.
|
||||
#[must_use]
|
||||
pub fn running_transients(&self) -> Vec<RunningTransient> {
|
||||
let inner = self.lock();
|
||||
|
|
@ -449,14 +452,13 @@ impl JobQueue {
|
|||
.nodes()
|
||||
.filter(|n| matches!(n.state, State::Running))
|
||||
.filter_map(|n| {
|
||||
let agent =
|
||||
n.payload
|
||||
.resource_deps()
|
||||
.into_iter()
|
||||
.find_map(|(name, _)| match name {
|
||||
Resource::Agent(a) => Some(a),
|
||||
_ => None,
|
||||
})?;
|
||||
let agent = n.deps.iter().find_map(|dep| match dep {
|
||||
hive_jobq::Dep::Resource {
|
||||
name: Resource::Agent(a),
|
||||
..
|
||||
} => Some(a.clone()),
|
||||
_ => None,
|
||||
})?;
|
||||
Some(RunningTransient {
|
||||
agent,
|
||||
label: n.payload.as_str().to_owned(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue