fix: true atomic bulk topology -- apply_set_parent + single write, add # Errors docs
This commit is contained in:
parent
7ec0a36d7a
commit
84383568b2
2 changed files with 30 additions and 6 deletions
|
|
@ -719,7 +719,12 @@ impl Coordinator {
|
|||
/// Batch version of [`reparent_with_notify`]: applies all moves under a
|
||||
/// single `META_LOCK` acquisition (one git commit) then sends per-agent
|
||||
/// notifications for each move that actually changed topology.
|
||||
/// First validation failure aborts the whole batch.
|
||||
/// First validation failure aborts the whole batch with no disk writes.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Propagates any error returned by [`crate::meta::bulk_commit_topology`]
|
||||
/// (validation failure or topology-file write error).
|
||||
pub async fn reparent_bulk_with_notify(
|
||||
self: &Arc<Self>,
|
||||
moves: &[(&str, Option<&str>)],
|
||||
|
|
|
|||
|
|
@ -400,13 +400,26 @@ pub async fn commit_topology(
|
|||
/// 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).
|
||||
/// validation error short-circuits the whole batch. True atomic write: all
|
||||
/// moves are pre-validated against a cumulative in-memory state with
|
||||
/// [`crate::topology::apply_set_parent`] before anything touches disk, then
|
||||
/// [`crate::topology::write`] is called exactly once. If any move fails
|
||||
/// validation the topology file is never modified.
|
||||
///
|
||||
/// The multi-move commit message uses `moves[0].1` as the destination label.
|
||||
/// This is intentional: the dashboard bulk-move UI always sends a single
|
||||
/// destination for all selected agents, so the message is always accurate in
|
||||
/// practice.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `String` error if any move fails validation (cycle, unknown
|
||||
/// agent, etc.) or if the topology file cannot be written. Git-commit failure
|
||||
/// is logged as a warning and does not propagate — `sync_agents` will recover.
|
||||
pub async fn bulk_commit_topology(
|
||||
moves: &[(&str, Option<&str>)],
|
||||
) -> std::result::Result<Vec<(String, Option<String>)>, String> {
|
||||
|
|
@ -416,9 +429,15 @@ pub async fn bulk_commit_topology(
|
|||
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.
|
||||
// Validate all moves against a cumulative in-memory state -- no disk
|
||||
// writes yet; first error aborts with the topology file untouched.
|
||||
let mut next = topo_before.clone();
|
||||
for (child, new_parent) in moves {
|
||||
crate::topology::set_parent(child, *new_parent)?;
|
||||
next = crate::topology::apply_set_parent(&next, child, *new_parent)?;
|
||||
}
|
||||
// Only flush to disk if something actually changed.
|
||||
if next != topo_before {
|
||||
crate::topology::write(&next).map_err(|e| format!("{e:#}"))?;
|
||||
}
|
||||
// Commit the whole batch as one git operation.
|
||||
let dir = meta_dir();
|
||||
|
|
|
|||
Loading…
Reference in a new issue