refactor(#2449): write power intent via a SetWanted DAG node, not a pre-submit side effect
The durable 'wanted' power intent was written by submit::{start,stop,
restart,graceful_restart,graceful_stop} as a synchronous pre-submit side
effect, then read by the DAG's tail Reconcile. That's not crash-safe
(a crash between the write and the enqueue loses it) and, with agent now
per-node, can't be per-agent in a DAG that spans agents.
Move it into the DAG as a head SetWanted node:
- NodeKind::SetWanted { up } + run_set_wanted executor (fails the node on
a write error, unlike the old warn-and-continue, so a stale intent
never reaches Reconcile).
- LEASE-NEEDING, not lease-exempt: it takes the agent lease so a power-op
DAG's intent-write + reconcile is atomic per-agent. If it were exempt,
two racing ops (restart vs stop) would run both intent-writes up front
and clobber each other before either reconciled — defeating the point
of moving the write into the DAG. (In stale_start the lease is thus held
across the head Prebuild, but that's a no-op there: the agent is down so
prebuild is skipped.)
- templates: explicit SetWanted node 0 on restart/graceful_restart/
graceful_stop, plus dedicated start/stop templates (SetWanted -> Reconcile)
and stale_start (SetWanted(Up) -> rebuild subgraph, reusing rebuild_nodes).
No compose helper / rebuild variant. reconcile_only is now boot-only.
- submit.rs: drop the set_wanted side effect; the stale-rev shape decision
(start vs stale_start) stays submit-side.
All 33 job_queue tests pass (shape/lease tests updated for the head node).
This commit is contained in:
parent
6c654921a0
commit
5fe8008cce
5 changed files with 189 additions and 100 deletions
|
|
@ -121,6 +121,20 @@ pub enum NodeKind {
|
|||
/// dashboard. Holds no lease and no build slot; the child DAGs it anchors
|
||||
/// still run concurrently — the grouping is a display link, not a dep edge.
|
||||
Noop,
|
||||
/// Write the agent's durable power intent (`wanted = Up` when `up`, else
|
||||
/// `Offline`) as a first-class DAG node, at the head of a power-op
|
||||
/// template so the downstream `Reconcile` reads it. Replaces the old
|
||||
/// pre-submit `set_wanted` side effect: the intent write is now part of
|
||||
/// the atomic DAG (crash-safe, per-agent — a multi-agent DAG carries one
|
||||
/// `SetWanted` per agent). Build-slot-exempt (a store write), but
|
||||
/// **lease-needing**: it takes the agent's lifecycle lease so the whole
|
||||
/// power-op DAG (intent write → reconcile) is atomic per-agent — two
|
||||
/// racing ops (e.g. restart vs stop) can't clobber each other's intent
|
||||
/// before either reconciles, which is the point of moving the write into
|
||||
/// the DAG. (In `stale_start` the lease is thus held across the head
|
||||
/// `Prebuild`, but that's a no-op there — the agent is down, so prebuild
|
||||
/// is skipped.)
|
||||
SetWanted { up: bool },
|
||||
}
|
||||
|
||||
impl NodeKind {
|
||||
|
|
@ -142,6 +156,7 @@ impl NodeKind {
|
|||
NodeKind::WritePermFile => "write_perm_file",
|
||||
NodeKind::ApprovalDeploy => "approval_deploy",
|
||||
NodeKind::Noop => "noop",
|
||||
NodeKind::SetWanted { .. } => "set_wanted",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +192,7 @@ impl NodeKind {
|
|||
| NodeKind::Drain
|
||||
| NodeKind::WriteDropin
|
||||
| NodeKind::ApprovalDeploy
|
||||
| NodeKind::SetWanted { .. }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue