rust_logic_graph/fault_tolerance/
failover.rs

1use crate::distributed::InMemoryStore;
2use crate::fault_tolerance::circuit_breaker::CircuitBreaker;
3use std::sync::Arc;
4use tokio::sync::RwLock;
5
6#[derive(Debug, Clone)]
7pub struct ServiceEndpoint {
8    pub name: String,
9    pub url: String,
10}
11
12pub struct FailoverManager {
13    endpoints: Arc<RwLock<Vec<ServiceEndpoint>>>,
14    cb: Arc<CircuitBreaker>,
15}
16
17impl FailoverManager {
18    pub fn new(endpoints: Vec<ServiceEndpoint>, cb: Arc<CircuitBreaker>) -> Self {
19        Self {
20            endpoints: Arc::new(RwLock::new(endpoints)),
21            cb,
22        }
23    }
24
25    pub async fn select(&self) -> Option<ServiceEndpoint> {
26        let eps = self.endpoints.read().await;
27        for ep in eps.iter() {
28            // very simple: if circuit allows, pick first
29            if self.cb.is_allowed().await {
30                return Some(ep.clone());
31            }
32        }
33        None
34    }
35
36    pub async fn add_endpoint(&self, ep: ServiceEndpoint) {
37        let mut eps = self.endpoints.write().await;
38        eps.push(ep);
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use crate::fault_tolerance::circuit_breaker::CircuitConfig;
46    use std::sync::Arc;
47
48    #[tokio::test]
49    async fn test_failover_selection() {
50        let cb = Arc::new(CircuitBreaker::new(
51            "svc-a",
52            Some(Arc::new(InMemoryStore::new())),
53            Some(CircuitConfig {
54                failure_threshold: 10,
55                recovery_timeout: std::time::Duration::from_secs(60),
56                probe_interval: std::time::Duration::from_secs(5),
57            }),
58        ));
59        let eps = vec![
60            ServiceEndpoint {
61                name: "primary".into(),
62                url: "https://primary.local".into(),
63            },
64            ServiceEndpoint {
65                name: "backup".into(),
66                url: "https://backup.local".into(),
67            },
68        ];
69        let fm = FailoverManager::new(eps, cb);
70        let selected = fm.select().await;
71        assert!(selected.is_some());
72    }
73}