add keepalives for route nodes

This commit is contained in:
John Smith
2022-04-07 09:55:09 -04:00
parent fe1754b84b
commit f7873aba88
9 changed files with 267 additions and 27 deletions

View File

@@ -19,6 +19,11 @@ const RELIABLE_PING_INTERVAL_MULTIPLIER: f64 = 2.0;
const UNRELIABLE_PING_SPAN_SECS: u32 = 60;
const UNRELIABLE_PING_INTERVAL_SECS: u32 = 5;
// Keepalive pings are done occasionally to ensure holepunched public dialinfo
// remains valid, as well as to make sure we remain in any relay node's routing table
const KEEPALIVE_PING_INTERVAL_SECS: u32 = 30;
// Do not change order here, it will mess up other sorts
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum BucketEntryState {
Dead,
@@ -60,6 +65,46 @@ impl BucketEntry {
}
}
pub fn sort_fastest(e1: &Self, e2: &Self) -> std::cmp::Ordering {
// Lower latency to the front
if let Some(e1_latency) = &e1.peer_stats.latency {
if let Some(e2_latency) = &e2.peer_stats.latency {
e1_latency.average.cmp(&e2_latency.average)
} else {
std::cmp::Ordering::Less
}
} else if e2.peer_stats.latency.is_some() {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
pub fn cmp_fastest_reliable(cur_ts: u64, e1: &Self, e2: &Self) -> std::cmp::Ordering {
// Reverse compare so most reliable is at front
let ret = e2.state(cur_ts).cmp(&e1.state(cur_ts));
if ret != std::cmp::Ordering::Equal {
return ret;
}
// Lower latency to the front
if let Some(e1_latency) = &e1.peer_stats.latency {
if let Some(e2_latency) = &e2.peer_stats.latency {
e1_latency.average.cmp(&e2_latency.average)
} else {
std::cmp::Ordering::Less
}
} else if e2.peer_stats.latency.is_some() {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Equal
}
}
pub fn sort_fastest_reliable_fn(cur_ts: u64) -> impl FnMut(&Self, &Self) -> std::cmp::Ordering {
move |e1, e2| Self::cmp_fastest_reliable(cur_ts, e1, e2)
}
pub fn update_dial_infos(&mut self, dial_infos: &[DialInfo]) {
self.dial_infos = dial_infos.to_vec();
self.dial_infos.sort();
@@ -185,15 +230,35 @@ impl BucketEntry {
}
}
// Check if this node needs a ping right now to validate it is still reachable
pub(super) fn needs_ping(&self, cur_ts: u64) -> bool {
// See which ping pattern we are to use
let mut state = self.state(cur_ts);
fn needs_constant_ping(&self, cur_ts: u64, interval: u64) -> bool {
match self.peer_stats.ping_stats.last_pinged {
None => true,
Some(last_pinged) => cur_ts.saturating_sub(last_pinged) >= (interval * 1000000u64),
}
}
// If the current dial info hasn't been recognized,
// then we gotta ping regardless so treat the node as unreliable, briefly
// Check if this node needs a ping right now to validate it is still reachable
pub(super) fn needs_ping(
&self,
routing_table: RoutingTable,
node_id: &DHTKey,
cur_ts: u64,
) -> bool {
let netman = routing_table.network_manager();
let relay_node = netman.relay_node();
// See which ping pattern we are to use
let state = self.state(cur_ts);
// If the current dial info hasn't been recognized then we gotta ping regardless
if !self.seen_our_dial_info && matches!(state, BucketEntryState::Reliable) {
state = BucketEntryState::Unreliable;
return self.needs_constant_ping(cur_ts, UNRELIABLE_PING_INTERVAL_SECS as u64);
}
// If this entry is our relay node, then we should ping it regularly
else if let Some(relay_node) = relay_node {
if relay_node.node_id() == *node_id {
return self.needs_constant_ping(cur_ts, KEEPALIVE_PING_INTERVAL_SECS as u64);
}
}
match state {
@@ -225,13 +290,7 @@ impl BucketEntry {
}
BucketEntryState::Unreliable => {
// If we are in an unreliable state, we need a ping every UNRELIABLE_PING_INTERVAL_SECS seconds
match self.peer_stats.ping_stats.last_pinged {
None => true,
Some(last_pinged) => {
cur_ts.saturating_sub(last_pinged)
>= (UNRELIABLE_PING_INTERVAL_SECS as u64 * 1000000u64)
}
}
self.needs_constant_ping(cur_ts, UNRELIABLE_PING_INTERVAL_SECS as u64)
}
BucketEntryState::Dead => false,
}

View File

@@ -477,6 +477,38 @@ impl RoutingTable {
f(entry)
}
pub fn find_inbound_relay(&self, cur_ts: u64) -> Option<NodeRef> {
let mut inner = self.inner.lock();
let mut best_inbound_relay: Option<NodeRef> = None;
// Iterate all known nodes for candidates
for b in &mut inner.buckets {
for (k, entry) in b.entries_mut() {
// Ensure it's not dead
if !matches!(entry.state(cur_ts), BucketEntryState::Dead) {
// Ensure we have a node info
if let Some(node_info) = &entry.peer_stats().node_info {
// Ensure network class can relay
if node_info.network_class.can_inbound_relay() {
if let Some(best_inbound_relay) = best_inbound_relay.as_mut() {
if best_inbound_relay.operate(|best| {
BucketEntry::cmp_fastest_reliable(cur_ts, best, entry)
}) == std::cmp::Ordering::Greater
{
*best_inbound_relay = NodeRef::new(self.clone(), *k, entry);
}
} else {
best_inbound_relay = Some(NodeRef::new(self.clone(), *k, entry));
}
}
}
}
}
}
best_inbound_relay
}
pub async fn find_self(&self, node_ref: NodeRef) -> Result<Vec<NodeRef>, String> {
let node_id = self.node_id();
let rpc_processor = self.rpc_processor();
@@ -635,7 +667,7 @@ impl RoutingTable {
let mut inner = self.inner.lock();
for b in &mut inner.buckets {
for (k, entry) in b.entries_mut() {
if entry.needs_ping(cur_ts) {
if entry.needs_ping(self.clone(), k, cur_ts) {
let nr = NodeRef::new(self.clone(), *k, entry);
log_rtab!(
" --- ping validating: {:?} ({})",
@@ -690,6 +722,8 @@ impl RoutingTable {
// Ping validate some nodes to groom the table
self.unlocked_inner.ping_validator_task.tick().await?;
// Keepalive
Ok(())
}