Skip to main content

atomr_cluster/
reachability.rs

1//! Reachability. akka.net: `Cluster/Reachability.cs`.
2//!
3//! Records which observers think which subjects are reachable. A node is
4//! considered unreachable if any observer reports it as such.
5
6use std::collections::HashMap;
7
8use atomr_core::actor::Address;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[non_exhaustive]
13pub enum ReachabilityStatus {
14    Reachable,
15    Unreachable,
16    Terminated,
17}
18
19#[derive(Debug, Default, Clone, Serialize, Deserialize)]
20pub struct Reachability {
21    pub records: HashMap<(Address, Address), ReachabilityStatus>,
22}
23
24impl Reachability {
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    pub fn unreachable(&mut self, observer: Address, subject: Address) {
30        self.records.insert((observer, subject), ReachabilityStatus::Unreachable);
31    }
32
33    pub fn reachable(&mut self, observer: Address, subject: Address) {
34        self.records.insert((observer, subject), ReachabilityStatus::Reachable);
35    }
36
37    pub fn terminated(&mut self, observer: Address, subject: Address) {
38        self.records.insert((observer, subject), ReachabilityStatus::Terminated);
39    }
40
41    pub fn status(&self, subject: &Address) -> ReachabilityStatus {
42        let mut any_unreachable = false;
43        for ((_, s), st) in &self.records {
44            if s == subject {
45                match st {
46                    ReachabilityStatus::Terminated => return ReachabilityStatus::Terminated,
47                    ReachabilityStatus::Unreachable => any_unreachable = true,
48                    ReachabilityStatus::Reachable => {}
49                }
50            }
51        }
52        if any_unreachable {
53            ReachabilityStatus::Unreachable
54        } else {
55            ReachabilityStatus::Reachable
56        }
57    }
58
59    pub fn is_reachable(&self, subject: &Address) -> bool {
60        matches!(self.status(subject), ReachabilityStatus::Reachable)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn marks_unreachable_then_reachable() {
70        let mut r = Reachability::new();
71        let a = Address::local("A");
72        let b = Address::local("B");
73        r.unreachable(a.clone(), b.clone());
74        assert!(!r.is_reachable(&b));
75        r.reachable(a.clone(), b.clone());
76        assert!(r.is_reachable(&b));
77    }
78}