From b140d8580fb00e22e08fb5b9a755fcf13fe3cf74 Mon Sep 17 00:00:00 2001 From: cloudhead Date: Fri, 17 Nov 2023 17:22:44 +0100 Subject: [PATCH] cli: Don't announce if already in sync In this change, we check whether we are in sync with a node before announcing refs to it. --- radicle-cli/examples/rad-sync.md | 6 ++--- radicle-cli/src/commands/sync.rs | 44 +++++++++++++++++++++++++++----- radicle/src/node.rs | 31 +++++++++++++++------- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/radicle-cli/examples/rad-sync.md b/radicle-cli/examples/rad-sync.md index 4b1e0f77..b492de30 100644 --- a/radicle-cli/examples/rad-sync.md +++ b/radicle-cli/examples/rad-sync.md @@ -28,11 +28,11 @@ $ rad sync --announce ``` If we try to sync again after the nodes have synced, we will already -be up to date and so we will see the same message. +be up to date. ``` $ rad sync --announce -✓ Synced with 2 node(s) +✓ Nothing to announce, already in sync with network (see `rad sync status`) ``` We can also use the `--fetch` option to only fetch objects: @@ -51,7 +51,7 @@ $ rad sync --fetch --announce ✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z.. ✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. ✓ Fetched repository from 2 seed(s) -✓ Synced with 2 node(s) +✓ Nothing to announce, already in sync with network (see `rad sync status`) ``` It's also possible to use the `--seed` flag to only sync with a specific node: diff --git a/radicle-cli/src/commands/sync.rs b/radicle-cli/src/commands/sync.rs index 7de8c26e..2acc2a74 100644 --- a/radicle-cli/src/commands/sync.rs +++ b/radicle-cli/src/commands/sync.rs @@ -328,16 +328,46 @@ fn sync_status(rid: Id, node: &mut Node) -> anyhow::Result<()> { fn announce_refs( rid: Id, - _mode: RepoSync, + mode: RepoSync, timeout: time::Duration, mut node: Node, profile: &Profile, ) -> anyhow::Result<()> { let repo = profile.storage.repository(rid)?; let doc = repo.identity_doc()?; - let connected: Vec<_> = if doc.visibility.is_public() { + let unsynced: Vec<_> = if doc.visibility.is_public() { let seeds = node.seeds(rid)?; - seeds.connected().map(|s| s.nid).collect() + let synced = seeds.iter().filter(|s| s.is_synced()); + + match mode { + RepoSync::Seeds(seeds) => { + let synced = synced.map(|s| s.nid).collect::>(); + if seeds.iter().all(|s| synced.contains(s)) { + term::success!( + "Already in sync with the specified seed(s) (see `rad sync status`)" + ); + return Ok(()); + } + } + RepoSync::Replicas(replicas) => { + let count = synced.count(); + if count >= seeds.len() { + term::success!( + "Nothing to announce, already in sync with network (see `rad sync status`)" + ); + return Ok(()); + } + if count >= replicas { + term::success!("Nothing to announce, already in sync with {count} seed(s) (see `rad sync status`)"); + return Ok(()); + } + } + } + seeds + .connected() + .filter(|s| !s.is_synced()) + .map(|s| s.nid) + .collect() } else { node.sessions()? .into_iter() @@ -346,13 +376,13 @@ fn announce_refs( .collect() }; - if connected.is_empty() { - term::info!("Not connected to any seeds."); + if unsynced.is_empty() { + term::info!("Not connected to any seeds for {rid}."); return Ok(()); } - let mut spinner = term::spinner(format!("Syncing with {} node(s)..", connected.len())); - let result = node.announce(rid, connected, timeout, |event| match event { + let mut spinner = term::spinner(format!("Syncing with {} node(s)..", unsynced.len())); + let result = node.announce(rid, unsynced, timeout, |event| match event { node::AnnounceEvent::Announced => {} node::AnnounceEvent::RefsSynced { remote } => { spinner.message(format!("Synced with {remote}..")); diff --git a/radicle/src/node.rs b/radicle/src/node.rs index 5f300ca0..81483e14 100644 --- a/radicle/src/node.rs +++ b/radicle/src/node.rs @@ -460,6 +460,11 @@ impl Seed { matches!(self.state, Some(State::Connected { .. })) } + /// Check if this seed is in sync with us. + pub fn is_synced(&self) -> bool { + matches!(self.sync, Some(SyncStatus::Synced { .. })) + } + pub fn new( nid: NodeId, addrs: Vec, @@ -497,22 +502,30 @@ impl Seeds { self.0.contains_key(nid) } + /// Number of seeds. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Check if there are any seeds. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Partitions the list of seeds into connected and disconnected seeds. /// Note that the disconnected seeds may be in a "connecting" state. pub fn partition(&self) -> (Vec, Vec) { - self.0 - .shuffled() - .map(|(_, v)| v) - .cloned() - .partition(|s| s.is_connected()) + self.iter().cloned().partition(|s| s.is_connected()) } /// Return connected seeds. pub fn connected(&self) -> impl Iterator { - self.0 - .shuffled() - .map(|(_, v)| v) - .filter(|s| s.is_connected()) + self.iter().filter(|s| s.is_connected()) + } + + /// Return all seeds. + pub fn iter(&self) -> impl Iterator { + self.0.shuffled().map(|(_, v)| v) } /// Check if a seed is connected.