aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/hyparview.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/hyparview.rs')
-rw-r--r--src/swarm/hyparview.rs38
1 files changed, 37 insertions, 1 deletions
diff --git a/src/swarm/hyparview.rs b/src/swarm/hyparview.rs
index 17d36b8..004f528 100644
--- a/src/swarm/hyparview.rs
+++ b/src/swarm/hyparview.rs
@@ -7,7 +7,9 @@
//! link both ends agreed to (NEIGHBOUR, answered by NEIGHBOUR_REPLY), not one every
//! node computes alike. A node with fewer asks others from its passive view, one at a
//! time per missing neighbour; one that has none asks with high priority, and is taken
-//! even where that means dropping another (told so: DISCONNECT). A neighbour that fails
+//! even where that means dropping another (told so: DISCONNECT). One left with none
+//! (dropped so itself) while its asks are low ones has one of them, or another, made
+//! high at once, even of a member that refused before. A neighbour that fails
//! (judged unreachable: unanswered HELLOs, dormant, not alive lately) leaves the active
//! view, and another is asked. Every neighbour is asked again now and then whether it
//! still is (`REAFFIRM_EVERY`): the paper's links are TCP connections, whose ends know
@@ -282,6 +284,40 @@ impl Topology for HyParView {
changed = true;
}
}
+ // No neighbour, and no ask that would make room for us (those sent while
+ // we had some, low, refused by full members, each refused for a while):
+ // one high, an ask not sent yet made so, or another, whoever refused before
+ // if nobody else is left.
+ if views.active.is_empty() && !views.asking.values().any(|asking| asking.high) {
+ let unsent = views
+ .asking
+ .iter()
+ .find(|(_, asking)| !asking.sent)
+ .map(|(member, _)| member.clone());
+ let member = unsent.or_else(|| {
+ let passive = Self::passive_view(&views, &reachable);
+ let anyone: Vec<String> = if passive.is_empty() {
+ reachable
+ .iter()
+ .filter(|member| !views.asking.contains_key(*member))
+ .cloned()
+ .collect()
+ } else {
+ passive
+ };
+ some_of(anyone, 1).pop()
+ });
+ if let Some(member) = member {
+ views.refused.remove(&member);
+ let asking = views.asking.entry(member).or_insert(Asking {
+ since: now,
+ high: true,
+ sent: false,
+ });
+ asking.high = true;
+ changed = true;
+ }
+ }
let mut to_ask: Vec<(String, bool)> = views
.asking
.iter()