fix(#1218): bulk topology move uses one git commit via new set-parent-bulk endpoint
This commit is contained in:
parent
644519f358
commit
7ec0a36d7a
4 changed files with 179 additions and 4 deletions
|
|
@ -397,6 +397,69 @@ pub async fn commit_topology(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Batch variant of [`commit_topology`]: applies every `(child, new_parent)`
|
||||
/// move under a single `META_LOCK` acquisition and creates **one** git commit
|
||||
/// for all of them. Moves are applied in the order given; the first
|
||||
/// validation error short-circuits the whole batch (no partial writes because
|
||||
/// `topology::set_parent` rewrites the in-memory map atomically and we only
|
||||
/// flush to disk after all moves have passed validation).
|
||||
///
|
||||
/// Returns a `Vec` of `(child, old_parent)` pairs for every move that
|
||||
/// actually changed the topology (idempotent same-parent moves are skipped),
|
||||
/// so the caller can send targeted notifications.
|
||||
pub async fn bulk_commit_topology(
|
||||
moves: &[(&str, Option<&str>)],
|
||||
) -> std::result::Result<Vec<(String, Option<String>)>, String> {
|
||||
if moves.is_empty() {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let _guard = META_LOCK.lock().await;
|
||||
// Snapshot parents before any writes so we can compute the diff.
|
||||
let topo_before = crate::topology::read();
|
||||
// Validate + apply all moves in sequence; first error aborts.
|
||||
for (child, new_parent) in moves {
|
||||
crate::topology::set_parent(child, *new_parent)?;
|
||||
}
|
||||
// Commit the whole batch as one git operation.
|
||||
let dir = meta_dir();
|
||||
let commit_msg = if moves.len() == 1 {
|
||||
let (child, new_parent) = moves[0];
|
||||
format!("topology: {} → {}", child, new_parent.unwrap_or("<root>"))
|
||||
} else {
|
||||
let names: Vec<&str> = moves.iter().map(|(c, _)| *c).collect();
|
||||
let dest = moves[0].1.unwrap_or("<root>");
|
||||
format!(
|
||||
"topology: move {} agents → {} ({})",
|
||||
moves.len(),
|
||||
dest,
|
||||
names.join(", ")
|
||||
)
|
||||
};
|
||||
let stage = async {
|
||||
git(&dir, &["add", "topology.json"]).await?;
|
||||
if has_staged_changes(&dir).await? {
|
||||
git_commit(&dir, &commit_msg).await?;
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
};
|
||||
if let Err(e) = stage.await {
|
||||
tracing::warn!(error = ?e, "bulk_commit_topology: topology written but git commit failed (sync_agents will recover)");
|
||||
}
|
||||
// Return (child, old_parent) for each move that changed state.
|
||||
let changed = moves
|
||||
.iter()
|
||||
.filter_map(|(child, new_parent)| {
|
||||
let old = topo_before.get(*child).cloned().flatten();
|
||||
if old.as_deref() != *new_parent {
|
||||
Some((child.to_string(), old))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(changed)
|
||||
}
|
||||
|
||||
fn render_flake(
|
||||
hyperhive_flake: &str,
|
||||
nixpkgs_flake: &str,
|
||||
|
|
|
|||
Loading…
Reference in a new issue