use crate::routing::XorName;
use crate::messaging::data::OperationId;
use dashmap::DashMap;
use itertools::Itertools;
use std::collections::BTreeSet;
use std::sync::Arc;
use tokio::sync::RwLock;
const NEIGHBOUR_COUNT: usize = 2;
const MIN_PENDING_OPS: usize = 10;
const PENDING_OP_TOLERANCE_RATIO: f64 = 0.1;
type NodeIdentifier = XorName;
#[derive(Clone, Debug)]
pub(crate) struct Liveness {
unfulfilled_requests: Arc<DashMap<NodeIdentifier, Arc<RwLock<Vec<OperationId>>>>>,
closest_nodes_to: Arc<DashMap<XorName, Vec<XorName>>>,
}
impl Liveness {
pub(crate) fn new() -> Self {
Self {
unfulfilled_requests: Arc::new(DashMap::new()),
closest_nodes_to: Arc::new(DashMap::new()),
}
}
pub(crate) async fn add_a_pending_request_operation(
&self,
node_id: NodeIdentifier,
operation_id: OperationId,
) {
let entry = self.unfulfilled_requests.entry(node_id).or_default();
trace!(
"Adding pending operation against node: {:?}: for op: {:?}",
node_id,
&operation_id
);
let v = entry.value();
v.write().await.push(operation_id);
}
pub(crate) fn retain_members_only(&self, current_members: BTreeSet<XorName>) {
let all_keys: Vec<_> = self
.closest_nodes_to
.iter()
.map(|entry| *entry.key())
.collect();
for key in &all_keys {
if !current_members.contains(key) {
let _prev = self.unfulfilled_requests.remove(key);
let _prev = self.closest_nodes_to.remove(key);
}
}
self.recompute_closest_nodes();
}
pub(crate) async fn request_operation_fulfilled(
&self,
node_id: &NodeIdentifier,
operation_id: OperationId,
) -> bool {
trace!(
"Attempting to remove pending_operation {:?} op: {:?}",
node_id,
operation_id
);
let mut has_removed = false;
if let Some(entry) = self.unfulfilled_requests.get(node_id) {
let v = entry.value();
v.write().await.retain(|x| {
if has_removed || x != &operation_id {
true
} else {
has_removed = true;
false
}
});
if has_removed {
trace!(
"Pending operation removed for node: {:?} op: {:?}",
node_id,
operation_id
);
} else {
trace!(
"No Pending operation find for node: {:?} op: {:?}",
node_id,
operation_id
);
}
}
has_removed
}
pub(crate) fn recompute_closest_nodes(&self) {
let all_known_nodes: Vec<_> = self
.closest_nodes_to
.iter()
.map(|entry| *entry.key())
.collect();
self.closest_nodes_to.alter_all(|name, _| {
all_known_nodes
.iter()
.filter(|&key| key != name)
.sorted_by(|lhs, rhs| name.cmp_distance(lhs, rhs))
.take(NEIGHBOUR_COUNT)
.copied()
.collect::<Vec<_>>()
});
}
pub(crate) async fn find_unresponsive_nodes(&self) -> Vec<(XorName, usize)> {
let mut unresponsive_nodes = Vec::new();
for entry in self.closest_nodes_to.iter() {
let (node, neighbours) = entry.pair();
let node = *node;
let mut max_pending_by_neighbours = 0;
for neighbour in neighbours.iter() {
if let Some(entry) = self.unfulfilled_requests.get(neighbour) {
let val = entry.value().read().await.len();
if val > max_pending_by_neighbours {
max_pending_by_neighbours = val
}
}
}
let pending_operations_count = if let Some(entry) = self.unfulfilled_requests.get(&node)
{
entry.value().read().await.len()
} else {
0
};
if pending_operations_count > MIN_PENDING_OPS
&& max_pending_by_neighbours > MIN_PENDING_OPS
&& pending_operations_count as f64 * PENDING_OP_TOLERANCE_RATIO
> max_pending_by_neighbours as f64
{
tracing::info!(
"Pending ops for {}: {} Neighbour max: {}",
node,
pending_operations_count,
max_pending_by_neighbours
);
unresponsive_nodes.push((node, pending_operations_count));
}
}
unresponsive_nodes
}
}