laminar_core/cluster/control/controller/
construction.rs1use super::*;
4
5impl ClusterController {
6 #[must_use]
8 pub fn new(
9 instance_id: NodeId,
10 kv: Arc<dyn ClusterKv>,
11 snapshot: Option<Arc<AssignmentSnapshotStore>>,
12 members_rx: watch::Receiver<Vec<NodeInfo>>,
13 ) -> Self {
14 Self::new_with_recovery_kv(instance_id, Arc::clone(&kv), kv, snapshot, members_rx)
15 }
16
17 #[must_use]
22 pub fn new_with_recovery_kv(
23 instance_id: NodeId,
24 kv: Arc<dyn ClusterKv>,
25 recovery_kv: Arc<dyn ClusterKv>,
26 snapshot: Option<Arc<AssignmentSnapshotStore>>,
27 members_rx: watch::Receiver<Vec<NodeInfo>>,
28 ) -> Self {
29 Self::new_with_recovery_incarnation(
30 instance_id,
31 kv,
32 recovery_kv,
33 snapshot,
34 members_rx,
35 Uuid::new_v4(),
36 )
37 }
38
39 #[must_use]
41 pub fn new_with_recovery_incarnation(
42 instance_id: NodeId,
43 kv: Arc<dyn ClusterKv>,
44 recovery_kv: Arc<dyn ClusterKv>,
45 snapshot: Option<Arc<AssignmentSnapshotStore>>,
46 members_rx: watch::Receiver<Vec<NodeInfo>>,
47 recovery_incarnation: Uuid,
48 ) -> Self {
49 let leader_eligible = Arc::new(AtomicBool::new(true));
50 let mut barrier = BarrierCoordinator::new(Arc::clone(&kv));
51 #[cfg(feature = "cluster")]
52 barrier.set_leader_election(
53 instance_id,
54 members_rx.clone(),
55 Arc::clone(&leader_eligible),
56 );
57 let controller = Self {
58 instance_id,
59 barrier,
60 kv,
61 recovery_kv,
62 snapshot,
63 members_rx,
64 checkpoint_assignment_fence: watch::channel(None).0,
66 checkpoint_drain_transition: watch::channel(None).0,
67 process_authority_transition: parking_lot::Mutex::new(()),
68 recovery_incarnation,
69 recovery_process_term: AtomicU64::new(0),
70 recovery_fault_request_sequence: AtomicU64::new(1),
71 cluster_min_watermark: Arc::new(AtomicI64::new(i64::MIN)),
72 committed_source_watermarks: parking_lot::RwLock::new(Arc::new(
73 rustc_hash::FxHashMap::default(),
74 )),
75 committed_watermark_publication: parking_lot::Mutex::new(()),
76 #[cfg(feature = "cluster")]
77 checkpoint_gap_waiters: std::sync::atomic::AtomicUsize::new(0),
78 draining: Arc::new(AtomicBool::new(false)),
79 recovering: Arc::new(AtomicBool::new(false)),
80 active: Arc::new(AtomicBool::new(true)),
81 process_lease_live: Arc::new(AtomicBool::new(true)),
82 process_lease_deadline: std::sync::OnceLock::new(),
83 process_lease_authority: std::sync::OnceLock::new(),
84 leader_eligible,
85 leader_candidacy: watch::channel(super::super::LeaderCandidacy::initial(false)).0,
86 leadership_participants: parking_lot::RwLock::new(None),
87 recovery_writes: tokio::sync::Mutex::new(()),
88 pending_release_fault_audit: tokio::sync::Mutex::new(None),
89 unresponsive: Arc::new(parking_lot::Mutex::new(rustc_hash::FxHashMap::default())),
90 self_locality: parking_lot::RwLock::new(Locality::default()),
91 #[cfg(feature = "cluster")]
92 leader_lease: std::sync::OnceLock::new(),
93 };
94 controller.notify_leader_eligibility_change();
95 controller
96 }
97
98 #[cfg(feature = "cluster")]
100 pub fn set_leader_lease_store(&self, store: Arc<super::super::LeaderLeaseStore>) {
101 self.barrier.set_leader_lease_store(store);
102 }
103
104 #[cfg(feature = "cluster")]
106 pub fn install_local_leader_proof_provider(self: &Arc<Self>) {
107 let controller = Arc::downgrade(self);
108 self.barrier
109 .set_local_leader_proof_provider(Arc::new(move || {
110 controller
111 .upgrade()
112 .and_then(|controller| controller.capture_leader_proof())
113 }));
114 }
115
116 #[cfg(feature = "cluster")]
123 pub fn checkpoint_authority(
124 &self,
125 ) -> Result<Arc<super::super::LeaderLeaseStore>, super::super::ClusterCheckpointAuthorityError>
126 {
127 self.barrier.checkpoint_authority()
128 }
129
130 #[must_use]
133 pub fn cluster_min_watermark(&self) -> Option<i64> {
134 let v = self.cluster_min_watermark.load(Ordering::Acquire);
135 if v == i64::MIN {
136 None
137 } else {
138 Some(v)
139 }
140 }
141
142 pub fn publish_cluster_min_watermark(&self, wm: i64) {
145 let _publication = self.committed_watermark_publication.lock();
146 self.publish_cluster_min_watermark_locked(wm);
147 }
148
149 pub(super) fn publish_cluster_min_watermark_locked(&self, wm: i64) {
150 let mut cur = self.cluster_min_watermark.load(Ordering::Acquire);
151 while wm > cur {
152 match self.cluster_min_watermark.compare_exchange(
153 cur,
154 wm,
155 Ordering::AcqRel,
156 Ordering::Acquire,
157 ) {
158 Ok(_) => break,
159 Err(observed) => cur = observed,
160 }
161 }
162 }
163
164 #[must_use]
166 pub fn committed_source_watermarks_snapshot(&self) -> Arc<rustc_hash::FxHashMap<String, i64>> {
167 let snapshot = self.committed_source_watermarks.read();
168 Arc::clone(&snapshot)
169 }
170
171 pub fn publish_committed_channel_progress(
180 &self,
181 channels: &[crate::checkpoint::ChannelProgress],
182 ) -> Result<(), String> {
183 let source_watermarks = crate::checkpoint::channel_progress_frontiers_by_source(channels)?
184 .into_iter()
185 .filter_map(|(source, frontier)| frontier.map(|frontier| (source.to_owned(), frontier)))
186 .collect::<std::collections::BTreeMap<_, _>>();
187 self.publish_committed_checkpoint_progress(channels, &source_watermarks)
188 }
189
190 pub fn publish_committed_checkpoint_progress(
200 &self,
201 channels: &[crate::checkpoint::ChannelProgress],
202 source_watermarks: &std::collections::BTreeMap<String, i64>,
203 ) -> Result<(), String> {
204 let cluster_min = crate::checkpoint::channel_progress_frontier(channels)?;
205 let current = crate::checkpoint::channel_progress_frontiers_by_source(channels)?;
206 if source_watermarks
207 .values()
208 .any(|watermark| *watermark == i64::MIN)
209 {
210 return Err("committed source watermark uses the reserved uninitialized value".into());
211 }
212 for (source, frontier) in current {
213 let Some(frontier) = frontier else {
214 continue;
215 };
216 if source_watermarks.get(source) != Some(&frontier) {
217 return Err(format!(
218 "committed source watermark '{source}' disagrees with channel progress"
219 ));
220 }
221 }
222 let _publication = self.committed_watermark_publication.lock();
223 {
224 let mut published = self.committed_source_watermarks.write();
225 let published = Arc::make_mut(&mut *published);
226 for (source_name, frontier) in source_watermarks {
227 published
228 .entry(source_name.clone())
229 .and_modify(|current| *current = (*current).max(*frontier))
230 .or_insert(*frontier);
231 }
232 }
233 if let Some(cluster_min) = cluster_min {
234 self.publish_cluster_min_watermark_locked(cluster_min);
235 }
236 Ok(())
237 }
238
239 pub fn replace_recovered_checkpoint_progress(
250 &self,
251 channels: &[crate::checkpoint::ChannelProgress],
252 source_watermarks: &std::collections::BTreeMap<String, i64>,
253 ) -> Result<(), String> {
254 let cluster_min = crate::checkpoint::channel_progress_frontier(channels)?;
255 let current = crate::checkpoint::channel_progress_frontiers_by_source(channels)?;
256 if source_watermarks
257 .values()
258 .any(|watermark| *watermark == i64::MIN)
259 {
260 return Err("committed source watermark uses the reserved uninitialized value".into());
261 }
262 for (source, frontier) in current {
263 let Some(frontier) = frontier else {
264 continue;
265 };
266 if source_watermarks.get(source) != Some(&frontier) {
267 return Err(format!(
268 "committed source watermark '{source}' disagrees with channel progress"
269 ));
270 }
271 }
272
273 let replacement = source_watermarks
274 .iter()
275 .map(|(source, watermark)| (source.clone(), *watermark))
276 .collect::<rustc_hash::FxHashMap<_, _>>();
277 let _publication = self.committed_watermark_publication.lock();
278 *self.committed_source_watermarks.write() = Arc::new(replacement);
279 self.cluster_min_watermark
280 .store(cluster_min.unwrap_or(i64::MIN), Ordering::Release);
281 Ok(())
282 }
283}