Skip to main content

laminar_core/cluster/control/
leader.rs

1//! Weak leader election: lowest-ID live instance wins. Split-brain is
2//! tolerated because `epoch=N/_COMMIT` is CAS-guarded.
3
4use crate::cluster::discovery::NodeId;
5
6/// Smallest non-sentinel ID in `live`, or `None` if empty/all-sentinel.
7#[must_use]
8pub fn leader_of(live: &[NodeId]) -> Option<NodeId> {
9    live.iter().copied().filter(|n| !n.is_unassigned()).min()
10}
11
12#[cfg(test)]
13mod tests {
14    use super::*;
15
16    #[test]
17    fn empty_has_no_leader() {
18        assert_eq!(leader_of(&[]), None);
19    }
20
21    #[test]
22    fn only_unassigned_has_no_leader() {
23        assert_eq!(leader_of(&[NodeId::UNASSIGNED]), None);
24    }
25
26    #[test]
27    fn single_instance_is_leader() {
28        assert_eq!(leader_of(&[NodeId(42)]), Some(NodeId(42)));
29    }
30
31    #[test]
32    fn smallest_wins() {
33        let live = [NodeId(100), NodeId(1), NodeId(50)];
34        assert_eq!(leader_of(&live), Some(NodeId(1)));
35    }
36
37    #[test]
38    fn unassigned_ignored_with_real_ids() {
39        let live = [NodeId::UNASSIGNED, NodeId(5), NodeId(3)];
40        assert_eq!(leader_of(&live), Some(NodeId(3)));
41    }
42
43    #[test]
44    fn deterministic_across_calls() {
45        let live = [NodeId(7), NodeId(3), NodeId(9)];
46        assert_eq!(leader_of(&live), leader_of(&live));
47    }
48}