node: keep a healthy connection to the network
Try having a number of outbound connections to other Radicle nodes at any given time. The number has been defined earlier as TARGET_OUTBOUND_PEERS. All network nodes are treated equally, there is no preference. Attempts to keep connected happen at set intervals (IDLE_INTERVAL) or when a node disconnects. Signed-off-by: Slack Coder <slackcoder@server.ky>
This commit is contained in:
parent
ff045e0813
commit
f3d867af4d
|
|
@ -945,14 +945,66 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maintain_connections(&mut self) {
|
fn next_connections(
|
||||||
// TODO: Connect to all potential seeds.
|
rng: &Rng,
|
||||||
if self.sessions.len() < TARGET_OUTBOUND_PEERS {
|
sessions: &Sessions,
|
||||||
let delta = TARGET_OUTBOUND_PEERS - self.sessions.len();
|
address_pool: &mut dyn Iterator<Item = (NodeId, address::KnownAddress)>,
|
||||||
|
) -> Vec<Address> {
|
||||||
for _ in 0..delta {
|
let mut initializing: Vec<Address> = Vec::new();
|
||||||
// TODO: Connect to random peer.
|
let mut negotiated: HashMap<NodeId, &Session> = HashMap::new();
|
||||||
|
for (_, s) in sessions.iter() {
|
||||||
|
if s.link != Link::Outbound {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
match s.state {
|
||||||
|
session::State::Initial => {
|
||||||
|
initializing.push(s.addr.into());
|
||||||
|
}
|
||||||
|
session::State::Negotiated { .. } => {
|
||||||
|
let node_id = s
|
||||||
|
.node_id()
|
||||||
|
.expect("negotiated sessions must have a node ID");
|
||||||
|
negotiated.insert(node_id, s);
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let wanted = TARGET_OUTBOUND_PEERS
|
||||||
|
.saturating_sub(initializing.len())
|
||||||
|
.saturating_sub(negotiated.len());
|
||||||
|
if wanted == 0 {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
// All nodes are considered equal
|
||||||
|
let mut address_pool: Vec<_> = address_pool
|
||||||
|
.filter(|(node_id, s)| {
|
||||||
|
!initializing.contains(&s.addr) && !negotiated.contains_key(node_id)
|
||||||
|
})
|
||||||
|
.take(wanted)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut next = Vec::new();
|
||||||
|
loop {
|
||||||
|
if address_pool.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let i = rng.usize(0..address_pool.len());
|
||||||
|
let (_, addr) = address_pool.swap_remove(i);
|
||||||
|
next.push(addr.addr);
|
||||||
|
}
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
fn maintain_connections(&mut self) {
|
||||||
|
let mut address_pool = self
|
||||||
|
.addresses
|
||||||
|
.entries()
|
||||||
|
.expect("address store be accessible");
|
||||||
|
let next = Self::next_connections(&self.rng, &self.sessions, &mut address_pool);
|
||||||
|
for addr in next {
|
||||||
|
self.reactor.connect(addr.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -648,6 +648,53 @@ fn test_persistent_peer_reconnect() {
|
||||||
assert_matches!(alice.outbox().next(), None);
|
assert_matches!(alice.outbox().next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_maintain_connections() {
|
||||||
|
let mut alice = Peer::new("alice", [7, 7, 7, 7], MockStorage::empty());
|
||||||
|
|
||||||
|
let connected = vec![
|
||||||
|
Peer::new("connected 1", [8, 8, 8, 1], MockStorage::empty()),
|
||||||
|
Peer::new("connected 2", [8, 8, 8, 2], MockStorage::empty()),
|
||||||
|
Peer::new("connected 3", [8, 8, 8, 3], MockStorage::empty()),
|
||||||
|
];
|
||||||
|
let mut unconnected = vec![
|
||||||
|
Peer::new("new 1", [9, 9, 9, 1], MockStorage::empty()),
|
||||||
|
Peer::new("new 2", [9, 9, 9, 2], MockStorage::empty()),
|
||||||
|
Peer::new("new 3", [9, 9, 9, 3], MockStorage::empty()),
|
||||||
|
];
|
||||||
|
|
||||||
|
for peer in connected.iter() {
|
||||||
|
alice.connect_to(peer);
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
connected.len(),
|
||||||
|
alice.sessions().len(),
|
||||||
|
"alice should be connected to all peers"
|
||||||
|
);
|
||||||
|
|
||||||
|
for peer in unconnected.iter() {
|
||||||
|
alice.receive(&connected[0].addr(), peer.node_announcement());
|
||||||
|
}
|
||||||
|
|
||||||
|
for peer in connected.iter() {
|
||||||
|
alice.disconnected(
|
||||||
|
&peer.addr(),
|
||||||
|
&nakamoto::DisconnectReason::Protocol(DisconnectReason::User),
|
||||||
|
);
|
||||||
|
|
||||||
|
let addr = alice
|
||||||
|
.outbox()
|
||||||
|
.find_map(|o| match o {
|
||||||
|
Io::Connect(addr) => Some(addr),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("Alice connects to a new peer");
|
||||||
|
assert!(addr != peer.addr());
|
||||||
|
unconnected.retain(|p| p.addr() != addr);
|
||||||
|
}
|
||||||
|
assert!(unconnected.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_and_pull() {
|
fn test_push_and_pull() {
|
||||||
let tempdir = tempfile::tempdir().unwrap();
|
let tempdir = tempfile::tempdir().unwrap();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue