aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/reach.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/reach.rs')
-rw-r--r--src/swarm/reach.rs61
1 files changed, 46 insertions, 15 deletions
diff --git a/src/swarm/reach.rs b/src/swarm/reach.rs
index 8c22ef6..e6cee2f 100644
--- a/src/swarm/reach.rs
+++ b/src/swarm/reach.rs
@@ -7,7 +7,8 @@
//! `Timing::unreached_after`, asked again halfway, with nothing else heard from it; a
//! member gone shows within seconds, not after `dormant_after`). It tells its
//! neighbours so in its STATUS (`Status::reaches`, each member by its pair key,
-//! `topology::pair_key`, 8 bytes instead of a node ID): in every keepalive STATUS and
+//! `topology::pair_key`, 8 bytes instead of a node ID, with its round trip to each as a
+//! bucket: `Status::round_trips`, `rtt::bucket`): in every keepalive STATUS and
//! STATUS_REPLY, and soon to every neighbour when it changed (the peer worker looks
//! every 5 s, and at once when a member we reach stops answering), but not in
//! announcements of heads, which go often. So every node knows whom its neighbours
@@ -32,17 +33,20 @@ use tracing::debug;
use crate::locks::MutexExt;
use crate::state::{AppState, HelloState, Standing};
use crate::swarm::peer::announce_heads;
-use crate::swarm::{members, topology};
+use crate::swarm::{members, rtt, topology};
-/// Whom we reach, as last told our neighbours.
+/// Whom we reach, as last told our neighbours, and our round trip to each.
#[derive(Default)]
pub(crate) struct Reach {
published: Option<Vec<u64>>,
+ /// One per member in `published`, in its order (`rtt::bucket`).
+ round_trips: Vec<u8>,
}
/// The pair keys of the members we reach now, sorted: those with an active session
-/// that answer.
-async fn measure(state: &AppState) -> Vec<u64> {
+/// that answer; and our round trip to each, as a bucket, held near a bound to the one
+/// told before (`before`, by pair key: `rtt::bucket`).
+async fn measure(state: &AppState, before: &HashMap<u64, u8>) -> (Vec<u64>, Vec<u8>) {
let now = Instant::now();
let mut reached = Vec::new();
for (peer, peer_state) in state.peers_snapshot().await {
@@ -53,21 +57,37 @@ async fn measure(state: &AppState) -> Vec<u64> {
&& !peer_state.answer_overdue(now)
&& matches!(peer_state.standing, Standing::Member { .. })
{
- reached.push(topology::pair_key(&peer));
+ reached.push((topology::pair_key(&peer), peer));
}
}
reached.sort_unstable();
- reached
+ let rtts = state.lock_rtt();
+ let round_trips = reached
+ .iter()
+ .map(|(key, peer)| {
+ let rtt = rtts.effective(peer, now, state.timing.rtt_fresh_for());
+ rtt::bucket(rtt, before.get(key).copied())
+ })
+ .collect();
+ (
+ reached.into_iter().map(|(key, _)| key).collect(),
+ round_trips,
+ )
}
/// Whom we reach, as we tell it (`Status::reaches`); empty until first measured.
pub(crate) fn published(state: &AppState) -> Vec<u64> {
- state
- .reach
- .lock_or_recover()
- .published
- .clone()
- .unwrap_or_default()
+ published_with_round_trips(state).0
+}
+
+/// Whom we reach and our round trip to each, as we tell it (`Status::reaches`,
+/// `Status::round_trips`); empty until first measured.
+pub(crate) fn published_with_round_trips(state: &AppState) -> (Vec<u64>, Vec<u8>) {
+ let reach = state.reach.lock_or_recover();
+ match &reach.published {
+ Some(keys) => (keys.clone(), reach.round_trips.clone()),
+ None => (Vec::new(), Vec::new()),
+ }
}
/// With the peer worker's periodic pass (every 5 s), and at once when a member we said
@@ -75,13 +95,24 @@ pub(crate) fn published(state: &AppState) -> Vec<u64> {
/// our neighbours are told at once (a STATUS each, unanswered unless they lack some of
/// our heads). A change that is undone within a pass isn't told.
pub(crate) async fn check(state: &AppState) {
- let now = measure(state).await;
+ let before: HashMap<u64, u8> = {
+ let reach = state.reach.lock_or_recover();
+ reach
+ .published
+ .iter()
+ .flatten()
+ .copied()
+ .zip(reach.round_trips.iter().copied())
+ .collect()
+ };
+ let (now, round_trips) = measure(state, &before).await;
{
let mut reach = state.reach.lock_or_recover();
- if reach.published.as_ref() == Some(&now) {
+ if reach.published.as_ref() == Some(&now) && reach.round_trips == round_trips {
return;
}
reach.published = Some(now.clone());
+ reach.round_trips = round_trips;
}
debug!(
reached = now.len(),