laminar_core/cluster/control/controller/
membership.rs1use super::*;
4
5impl ClusterController {
6 #[must_use]
8 pub fn live_instances(&self) -> Vec<NodeId> {
9 let mut ids: Vec<NodeId> = self
10 .members_rx
11 .borrow()
12 .iter()
13 .filter(|m| m.id != self.instance_id && matches!(m.state, NodeState::Active))
14 .map(|m| m.id)
15 .collect();
16 if self.active.load(Ordering::SeqCst) && self.process_lease_is_live() {
17 ids.push(self.instance_id);
18 }
19 ids
20 }
21
22 #[must_use]
29 pub fn checkpoint_instances(&self) -> Vec<NodeId> {
30 let mut ids: Vec<NodeId> = self
31 .members_rx
32 .borrow()
33 .iter()
34 .filter(|member| {
35 member.id != self.instance_id
36 && matches!(member.state, NodeState::Active | NodeState::Draining)
37 })
38 .map(|member| member.id)
39 .collect();
40 if self.active.load(Ordering::SeqCst) && self.process_lease_is_live() {
41 ids.push(self.instance_id);
42 }
43 ids.sort_unstable();
44 ids.dedup();
45 ids
46 }
47
48 pub fn note_unresponsive(&self, peers: &[NodeId]) {
50 let fence = self.checkpoint_assignment_fence.borrow().clone();
51 let mut map = self.unresponsive.lock();
52 let mut changed = false;
53 for p in peers {
54 let incarnation = fence
55 .as_ref()
56 .and_then(|certificate| certificate.participant_incarnation(p.0));
57 changed |= map.insert(p.0, incarnation) != Some(incarnation);
58 }
59 drop(map);
60 if changed {
61 self.notify_leader_eligibility_change();
62 }
63 }
64
65 pub fn note_responsive(&self, peers: &[NodeId]) {
67 let mut map = self.unresponsive.lock();
68 let mut changed = false;
69 for p in peers {
70 changed |= map.remove(&p.0).is_some();
71 }
72 drop(map);
73 if changed {
74 self.notify_leader_eligibility_change();
75 }
76 }
77
78 pub fn note_recovery_responsive(&self, participants: &[CheckpointParticipant]) {
82 let mut map = self.unresponsive.lock();
83 let mut changed = false;
84 for participant in participants {
85 let exact = matches!(
86 map.get(&participant.node_id),
87 Some(Some(failed_boot)) if *failed_boot == participant.boot_incarnation
88 );
89 if exact {
90 changed |= map.remove(&participant.node_id).is_some();
91 }
92 }
93 drop(map);
94 if changed {
95 self.notify_leader_eligibility_change();
96 }
97 }
98
99 #[must_use]
101 pub fn is_unresponsive(&self, peer: NodeId) -> bool {
102 self.unresponsive.lock().contains_key(&peer.0)
103 }
104
105 pub fn admit_successor_process(&self, participant: CheckpointParticipant) -> bool {
108 let mut unresponsive = self.unresponsive.lock();
109 let admitted = match unresponsive.get(&participant.node_id).copied() {
110 None => true,
111 Some(Some(failed_boot)) if failed_boot != participant.boot_incarnation => {
112 unresponsive.remove(&participant.node_id);
113 drop(unresponsive);
114 self.notify_leader_eligibility_change();
115 return true;
116 }
117 Some(Some(_) | None) => false,
118 };
119 drop(unresponsive);
120 admitted
121 }
122
123 pub fn begin_drain(&self) -> bool {
126 #[cfg(feature = "cluster")]
127 let has_durable_leadership = self.has_leader_lease_fencing();
128 #[cfg(not(feature = "cluster"))]
129 let has_durable_leadership = false;
130 let retain_leadership = has_durable_leadership
131 && self.is_leader()
132 && self
133 .checkpoint_assignment_fence
134 .borrow()
135 .as_ref()
136 .is_some_and(|fence| {
137 fence.is_canonical()
138 && fence.participant_incarnation(self.instance_id.0)
139 == Some(self.recovery_incarnation)
140 });
141 self.draining.store(true, Ordering::SeqCst);
142 if !retain_leadership && self.leader_eligible.swap(false, Ordering::SeqCst) {
143 self.notify_leader_eligibility_change();
144 }
145 retain_leadership
146 }
147
148 #[must_use]
150 pub fn is_draining(&self) -> bool {
151 self.draining.load(Ordering::SeqCst)
152 }
153
154 pub fn set_recovering(&self, recovering: bool) {
156 let _transition = self.process_authority_transition.lock();
157 let recovering = recovering || !self.process_lease_is_live();
158 self.recovering.store(recovering, Ordering::SeqCst);
159 }
160
161 #[must_use]
163 pub fn is_recovering(&self) -> bool {
164 self.recovering.load(Ordering::SeqCst)
165 }
166}