diff options
| author | tv <tv@krebsco.de> | 2026-11-03 01:19:05 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-03 01:19:05 +0100 |
| commit | c12ce1a67e387474d17419bb252211bf04b2e443 (patch) | |
| tree | 61157a186b9a7c884859e53e8cdea3155e92ded5 /src/swarm | |
| parent | 72d05e54de4a051148578d8445ad4527550052ff (diff) | |
always announce new heads
Diffstat (limited to 'src/swarm')
| -rw-r--r-- | src/swarm/relay.rs | 42 | ||||
| -rw-r--r-- | src/swarm/topology/mod.rs | 12 |
2 files changed, 9 insertions, 45 deletions
diff --git a/src/swarm/relay.rs b/src/swarm/relay.rs index 5ab93f2..1393d50 100644 --- a/src/swarm/relay.rs +++ b/src/swarm/relay.rs @@ -16,10 +16,11 @@ //! message would announce to every neighbour, and announcements would outnumber the //! data they announce (about 15 to 1 at 250 nodes). //! -//! In a full mesh the writer reaches everyone itself, so nothing is announced, but to -//! the neighbours the writer can't reach: those on none of its transports (a member on -//! Tor alone, reached through one on Tor and libp2p, from a writer on libp2p alone). -//! Whom it reaches is what the member list makes its neighbours (`topology::neighbours_of`). +//! In a full mesh too: the member list makes every member the writer's neighbour, but +//! that says nothing about whether the writer reaches them. One behind NAT may reach a +//! single one of them, one on another transport (on Tor alone) none, and no node can +//! tell which from where it is. A neighbour that has the changes already does nothing +//! with an announcement but answer it. use std::collections::{HashMap, HashSet}; use std::sync::atomic::Ordering; @@ -44,15 +45,7 @@ const ANNOUNCE_DELAY: Duration = Duration::from_millis(200); /// After changes from `from` were applied and our heads changed: tell our other /// neighbours, in the next announcement round. pub(crate) async fn announce(state: &AppState, from: &str) { - // While the view is unknown everyone counts as a neighbour, but that isn't a full - // mesh: the others may not hear from the writer. - if !state.initial_sync_done.load(Ordering::Acquire) - || (topology::view_known(state) - && topology::is_full_mesh(state) - && unreached(state, &HashSet::from([from.to_owned()])) - .await - .is_empty()) - { + if !state.initial_sync_done.load(Ordering::Acquire) { return; } let schedule = { @@ -101,38 +94,19 @@ async fn announce_now(state: &AppState, senders: &HashSet<String>) { /// The neighbours to tell of the changes `senders` sent us. The peer whose changes we /// announce has them already, so with a single sender it is skipped; with several, each -/// lacks the others' changes. In a full mesh, also those every sender is a neighbour of: -/// the writers reached them themselves. +/// lacks the others' changes. async fn unreached(state: &AppState, senders: &HashSet<String>) -> Vec<String> { let skip = match senders.len() { 1 => senders.iter().next().cloned(), _ => None, }; - let full_mesh = topology::view_known(state) && topology::is_full_mesh(state); - // Unknown (a sender not in the member list): nobody counts as reached by it. - let reached: Vec<Option<std::collections::BTreeSet<String>>> = if full_mesh { - senders - .iter() - .map(|sender| topology::neighbours_of(state, sender)) - .collect() - } else { - Vec::new() - }; - let reached_by_all = |peer: &str| { - full_mesh - && reached.iter().zip(senders).all(|(neighbours, sender)| { - sender == peer || neighbours.as_ref().is_some_and(|n| n.contains(peer)) - }) - }; state .peer_states .read() .await .keys() .filter(|peer| { - skip.as_deref() != Some(peer.as_str()) - && topology::is_neighbour(state, peer) - && !reached_by_all(peer) + skip.as_deref() != Some(peer.as_str()) && topology::is_neighbour(state, peer) }) .cloned() .collect() diff --git a/src/swarm/topology/mod.rs b/src/swarm/topology/mod.rs index df75f46..e3479e3 100644 --- a/src/swarm/topology/mod.rs +++ b/src/swarm/topology/mod.rs @@ -67,6 +67,7 @@ pub(crate) fn view_known(state: &AppState) -> bool { } /// Whether every reachable member is our neighbour (also while the view is unknown). +#[cfg(test)] pub(crate) fn is_full_mesh(state: &AppState) -> bool { state .neighbours @@ -141,17 +142,6 @@ pub(crate) fn intended_graph(state: &AppState) -> Option<BTreeMap<String, BTreeS Some(graph) } -/// `member`'s neighbours as the member list makes them, as this node sees it (as in -/// `intended_graph`); None until the view is known, or for one the list doesn't have. -pub(crate) fn neighbours_of(state: &AppState, member: &str) -> Option<BTreeSet<String>> { - let view = state.neighbours.read_or_recover(); - let view = view.as_ref()?; - view.members - .iter() - .any(|listed| listed == member) - .then(|| view.neighbours_of(state.local_id(), member)) -} - /// The transports `member` is on, by the member list (the schemes of its addresses); /// ours as we are on them. None known: empty. pub(crate) fn transports_of(state: &AppState, member: &str) -> BTreeSet<String> { |
