feat(#2450): collapse the startup sweep into one inline DAG
The boot is now ONE DAG, assembled inline in submit_boot_tree — no boot_root
Noop anchor, no per-agent child DAGs, no display-only parent_id grouping: a
sweep MetaLock root (only when something is stale) that grows one rebuild
subgraph per stale agent into the same DAG (via append_subgraph, previous
commit), plus one Reconcile root per drifted agent (independent — a boot
reconcile needs no lock bump).
- submit_boot_tree builds the DagSpec inline; removed the single-use
templates::{boot_root, startup_sweep} builders (inlined per the operator's
"don't force single-use shapes into templates.rs" steer).
- fanout_specs simplified to the meta-update cascade path only — the startup
sweep no longer fans out child DAGs, so its branch was dead.
- test: append_subgraph_roots_on_emitter_and_rebases_local_deps.
Vestigial after this (deliberately left as follow-ups, flagged in the PR):
NodeKind::Noop is now unconstructed (contained to hive-c0re, removable);
Template::StartupSweep is unconstructed but a hive-sh4re wire type
(frontend-coordinated removal).
This commit is contained in:
parent
b6defdeaaf
commit
4545dd312e
5 changed files with 138 additions and 112 deletions
|
|
@ -288,12 +288,14 @@ pub async fn run(coord: Arc<Coordinator>) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Submit this boot's DAGs under one `Boot` root: a `Noop` anchor with the
|
||||
/// startup sweep + per-agent reconciles hung off it via `parent_id`, so the
|
||||
/// dashboard renders the boot as a single tree instead of N+1 rows. No-op when
|
||||
/// there's nothing to do. The `parent_id` link is a display grouping, not a
|
||||
/// dependency edge — the children run concurrently, so the reconciles never
|
||||
/// wait behind the lock bump.
|
||||
/// Submit this boot's work as **one DAG** (no `boot_root` Noop anchor, no
|
||||
/// per-agent child DAGs). Node 0 is the sweep `MetaLock` (only when
|
||||
/// something is stale) — its executor bumps the hyperhive lock, then grows
|
||||
/// one rebuild subgraph per stale agent into *this same* DAG (rooted on the
|
||||
/// `MetaLock`, so they build against the post-bump lock; see
|
||||
/// `exec::run_meta_lock`). Every drifted agent gets a boot `Reconcile` as an
|
||||
/// independent root — a boot reconcile needs no lock bump, so it converges
|
||||
/// concurrently with the sweep. No-op when there's nothing to do.
|
||||
fn submit_boot_tree(
|
||||
coord: &Arc<Coordinator>,
|
||||
any_stale: bool,
|
||||
|
|
@ -302,59 +304,61 @@ fn submit_boot_tree(
|
|||
n_deferred: usize,
|
||||
n_skipped: usize,
|
||||
) {
|
||||
// Only emit a boot root when there's actually boot work — a fully-quiet
|
||||
// boot (nothing stale, nothing drifted) submits nothing, exactly as before.
|
||||
let boot_root_id = if any_stale || !drifted.is_empty() {
|
||||
let reason = format!(
|
||||
"boot: {} rebuild(s), {} reconcile(s), {} deferred (offline), {} up-to-date",
|
||||
fanout.len(),
|
||||
drifted.len(),
|
||||
n_deferred,
|
||||
n_skipped,
|
||||
);
|
||||
match coord
|
||||
.job_queue
|
||||
.submit(crate::job_queue::templates::boot_root(reason))
|
||||
{
|
||||
Ok(id) => Some(id),
|
||||
Err(e) => {
|
||||
tracing::warn!(error = ?e, "boot reconcile: boot-root submit failed");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
use crate::job_queue::{DagSpec, NodeKind, NodeSpec, Source, Template};
|
||||
|
||||
// Fully-quiet boot (nothing stale, nothing drifted) submits nothing.
|
||||
if !any_stale && drifted.is_empty() {
|
||||
return;
|
||||
}
|
||||
let reason = format!(
|
||||
"boot: {} rebuild(s), {} reconcile(s), {} deferred (offline), {} up-to-date",
|
||||
fanout.len(),
|
||||
drifted.len(),
|
||||
n_deferred,
|
||||
n_skipped,
|
||||
);
|
||||
|
||||
let mut nodes: Vec<NodeSpec> = Vec::new();
|
||||
// Sweep whenever ANY marker is stale — even when every stale agent is
|
||||
// wanted-offline: the hyperhive lock bump must land now so their later
|
||||
// start-upgrade rebuilds build against it. No stale agents ⇒ no sweep ⇒ no
|
||||
// meta commit on a no-change boot.
|
||||
// start-upgrade rebuilds build against it. No stale agents ⇒ no MetaLock
|
||||
// ⇒ no meta commit on a no-change boot. The `fanout` list rides the
|
||||
// MetaLock into `run_meta_lock`, which appends the rebuild subgraphs.
|
||||
if any_stale {
|
||||
let reason = format!(
|
||||
"startup sweep: {} rebuild(s), {} deferred (offline), {} up-to-date",
|
||||
fanout.len(),
|
||||
n_deferred,
|
||||
n_skipped,
|
||||
);
|
||||
let mut spec = crate::job_queue::templates::startup_sweep(reason, fanout);
|
||||
spec.parent_id = boot_root_id;
|
||||
if let Err(e) = coord.job_queue.submit(spec) {
|
||||
tracing::warn!(error = ?e, "boot reconcile: sweep submit failed");
|
||||
}
|
||||
nodes.push(NodeSpec {
|
||||
agent: "hyperhive".to_owned(),
|
||||
kind: NodeKind::MetaLock {
|
||||
sweep: true,
|
||||
fanout: Some(fanout),
|
||||
},
|
||||
deps: Vec::new(),
|
||||
});
|
||||
}
|
||||
// One boot Reconcile per drifted agent — independent roots.
|
||||
for name in drifted {
|
||||
let mut spec = crate::job_queue::templates::reconcile_only(
|
||||
crate::job_queue::Template::Reconcile,
|
||||
&name,
|
||||
crate::job_queue::Source::AutoUpdate,
|
||||
"boot reconcile".to_owned(),
|
||||
None,
|
||||
);
|
||||
spec.parent_id = boot_root_id;
|
||||
if let Err(e) = coord.job_queue.submit(spec) {
|
||||
tracing::warn!(%name, error = ?e, "boot reconcile: submit failed");
|
||||
}
|
||||
nodes.push(NodeSpec {
|
||||
agent: name,
|
||||
kind: NodeKind::Reconcile,
|
||||
deps: Vec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let spec = DagSpec {
|
||||
template: Template::Boot,
|
||||
source: Source::AutoUpdate,
|
||||
reason,
|
||||
parent_id: None,
|
||||
approval_id: None,
|
||||
inputs: Vec::new(),
|
||||
perm_payload: None,
|
||||
// Rebuilding when the sweep will grow rebuild subgraphs (per-agent
|
||||
// crash-watch suppression during their Swap, applied at claim time);
|
||||
// a reconcile-only boot needs no transient.
|
||||
transient: any_stale.then_some(crate::coordinator::TransientKind::Rebuilding),
|
||||
nodes,
|
||||
};
|
||||
if let Err(e) = coord.job_queue.submit(spec) {
|
||||
tracing::warn!(error = ?e, "boot: sweep DAG submit failed");
|
||||
}
|
||||
coord.emit_rebuild_queue_snapshot();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue