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 draining: Arc::new(AtomicBool::new(false)),
77 recovering: Arc::new(AtomicBool::new(false)),
78 active: Arc::new(AtomicBool::new(true)),
79 process_lease_live: Arc::new(AtomicBool::new(true)),
80 process_lease_deadline: std::sync::OnceLock::new(),
81 process_lease_authority: std::sync::OnceLock::new(),
82 leader_eligible,
83 leader_candidacy: watch::channel(super::super::LeaderCandidacy::initial(false)).0,
84 leadership_participants: parking_lot::RwLock::new(None),
85 recovery_writes: tokio::sync::Mutex::new(()),
86 pending_release_fault_audit: tokio::sync::Mutex::new(None),
87 unresponsive: Arc::new(parking_lot::Mutex::new(rustc_hash::FxHashMap::default())),
88 self_locality: parking_lot::RwLock::new(Locality::default()),
89 #[cfg(feature = "cluster")]
90 leader_lease: std::sync::OnceLock::new(),
91 };
92 controller.notify_leader_eligibility_change();
93 controller
94 }
95
96 #[cfg(feature = "cluster")]
98 pub fn set_leader_lease_store(&self, store: Arc<super::super::LeaderLeaseStore>) {
99 self.barrier.set_leader_lease_store(store);
100 }
101
102 #[cfg(feature = "cluster")]
104 pub fn install_local_leader_proof_provider(self: &Arc<Self>) {
105 let controller = Arc::downgrade(self);
106 self.barrier
107 .set_local_leader_proof_provider(Arc::new(move || {
108 controller
109 .upgrade()
110 .and_then(|controller| controller.capture_leader_proof())
111 }));
112 }
113
114 #[cfg(feature = "cluster")]
121 pub fn checkpoint_authority(
122 &self,
123 ) -> Result<Arc<super::super::LeaderLeaseStore>, super::super::ClusterCheckpointAuthorityError>
124 {
125 self.barrier.checkpoint_authority()
126 }
127
128 #[must_use]
131 pub fn cluster_min_watermark(&self) -> Option<i64> {
132 let v = self.cluster_min_watermark.load(Ordering::Acquire);
133 if v == i64::MIN {
134 None
135 } else {
136 Some(v)
137 }
138 }
139
140 pub fn publish_cluster_min_watermark(&self, wm: i64) {
143 let _publication = self.committed_watermark_publication.lock();
144 self.publish_cluster_min_watermark_locked(wm);
145 }
146
147 pub(super) fn publish_cluster_min_watermark_locked(&self, wm: i64) {
148 let mut cur = self.cluster_min_watermark.load(Ordering::Acquire);
149 while wm > cur {
150 match self.cluster_min_watermark.compare_exchange(
151 cur,
152 wm,
153 Ordering::AcqRel,
154 Ordering::Acquire,
155 ) {
156 Ok(_) => break,
157 Err(observed) => cur = observed,
158 }
159 }
160 }
161
162 #[must_use]
164 pub fn committed_source_watermarks_snapshot(&self) -> Arc<rustc_hash::FxHashMap<String, i64>> {
165 let snapshot = self.committed_source_watermarks.read();
166 Arc::clone(&snapshot)
167 }
168
169 pub fn publish_committed_channel_progress(
178 &self,
179 channels: &[crate::checkpoint::ChannelProgress],
180 ) -> Result<(), String> {
181 let source_watermarks = crate::checkpoint::channel_progress_frontiers_by_source(channels)?
182 .into_iter()
183 .filter_map(|(source, frontier)| frontier.map(|frontier| (source.to_owned(), frontier)))
184 .collect::<std::collections::BTreeMap<_, _>>();
185 self.publish_committed_checkpoint_progress(channels, &source_watermarks)
186 }
187
188 pub fn publish_committed_checkpoint_progress(
198 &self,
199 channels: &[crate::checkpoint::ChannelProgress],
200 source_watermarks: &std::collections::BTreeMap<String, i64>,
201 ) -> Result<(), String> {
202 let cluster_min = crate::checkpoint::channel_progress_frontier(channels)?;
203 let current = crate::checkpoint::channel_progress_frontiers_by_source(channels)?;
204 if source_watermarks
205 .values()
206 .any(|watermark| *watermark == i64::MIN)
207 {
208 return Err("committed source watermark uses the reserved uninitialized value".into());
209 }
210 for (source, frontier) in current {
211 let Some(frontier) = frontier else {
212 continue;
213 };
214 if source_watermarks.get(source) != Some(&frontier) {
215 return Err(format!(
216 "committed source watermark '{source}' disagrees with channel progress"
217 ));
218 }
219 }
220 let _publication = self.committed_watermark_publication.lock();
221 {
222 let mut published = self.committed_source_watermarks.write();
223 let published = Arc::make_mut(&mut *published);
224 for (source_name, frontier) in source_watermarks {
225 published
226 .entry(source_name.clone())
227 .and_modify(|current| *current = (*current).max(*frontier))
228 .or_insert(*frontier);
229 }
230 }
231 if let Some(cluster_min) = cluster_min {
232 self.publish_cluster_min_watermark_locked(cluster_min);
233 }
234 Ok(())
235 }
236
237 pub fn replace_recovered_checkpoint_progress(
248 &self,
249 channels: &[crate::checkpoint::ChannelProgress],
250 source_watermarks: &std::collections::BTreeMap<String, i64>,
251 ) -> Result<(), String> {
252 let cluster_min = crate::checkpoint::channel_progress_frontier(channels)?;
253 let current = crate::checkpoint::channel_progress_frontiers_by_source(channels)?;
254 if source_watermarks
255 .values()
256 .any(|watermark| *watermark == i64::MIN)
257 {
258 return Err("committed source watermark uses the reserved uninitialized value".into());
259 }
260 for (source, frontier) in current {
261 let Some(frontier) = frontier else {
262 continue;
263 };
264 if source_watermarks.get(source) != Some(&frontier) {
265 return Err(format!(
266 "committed source watermark '{source}' disagrees with channel progress"
267 ));
268 }
269 }
270
271 let replacement = source_watermarks
272 .iter()
273 .map(|(source, watermark)| (source.clone(), *watermark))
274 .collect::<rustc_hash::FxHashMap<_, _>>();
275 let _publication = self.committed_watermark_publication.lock();
276 *self.committed_source_watermarks.write() = Arc::new(replacement);
277 self.cluster_min_watermark
278 .store(cluster_min.unwrap_or(i64::MIN), Ordering::Release);
279 Ok(())
280 }
281}