Skip to main content

forge_core/cluster/
traits.rs

1use super::node::NodeId;
2use super::roles::LeaderRole;
3
4/// Information about the cluster.
5#[derive(Debug, Clone)]
6pub struct ClusterInfo {
7    /// Cluster name.
8    pub name: String,
9    /// Total number of registered nodes.
10    pub total_nodes: usize,
11    /// Number of active nodes.
12    pub active_nodes: usize,
13    /// Number of draining nodes.
14    pub draining_nodes: usize,
15    /// Number of dead nodes.
16    pub dead_nodes: usize,
17    /// Current scheduler leader node ID.
18    pub scheduler_leader: Option<NodeId>,
19}
20
21impl ClusterInfo {
22    /// Create empty cluster info.
23    pub fn empty(name: impl Into<String>) -> Self {
24        Self {
25            name: name.into(),
26            total_nodes: 0,
27            active_nodes: 0,
28            draining_nodes: 0,
29            dead_nodes: 0,
30            scheduler_leader: None,
31        }
32    }
33}
34
35/// Leadership information for a role.
36#[derive(Debug, Clone)]
37pub struct LeaderInfo {
38    /// The leader role.
39    pub role: LeaderRole,
40    /// Node ID of the leader.
41    pub node_id: NodeId,
42    /// When leadership was acquired.
43    pub acquired_at: chrono::DateTime<chrono::Utc>,
44    /// When the lease expires.
45    pub lease_until: chrono::DateTime<chrono::Utc>,
46}
47
48impl LeaderInfo {
49    /// Check if the lease is still valid.
50    pub fn is_valid(&self) -> bool {
51        self.lease_until > chrono::Utc::now()
52    }
53}
54
55#[cfg(test)]
56#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn test_cluster_info_empty() {
62        let info = ClusterInfo::empty("test-cluster");
63        assert_eq!(info.name, "test-cluster");
64        assert_eq!(info.total_nodes, 0);
65        assert_eq!(info.active_nodes, 0);
66        assert!(info.scheduler_leader.is_none());
67    }
68
69    #[test]
70    fn test_leader_info_validity() {
71        let node_id = NodeId::new();
72        let info = LeaderInfo {
73            role: LeaderRole::Scheduler,
74            node_id,
75            acquired_at: chrono::Utc::now(),
76            lease_until: chrono::Utc::now() + chrono::Duration::minutes(1),
77        };
78        assert!(info.is_valid());
79
80        let expired_info = LeaderInfo {
81            role: LeaderRole::Scheduler,
82            node_id,
83            acquired_at: chrono::Utc::now() - chrono::Duration::minutes(5),
84            lease_until: chrono::Utc::now() - chrono::Duration::minutes(1),
85        };
86        assert!(!expired_info.is_valid());
87    }
88}