jobq: split RebuildOpts into two rebuild entry points
RebuildOpts held one real parameter (relock) and one single-call-site flag (graceful). The struct justified itself as swap-protection for two positional bools; with graceful out of the signature there is nothing left to swap. graceful stays an internal switch rather than moving to the caller: it re-parents the stop root (StopForUpdate goes from part_of(prebuild) to part_of(signal)) rather than prepending nodes, so a caller could only declare it by being handed the subtree's internals — and that nesting keeps the agent lease continuous across the whole stop. run_meta_lock no longer returns options: both fields were a pure function of the sweep flag its caller had just passed in. 315 tests pass unchanged.
This commit is contained in:
parent
8db1cbd204
commit
05f84191fb
4 changed files with 80 additions and 110 deletions
|
|
@ -90,9 +90,18 @@ fn resolve_approval_tails(b: &Job, approval_id: i64, root: Handle<'_>) {
|
|||
///
|
||||
/// Same reason as [`fanned_out_mechanical`] for living here: this was the
|
||||
/// second construction site declaring nodes inline in an executor.
|
||||
pub(crate) fn grown_rebuilds(b: &Job, agents: &[String], opts: RebuildOpts) {
|
||||
pub(crate) fn grown_rebuilds(b: &Job, agents: &[String], relock: bool) {
|
||||
for agent in agents {
|
||||
rebuild_nodes(b, agent, opts, None);
|
||||
rebuild_nodes(b, agent, relock, None);
|
||||
}
|
||||
}
|
||||
|
||||
/// As [`grown_rebuilds`], but each agent gets its `Signal` → `Drain` window
|
||||
/// before being stopped. The boot sweep's flavour: it stops agents that were
|
||||
/// mid-turn when the host came up, so they drain rather than being cut off.
|
||||
pub(crate) fn grown_graceful_rebuilds(b: &Job, agents: &[String], relock: bool) {
|
||||
for agent in agents {
|
||||
graceful_rebuild_nodes(b, agent, relock, None);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,19 +122,6 @@ pub(crate) fn fanned_out_mechanical(b: &Job, kind: NodeKind) {
|
|||
let _ = b.node(kind).needs(lease);
|
||||
}
|
||||
|
||||
/// Knobs for [`rebuild_nodes`]. A struct rather than two positional `bool`s so
|
||||
/// a call site cannot silently swap them.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct RebuildOpts {
|
||||
/// Re-lock the meta flake inside `MetaSync`.
|
||||
pub relock: bool,
|
||||
/// Give the agent its `Signal` → `Drain` window to finish the turn in
|
||||
/// flight before the container is stopped, instead of stopping it
|
||||
/// outright. Costs up to one `GRACEFUL_STOP_TIMEOUT` per subgraph, and
|
||||
/// those overlap across agents.
|
||||
pub graceful: bool,
|
||||
}
|
||||
|
||||
/// The group-roots a [`rebuild_nodes`] subgraph exposes to its caller: what a
|
||||
/// tail node edges onto, and what a follow-up node waits for.
|
||||
///
|
||||
|
|
@ -179,14 +175,14 @@ impl<'a> RebuildRoots<'a> {
|
|||
/// cancel-cascades `Prebuild`, i.e. terminal, so the tail still runs). It
|
||||
/// takes a fresh lease; the tiny gap is harmless — `Reconcile` converges to
|
||||
/// the persisted `wanted` idempotently.
|
||||
pub(crate) fn rebuild_nodes<'a>(
|
||||
fn rebuild_subtree<'a>(
|
||||
b: &'a Job,
|
||||
agent: &str,
|
||||
opts: RebuildOpts,
|
||||
relock: bool,
|
||||
graceful: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
let a = || agent.to_owned();
|
||||
let RebuildOpts { relock, graceful } = opts;
|
||||
|
||||
let mut meta_sync = b
|
||||
.node(NodeKind::MetaSync { agent: a(), relock })
|
||||
|
|
@ -249,6 +245,35 @@ pub(crate) fn rebuild_nodes<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// The rebuild subtree, stopping the agent outright — the shape five of the six
|
||||
/// call sites want. `relock` re-locks the meta flake inside `MetaSync`; `after`,
|
||||
/// when given, is the node this subgraph chains behind. See
|
||||
/// [`rebuild_subtree`] for the structure.
|
||||
pub(crate) fn rebuild_nodes<'a>(
|
||||
b: &'a Job,
|
||||
agent: &str,
|
||||
relock: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
rebuild_subtree(b, agent, relock, false, after)
|
||||
}
|
||||
|
||||
/// As [`rebuild_nodes`], but the agent gets a `Signal` → `Drain` window to
|
||||
/// finish the turn in flight before it is stopped. Costs up to one
|
||||
/// `GRACEFUL_STOP_TIMEOUT` per subgraph, and those overlap across agents.
|
||||
///
|
||||
/// A separate entry point rather than a flag because `graceful` does not
|
||||
/// *prepend* nodes — it **re-parents** the stop root, so a caller cannot
|
||||
/// declare it without being handed the internals. Only the boot sweep wants it.
|
||||
pub(crate) fn graceful_rebuild_nodes<'a>(
|
||||
b: &'a Job,
|
||||
agent: &str,
|
||||
relock: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
rebuild_subtree(b, agent, relock, true, after)
|
||||
}
|
||||
|
||||
/// The rebuild subgraph a [`NodeKind::DeployApply`] grows into its own DAG once
|
||||
/// the merge has landed and `prepare_deploy` has staged the lock, plus the
|
||||
/// [`NodeKind::FinalizeDeploy`] that closes the window behind it.
|
||||
|
|
@ -274,15 +299,7 @@ pub(crate) fn rebuild_nodes<'a>(
|
|||
/// and `FinalizeDeploy` declare is re-entered from the ancestor already holding
|
||||
/// it rather than deadlocking against it.
|
||||
pub(crate) fn deploy_rebuild_nodes(b: &Job, agent: &str, approval_id: i64) {
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
agent,
|
||||
RebuildOpts {
|
||||
relock: false,
|
||||
graceful: false,
|
||||
},
|
||||
None,
|
||||
);
|
||||
let roots = rebuild_nodes(b, agent, false, None);
|
||||
let _finalize = b
|
||||
.node(NodeKind::FinalizeDeploy {
|
||||
agent: agent.to_owned(),
|
||||
|
|
@ -305,15 +322,7 @@ pub(crate) fn deploy_rebuild_nodes(b: &Job, agent: &str, approval_id: i64) {
|
|||
/// node. Edging `Reconcile` alone would not do: it is `AfterAny` `Prebuild`, so
|
||||
/// it reaches `Done` even after a failed swap and the tail would report success.
|
||||
pub fn rebuild(b: &Job, agent: &str, relock: bool) {
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
agent,
|
||||
RebuildOpts {
|
||||
relock,
|
||||
graceful: false,
|
||||
},
|
||||
None,
|
||||
);
|
||||
let roots = rebuild_nodes(b, agent, relock, None);
|
||||
emit_rebuilt_tails(b, agent, &roots.all());
|
||||
}
|
||||
|
||||
|
|
@ -428,15 +437,7 @@ pub fn perm_change(b: &Job, agent: &str, payload: PermPayload) {
|
|||
payload,
|
||||
})
|
||||
.needs(Resource::MetaWindow);
|
||||
let roots = rebuild_nodes(
|
||||
b,
|
||||
agent,
|
||||
RebuildOpts {
|
||||
relock: true,
|
||||
graceful: false,
|
||||
},
|
||||
Some(write),
|
||||
);
|
||||
let roots = rebuild_nodes(b, agent, true, Some(write));
|
||||
emit_rebuilt_tails(
|
||||
b,
|
||||
agent,
|
||||
|
|
|
|||
Loading…
Reference in a new issue