hash_rings/
mpc.rs

1//! Hashing ring implemented using multi-probe consistent hashing.
2
3use crate::util;
4use rand::{Rng, XorShiftRng};
5use siphasher::sip::SipHasher;
6use std::collections::hash_map::RandomState;
7use std::collections::BTreeMap;
8use std::hash::{BuildHasher, Hash, Hasher};
9
10const PRIME: u64 = 0xFFFF_FFFF_FFFF_FFC5;
11
12/// A hashing ring implemented using multi-probe consistent hashing.
13///
14/// Multi-probe consistent hashing is a variation on consistent hashing where instead of the nodes
15/// being hashed multiple times to reduce variance, the keys are hashed multiple times. Each key is
16/// hashed `hash_count` times and the closest node over all hashes is returned.
17///
18/// # Examples
19/// ```
20/// use hash_rings::mpc::Ring;
21/// use std::collections::hash_map::DefaultHasher;
22/// use std::hash::BuildHasherDefault;
23///
24/// type DefaultBuildHasher = BuildHasherDefault<DefaultHasher>;
25///
26/// let mut ring = Ring::with_hasher(DefaultBuildHasher::default(), 2);
27///
28/// ring.insert_node(&"node-1");
29/// ring.insert_node(&"node-2");
30///
31/// ring.remove_node(&"node-1");
32///
33/// assert_eq!(ring.get_node(&"point-1"), &"node-2");
34/// assert_eq!(ring.len(), 1);
35///
36/// let mut iterator = ring.iter();
37/// assert_eq!(iterator.next(), Some(&"node-2"));
38/// assert_eq!(iterator.next(), None);
39/// ```
40pub struct Ring<'a, T, H = RandomState> {
41    nodes: BTreeMap<u64, &'a T>,
42    hash_count: u64,
43    hashers: [SipHasher; 2],
44    hash_builder: H,
45}
46
47impl<'a, T> Ring<'a, T, RandomState> {
48    /// Constructs a new, empty `Ring<T>` that hashes `hash_count` times when a key is inserted.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use hash_rings::mpc::Ring;
54    ///
55    /// let mut ring: Ring<&str> = Ring::new(2);
56    /// ```
57    pub fn new(hash_count: u64) -> Self {
58        assert!(hash_count > 0);
59        Self {
60            nodes: BTreeMap::new(),
61            hash_count,
62            hashers: Self::get_hashers(),
63            hash_builder: Default::default(),
64        }
65    }
66}
67
68impl<'a, T, H> Ring<'a, T, H> {
69    fn get_hashers() -> [SipHasher; 2] {
70        let mut rng = XorShiftRng::new_unseeded();
71        [
72            SipHasher::new_with_keys(rng.next_u64(), rng.next_u64()),
73            SipHasher::new_with_keys(rng.next_u64(), rng.next_u64()),
74        ]
75    }
76
77    fn get_hashes<U>(&self, item: &U) -> [u64; 2]
78    where
79        U: Hash,
80    {
81        let mut ret = [0; 2];
82        for (index, hash) in ret.iter_mut().enumerate() {
83            let mut sip = self.hashers[index];
84            item.hash(&mut sip);
85            *hash = sip.finish();
86        }
87        ret
88    }
89
90    fn get_distance(hash: u64, next_hash: u64) -> u64 {
91        if hash > next_hash {
92            next_hash + (<u64>::max_value() - hash)
93        } else {
94            next_hash - hash
95        }
96    }
97
98    fn get_next_hash(&self, hash: u64) -> u64 {
99        let next_hash_opt = self
100            .nodes
101            .range(hash..)
102            .next()
103            .or_else(|| self.nodes.iter().next())
104            .map(|entry| *entry.0);
105        match next_hash_opt {
106            Some(hash) => hash,
107            None => panic!("Error: empty ring."),
108        }
109    }
110
111    /// Constructs a new, empty `Ring<T>` that hashes `hash_count` times when a key is inserted
112    /// with a specified hash builder.
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use hash_rings::mpc::Ring;
118    /// use std::collections::hash_map::DefaultHasher;
119    /// use std::hash::BuildHasherDefault;
120    ///
121    /// type DefaultBuildHasher = BuildHasherDefault<DefaultHasher>;
122    ///
123    /// let mut ring: Ring<&str, _> = Ring::with_hasher(DefaultBuildHasher::default(), 2);
124    /// ```
125    pub fn with_hasher(hash_builder: H, hash_count: u64) -> Self {
126        assert!(hash_count > 0);
127        Self {
128            nodes: BTreeMap::new(),
129            hash_count,
130            hashers: Self::get_hashers(),
131            hash_builder,
132        }
133    }
134
135    /// Inserts a node into the ring with a number of replicas.
136    ///
137    /// Increasing the number of replicas will increase the number of expected points mapped to the
138    /// node. For example, a node with three replicas will receive approximately three times more
139    /// points than a node with one replica.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use hash_rings::mpc::Ring;
145    ///
146    /// let mut ring: Ring<&str> = Ring::new(2);
147    /// ring.insert_node(&"node-1");
148    /// ```
149    pub fn insert_node(&mut self, id: &'a T)
150    where
151        T: Hash,
152        H: BuildHasher,
153    {
154        self.nodes
155            .insert(util::gen_hash(&self.hash_builder, id), id);
156    }
157
158    /// Removes a node.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// use hash_rings::mpc::Ring;
164    ///
165    /// let mut ring: Ring<&str> = Ring::new(2);
166    ///
167    /// ring.insert_node(&"node-1");
168    /// ring.remove_node(&"node-1");
169    /// ```
170    pub fn remove_node(&mut self, id: &T)
171    where
172        T: Hash,
173        H: BuildHasher,
174    {
175        self.nodes.remove(&util::gen_hash(&self.hash_builder, id));
176    }
177
178    /// Returns the node associated with a point.
179    ///
180    /// # Panics
181    ///
182    /// Panics if the ring is empty.
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use hash_rings::mpc::Ring;
188    ///
189    /// let mut ring: Ring<&str> = Ring::new(2);
190    ///
191    /// ring.insert_node(&"node-1");
192    /// assert_eq!(ring.get_node(&"point-1"), &"node-1");
193    /// ```
194    pub fn get_node<U>(&self, point: &U) -> &T
195    where
196        U: Hash,
197    {
198        let hashes = self.get_hashes(point);
199        let hash = (0..self.hash_count)
200            .map(|i| {
201                let hash = hashes[0].wrapping_add((i as u64).wrapping_mul(hashes[1]) % PRIME);
202                let next_hash = self.get_next_hash(hash);
203                (Self::get_distance(hash, next_hash), next_hash)
204            })
205            .min()
206            .expect("Error: expected positive hash count.");
207
208        self.nodes[&hash.1]
209    }
210
211    /// Returns the number of nodes in the ring.
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// use hash_rings::mpc::Ring;
217    ///
218    /// let mut ring: Ring<&str> = Ring::new(2);
219    ///
220    /// ring.insert_node(&"node-1");
221    /// assert_eq!(ring.len(), 1);
222    /// ```
223    pub fn len(&self) -> usize {
224        self.nodes.len()
225    }
226
227    /// Returns `true` if the ring is empty.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// use hash_rings::mpc::Ring;
233    ///
234    /// let mut ring: Ring<&str> = Ring::new(2);
235    ///
236    /// assert!(ring.is_empty());
237    /// ring.insert_node(&"node-1");
238    /// assert!(!ring.is_empty());
239    /// ```
240    pub fn is_empty(&self) -> bool {
241        self.nodes.is_empty()
242    }
243
244    /// Returns an iterator over the ring. The iterator will yield the nodes in the ring in no
245    /// particular order.
246    ///
247    /// # Examples
248    ///
249    /// ```
250    /// use hash_rings::mpc::Ring;
251    ///
252    /// let mut ring = Ring::new(2);
253    /// ring.insert_node(&"node-1");
254    ///
255    /// let mut iterator = ring.iter();
256    /// assert_eq!(iterator.next(), Some(&"node-1"));
257    /// assert_eq!(iterator.next(), None);
258    /// ```
259    pub fn iter(&'a self) -> impl Iterator<Item = &'a T> {
260        self.nodes.iter().map(|node| *node.1)
261    }
262}
263
264impl<'a, T, H> IntoIterator for &'a Ring<'a, T, H>
265where
266    T: Hash + Eq,
267{
268    type IntoIter = Box<dyn Iterator<Item = &'a T> + 'a>;
269    type Item = (&'a T);
270
271    fn into_iter(self) -> Self::IntoIter {
272        Box::new(self.iter())
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::Ring;
279    use crate::test_util::BuildDefaultHasher;
280
281    #[test]
282    #[should_panic]
283    fn test_new_zero_hash_count() {
284        let _ring: Ring<'_, u32, BuildDefaultHasher> =
285            Ring::with_hasher(BuildDefaultHasher::default(), 0);
286    }
287
288    #[test]
289    #[should_panic]
290    fn test_get_node_empty_ring() {
291        let ring: Ring<'_, u32, BuildDefaultHasher> =
292            Ring::with_hasher(BuildDefaultHasher::default(), 2);
293        ring.get_node(&0);
294    }
295
296    #[test]
297    fn test_get_node() {
298        let mut ring = Ring::with_hasher(BuildDefaultHasher::default(), 2);
299
300        ring.insert_node(&0);
301        assert_eq!(ring.get_node(&2), &0);
302
303        ring.insert_node(&1);
304        assert_eq!(ring.get_node(&2), &1);
305
306        ring.remove_node(&1);
307        assert_eq!(ring.get_node(&2), &0);
308    }
309
310    #[test]
311    fn test_len() {
312        let mut ring = Ring::with_hasher(BuildDefaultHasher::default(), 2);
313        ring.insert_node(&0);
314
315        assert_eq!(ring.len(), 1);
316    }
317
318    #[test]
319    fn test_is_empty() {
320        let mut ring = Ring::with_hasher(BuildDefaultHasher::default(), 2);
321        assert!(ring.is_empty());
322
323        ring.insert_node(&0);
324        assert!(!ring.is_empty());
325    }
326
327    #[test]
328    fn test_iter() {
329        let mut ring = Ring::with_hasher(BuildDefaultHasher::default(), 2);
330        ring.insert_node(&0);
331
332        let mut iterator = ring.iter();
333        assert_eq!(iterator.next(), Some(&0));
334        assert_eq!(iterator.next(), None);
335    }
336}