feat(#2446): release a DAG's per-agent lease when that agent's subgraph is terminal
A per-agent lifecycle lease gates that agent's container globally across concurrent DAGs, so it should be held for exactly as long as the agent's work in a DAG is in flight, no longer. settle() previously freed every lease a DAG held only at whole-DAG terminal, so a multi-agent DAG (a hive-wide restart) kept agent A's container locked until B and C also finished, blocking any other DAG wanting A. Now free each agent's lease the moment its own subgraph within the DAG is terminal (no live node still targets it), and drop that agent's dashboard transient pill on the same edge via a new per-agent release channel. A single-agent DAG is unaffected: its agent's subgraph goes terminal exactly when the whole DAG does, so behaviour is identical.
This commit is contained in:
parent
9edd37501a
commit
9fdadb99c0
4 changed files with 136 additions and 25 deletions
|
|
@ -328,6 +328,52 @@ fn multi_agent_restart_is_one_dag_with_concurrent_per_agent_subgraphs() {
|
|||
);
|
||||
}
|
||||
|
||||
/// A multi-agent DAG frees an agent's lease the moment THAT agent's
|
||||
/// subgraph is terminal — not when the whole DAG finishes. So a
|
||||
/// concurrent DAG wanting the finished agent can proceed while the rest
|
||||
/// of the first DAG runs on.
|
||||
#[test]
|
||||
fn multi_agent_lease_frees_per_subgraph_not_whole_dag() {
|
||||
let q = JobQueue::new(4);
|
||||
let id = submit(&q, restart_online(&["agent-a", "agent-b"], false, "r"));
|
||||
|
||||
// Drive agent-a's ENTIRE subgraph to Done while leaving agent-b's
|
||||
// head running (so agent-b keeps holding its lease).
|
||||
let mut b_in_flight = false;
|
||||
loop {
|
||||
let mut progressed = false;
|
||||
for c in q.claim_ready() {
|
||||
if c.agent == "agent-a" {
|
||||
q.complete_node(id, c.node_id, Ok(()));
|
||||
progressed = true;
|
||||
} else {
|
||||
b_in_flight = true; // leave agent-b's node running
|
||||
}
|
||||
}
|
||||
if !progressed {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(b_in_flight, "agent-b subgraph should still be in flight");
|
||||
// The DAG as a whole is NOT terminal — agent-b runs on.
|
||||
assert_eq!(state_of(&q, id), State::Running);
|
||||
|
||||
// agent-a's lease is freed early → a concurrent agent-a DAG runs;
|
||||
// an agent-b DAG still blocks on the lease agent-b's subgraph holds.
|
||||
submit(&q, restart_online(&["agent-a"], false, "concurrent-a"));
|
||||
submit(&q, restart_online(&["agent-b"], false, "concurrent-b"));
|
||||
let claims = q.claim_ready();
|
||||
let agents: Vec<&str> = claims.iter().map(|c| c.agent.as_str()).collect();
|
||||
assert!(
|
||||
agents.contains(&"agent-a"),
|
||||
"agent-a lease freed the moment its subgraph settled"
|
||||
);
|
||||
assert!(
|
||||
!agents.contains(&"agent-b"),
|
||||
"agent-b lease still held — its subgraph is still in flight"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_agent_stop_is_one_dag_with_concurrent_per_agent_subgraphs() {
|
||||
let q = JobQueue::new(4);
|
||||
|
|
|
|||
Loading…
Reference in a new issue