use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::{BuildHasher, Hash, Hasher};
use std::ops::RangeInclusive;
#[cfg(feature = "derive")]
use serde::{Deserialize, Serialize};
use super::HashRing;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
pub struct Replicas<T> {
pub hash_range: RangeInclusive<u64>,
pub nodes: Vec<T>,
}
impl<T> HashRing<T>
where
T: Hash + Clone + Debug + PartialEq,
{
pub fn get_hash_ranges(&self) -> Vec<Replicas<T>> {
if self.len() == 1 {
return vec![Replicas {
hash_range: 0..=u64::MAX,
nodes: vec![self.ring.first().unwrap().node.clone()],
}];
}
let mut replication_setup = vec![];
let mut left = match self.ring.last() {
Some(left) => left.clone(),
None => {
return replication_setup;
}
};
for right in self.ring.iter() {
if left.key > right.key {
replication_setup.push(Replicas {
hash_range: left.key + 1..=u64::MAX,
nodes: self.get(&(right.node.clone(), right.virtual_id)),
});
replication_setup.push(Replicas {
hash_range: 0..=right.key,
nodes: self.get(&(right.node.clone(), right.virtual_id)),
});
} else {
replication_setup.push(Replicas {
hash_range: left.key + 1..=right.key,
nodes: self.get(&(right.node.clone(), right.virtual_id)),
});
}
left = right.clone();
}
replication_setup
}
pub fn find_sources(
&self,
target: &T,
source: &HashRing<T>,
available_nodes: &[T],
) -> Vec<Replicas<T>> {
let mut sources = vec![];
let from = source.get_hash_ranges();
let to = self.get_hash_ranges();
for needed in to {
if !needed.nodes.contains(target) {
continue;
}
for supply in from.iter() {
if let Some(range) = intersect(&needed.hash_range, &supply.hash_range) {
let mut nodes = supply.nodes.clone();
nodes.retain(|f| available_nodes.contains(f));
if nodes.contains(target) {
continue;
}
sources.push(Replicas {
hash_range: range,
nodes,
});
}
}
}
self.merge_replicas(sources)
}
pub fn merge_replicas(&self, mut replicas: Vec<Replicas<T>>) -> Vec<Replicas<T>> {
replicas.sort_by(|a, b| a.hash_range.start().cmp(b.hash_range.start()));
let mut replica_sets: HashMap<u64, Vec<Replicas<T>>> = HashMap::new();
for replica in replicas.into_iter() {
let hash = self.hash_nodes(&replica.nodes);
let entry = replica_sets.entry(hash).or_default();
entry.push(replica);
}
let mut combined = vec![];
for (_, mut set) in replica_sets.into_iter() {
let set_tail = set.split_off(1);
let mut current = match set.pop() {
Some(current) => current,
None => continue,
};
for r in set_tail {
if *current.hash_range.end() < u64::MAX
&& *r.hash_range.start() == current.hash_range.end() + 1
{
current.hash_range = *current.hash_range.start()..=*r.hash_range.end();
} else {
combined.push(current);
current = r;
}
}
combined.push(current);
}
combined
}
pub fn hash_nodes(&self, nodes: &Vec<T>) -> u64 {
let mut hasher = self.hash_builder.build_hasher();
for node in nodes {
node.hash(&mut hasher);
}
hasher.finish()
}
}
fn intersect<T: Ord + Copy>(
a: &RangeInclusive<T>,
b: &RangeInclusive<T>,
) -> Option<RangeInclusive<T>> {
let start = *a.start().max(b.start());
let end = *a.end().min(b.end());
if start <= end {
Some(start..=end)
} else {
None
}
}
#[cfg(test)]
mod tests {
use crate::hashring::HashRing;
use crate::hashring::coordinator::Replicas;
use pretty_assertions::assert_eq;
use std::hash::{Hash, Hasher};
use std::net::Ipv4Addr;
use std::str::FromStr;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Node {
addr: Ipv4Addr,
}
impl Node {
fn new(ip: &str) -> Self {
let addr = Ipv4Addr::from_str(ip).unwrap();
Node { addr }
}
}
impl Hash for Node {
fn hash<H: Hasher>(&self, s: &mut H) {
(self.addr).hash(s)
}
}
#[test]
fn hash_ranges_find_sources_minimal() {
let node1 = Node::new("127.0.0.1"); let node2 = Node::new("127.0.0.2");
let mut ring_original = HashRing::new(0, 1);
ring_original.add(node2);
let mut ring_new = HashRing::new(0, 1);
ring_new.add(node1);
let ring_original_hashrange = ring_original.get_hash_ranges();
let expected_original = vec![Replicas {
hash_range: 0..=u64::MAX,
nodes: vec![node2],
}];
assert_eq!(expected_original, ring_original_hashrange);
let ring_new_hashrange = ring_new.get_hash_ranges();
let expected_new = vec![Replicas {
hash_range: 0..=u64::MAX,
nodes: vec![node1],
}];
assert_eq!(expected_new, ring_new_hashrange);
let sources = ring_new.find_sources(&node1, &ring_original, &[node2]);
let expected: Vec<Replicas<Node>> = vec![Replicas {
hash_range: 0..=u64::MAX,
nodes: vec![node2],
}];
assert_eq!(expected, sources);
}
#[test]
fn hash_ranges_should_be_calculated_correctly() {
let node1 = Node::new("127.0.0.1"); let node4 = Node::new("127.0.0.4"); let node2 = Node::new("127.0.0.2"); let node3 = Node::new("127.0.0.3");
let nodes_original = vec![node1, node2, node3];
let mut ring_original = HashRing::new(0, 1);
ring_original.batch_add(nodes_original.clone());
let hash1 = ring_original.get_hash(&(&node1, 0_usize));
assert_eq!(1093046220658055553, hash1);
let hash2 = ring_original.get_hash(&(&node2, 0_usize));
assert_eq!(7508079630756128442, hash2);
let hash3 = ring_original.get_hash(&(&node3, 0_usize));
assert_eq!(12322253174093194230, hash3);
let hash4 = ring_original.get_hash(&(&node4, 0_usize));
assert_eq!(7061776985767999842, hash4);
let nodes_new = vec![node1, node2, node3, node4];
let mut ring_new = HashRing::new(0, 1);
ring_new.batch_add(nodes_new.clone());
let replica_setup_original = ring_original.get_hash_ranges();
let expected_original = vec![
Replicas {
hash_range: (hash3 + 1)..=u64::MAX,
nodes: vec![node1],
},
Replicas {
hash_range: 0..=hash1,
nodes: vec![node1],
},
Replicas {
hash_range: (hash1 + 1)..=hash2,
nodes: vec![node2],
},
Replicas {
hash_range: (hash2 + 1)..=hash3,
nodes: vec![node3],
},
];
assert_eq!(expected_original, replica_setup_original);
let replica_setup_new = ring_new.get_hash_ranges();
let expected_new = vec![
Replicas {
hash_range: (hash3 + 1)..=u64::MAX,
nodes: vec![node1],
},
Replicas {
hash_range: 0..=hash1,
nodes: vec![node1],
},
Replicas {
hash_range: (hash1 + 1)..=hash4,
nodes: vec![node4],
},
Replicas {
hash_range: (hash4 + 1)..=hash2,
nodes: vec![node2],
},
Replicas {
hash_range: (hash2 + 1)..=hash3,
nodes: vec![node3],
},
];
assert_eq!(expected_new, replica_setup_new);
let sources = ring_new.find_sources(&node1, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node4, &ring_original, &nodes_original);
let expected = vec![Replicas {
hash_range: (hash1 + 1)..=hash4,
nodes: vec![node2],
}];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node2, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node3, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
}
#[test]
fn hash_ranges_should_be_calculated_correctly_with_replicas() {
let node1 = Node::new("127.0.0.1"); let node2 = Node::new("127.0.0.2"); let node3 = Node::new("127.0.0.3"); let node4 = Node::new("127.0.0.4");
let nodes_original = vec![node1, node2, node3];
let mut ring_original = HashRing::new(1, 2);
ring_original.batch_add(nodes_original.clone());
let hash3_1 = ring_original.get_hash(&(&node3, 1_usize));
assert_eq!(24307670534837389, hash3_1);
let hash1_0 = ring_original.get_hash(&(&node1, 0_usize));
assert_eq!(1093046220658055553, hash1_0);
let hash2_1 = ring_original.get_hash(&(&node2, 1_usize));
assert_eq!(7110299084231520957, hash2_1);
let hash2_0 = ring_original.get_hash(&(&node2, 0_usize));
assert_eq!(7508079630756128442, hash2_0);
let hash1_1 = ring_original.get_hash(&(&node1, 1_usize));
assert_eq!(10619849754955980960, hash1_1);
let hash3_0 = ring_original.get_hash(&(&node3, 0_usize));
assert_eq!(12322253174093194230, hash3_0);
let hash4_1 = ring_original.get_hash(&(&node4, 1_usize));
assert_eq!(1807640587661881848, hash4_1);
let hash4_0 = ring_original.get_hash(&(&node4, 0_usize));
assert_eq!(7061776985767999842, hash4_0);
let nodes_new = vec![node1, node2, node3, node4];
let mut ring_new = HashRing::new(0, 1);
ring_new.batch_add(nodes_new.clone());
let replica_setup_original = ring_original.get_hash_ranges();
let expected_original = vec![
Replicas {
hash_range: (hash3_0 + 1)..=u64::MAX,
nodes: vec![node3, node1],
},
Replicas {
hash_range: 0..=hash3_1,
nodes: vec![node3, node1],
},
Replicas {
hash_range: (hash3_1 + 1)..=hash1_0,
nodes: vec![node1, node2],
},
Replicas {
hash_range: (hash1_0 + 1)..=hash2_1,
nodes: vec![node2, node1],
},
Replicas {
hash_range: (hash2_1 + 1)..=hash2_0,
nodes: vec![node2, node1],
},
Replicas {
hash_range: (hash2_0 + 1)..=hash1_1,
nodes: vec![node1, node3],
},
Replicas {
hash_range: (hash1_1 + 1)..=hash3_0,
nodes: vec![node3, node1],
},
];
assert_eq!(expected_original, replica_setup_original);
let replica_setup_new = ring_new.get_hash_ranges();
let expected_new = vec![
Replicas {
hash_range: (hash3_0 + 1)..=u64::MAX,
nodes: vec![node1],
},
Replicas {
hash_range: 0..=hash1_0,
nodes: vec![node1],
},
Replicas {
hash_range: (hash1_0 + 1)..=hash4_0,
nodes: vec![node4],
},
Replicas {
hash_range: (hash4_0 + 1)..=hash2_0,
nodes: vec![node2],
},
Replicas {
hash_range: (hash2_0 + 1)..=hash3_0,
nodes: vec![node3],
},
];
assert_eq!(expected_new, replica_setup_new);
let sources = ring_new.find_sources(&node1, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node4, &ring_original, &nodes_original);
let expected = vec![Replicas {
hash_range: (hash1_0 + 1)..=hash4_0,
nodes: vec![node2, node1],
}];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node2, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
let sources = ring_new.find_sources(&node3, &ring_original, &nodes_original);
let expected: Vec<Replicas<Node>> = vec![];
assert_eq!(expected, sources);
}
}