job_queue: drop Claim, claim_ready and the completion wrappers
c0re's run loop now goes through hive_jobq's claim_next seam, so the host layer no longer needs its own claim/complete vocabulary. exec::run_node takes (NodeId, &NodeKind) instead of a &Claim snapshot. The agent already rides the payload, and the DAG id is a derived read (JobQueue::dag_of) that only three arms want, so it is taken per-arm rather than eagerly for every node. Two arms (WritePermFile, Reparent) re-matched the kind behind a bail! that could never fire; the match arm already destructures the payload, so they take it directly now. Deleted from the c0re layer: - struct Claim - JobQueue::claim_ready - JobQueue::complete_node / complete_node_growing - scheduler::NodeDone / handle_completion Completion happens inside the future claim_next hands back, so "ran the node but forgot to complete it" is not expressible on the production path any more. The node done / node failed logging moved with it -- it lived in handle_completion but is not dead code. claim_ready and the completion wrappers were left with no non-test callers, so the tests carry them as ClaimReady / CompleteNode extension traits over the crate primitives. JobQueue::new_job stays: run_worker still mints an empty builder on a failed outcome.
This commit is contained in:
parent
be1060e52f
commit
ab53f6710d
4 changed files with 263 additions and 255 deletions
|
|
@ -65,8 +65,96 @@ fn stop_online(
|
|||
submit::stop_spec(&targets, graceful, Source::Manual, reason.to_owned())
|
||||
}
|
||||
|
||||
/// What a test observes about a node the settle loop just started: its id, its
|
||||
/// DAG, and its payload.
|
||||
///
|
||||
/// **Test-only, and deliberately not a production type.** `exec::run_node`
|
||||
/// takes `(NodeId, &NodeKind)` and derives the DAG id on the two arms that
|
||||
/// actually want it — nothing in production needs a claim snapshot to exist.
|
||||
/// The assertions here are about *which* node the graph let run, which does
|
||||
/// need the payload next to the id.
|
||||
#[derive(Debug, Clone)]
|
||||
struct Claimed {
|
||||
dag_id: u64,
|
||||
node_id: NodeId,
|
||||
kind: NodeKind,
|
||||
agent: String,
|
||||
}
|
||||
|
||||
/// Drive one settle wave and report every node that started.
|
||||
///
|
||||
/// An **extension trait rather than a method on [`JobQueue`]**: production
|
||||
/// claims one node at a time ([`hive_jobq::scheduler::Scheduler::claim_next`])
|
||||
/// and has no use for a whole wave, so this must not be reachable from
|
||||
/// non-test code. `settle()` is that same claim primitive in a loop, so a test
|
||||
/// driving it here exercises the production path.
|
||||
trait ClaimReady {
|
||||
fn claim_ready(&self) -> Vec<Claimed>;
|
||||
}
|
||||
|
||||
impl ClaimReady for JobQueue {
|
||||
fn claim_ready(&self) -> Vec<Claimed> {
|
||||
let mut sched = self.sched().lock().expect("job_queue mutex poisoned");
|
||||
let started = sched.settle();
|
||||
started
|
||||
.into_iter()
|
||||
.filter_map(|node_id| {
|
||||
let kind = sched.graph().node(node_id)?.payload.clone();
|
||||
Some(Claimed {
|
||||
dag_id: sched.graph().root_of(node_id)?.get(),
|
||||
node_id,
|
||||
agent: kind.agent().to_owned(),
|
||||
kind,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Drive a node terminal by hand.
|
||||
///
|
||||
/// Also an extension trait, for the same reason as [`ClaimReady`]: production
|
||||
/// completes a node **inside** the future
|
||||
/// [`hive_jobq::scheduler::Scheduler::claim_next`] hands back, so "run the node,
|
||||
/// then remember to complete it" is not an expressible sequence there — which
|
||||
/// was the whole point of the seam. These tests need to express it, because
|
||||
/// they exercise the graph without running any executor.
|
||||
trait CompleteNode {
|
||||
fn complete_node(&self, node_id: NodeId, result: Result<(), String>);
|
||||
fn complete_node_growing(&self, node_id: NodeId, result: Result<(), String>, grown: Job);
|
||||
}
|
||||
|
||||
impl CompleteNode for JobQueue {
|
||||
fn complete_node(&self, node_id: NodeId, result: Result<(), String>) {
|
||||
self.sched()
|
||||
.lock()
|
||||
.expect("job_queue mutex poisoned")
|
||||
.complete(node_id, outcome_of(result));
|
||||
self.notify.notify_one();
|
||||
}
|
||||
|
||||
fn complete_node_growing(&self, node_id: NodeId, result: Result<(), String>, grown: Job) {
|
||||
// A rejected grown job is logged, not propagated: the node's own work
|
||||
// already ran, and refusing to complete it here would both misreport
|
||||
// that and wedge the DAG on a node stuck `Running`.
|
||||
if let Err(e) = self
|
||||
.sched()
|
||||
.lock()
|
||||
.expect("job_queue mutex poisoned")
|
||||
.complete_growing(node_id, outcome_of(result), grown)
|
||||
{
|
||||
tracing::error!(
|
||||
node = node_id.get(),
|
||||
error = %e,
|
||||
"job_queue: work grown by a completing node was rejected"
|
||||
);
|
||||
}
|
||||
self.notify.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
/// Claim helper asserting exactly one node comes back.
|
||||
fn claim_one(q: &JobQueue) -> Claim {
|
||||
fn claim_one(q: &JobQueue) -> Claimed {
|
||||
let mut claims = q.claim_ready();
|
||||
assert_eq!(
|
||||
claims.len(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue