1use std::collections::Bound::Unbounded;
16use std::collections::hash_map::Entry;
17use std::collections::{HashMap, HashSet};
18use std::mem::take;
19use std::ops::{Bound, RangeBounds};
20use std::sync::Arc;
21use std::time::Instant;
22
23use educe::Educe;
24use itertools::Itertools;
25use risingwave_common::util::epoch::EpochPair;
26use risingwave_pb::common::WorkerNode;
27use risingwave_pb::id::{ActorId, PartialGraphId, TableId, WorkerId};
28use risingwave_pb::stream_plan::barrier_mutation::Mutation;
29use risingwave_pb::stream_service::BarrierCompleteResponse;
30use risingwave_pb::stream_service::streaming_control_stream_response::{
31 ResetPartialGraphResponse, Response,
32};
33use tracing::{debug, warn};
34
35use crate::barrier::BarrierKind;
36use crate::barrier::command::PostCollectCommand;
37use crate::barrier::context::GlobalBarrierWorkerContext;
38use crate::barrier::info::BarrierInfo;
39use crate::barrier::notifier::{CollectionNotifier, NotifierStarter};
40use crate::barrier::rpc::{ControlStreamManager, WorkerNodeEvent};
41use crate::barrier::utils::{BarrierItemCollector, NodeToCollect, is_valid_after_worker_err};
42use crate::manager::MetaSrvEnv;
43use crate::model::StreamJobActorsToCreate;
44use crate::{MetaError, MetaResult};
45
46#[derive(Debug)]
47pub(super) struct PartialGraphBarrierInfo {
48 enqueue_time: Instant,
49 pub(super) post_collect_command: PostCollectCommand,
50 pub(super) barrier_info: BarrierInfo,
51 pub(super) notifier: Option<CollectionNotifier>,
52 pub(super) table_ids_to_commit: HashSet<TableId>,
53}
54
55impl PartialGraphBarrierInfo {
56 pub(super) fn new(
57 post_collect_command: PostCollectCommand,
58 barrier_info: BarrierInfo,
59 notifier: Option<&mut NotifierStarter>,
60 table_ids_to_commit: HashSet<TableId>,
61 ) -> Self {
62 Self {
63 enqueue_time: Instant::now(),
64 post_collect_command,
65 barrier_info,
66 notifier: notifier.map(NotifierStarter::add_notify),
67 table_ids_to_commit,
68 }
69 }
70
71 pub(super) fn elapsed_secs(&self) -> f64 {
72 self.enqueue_time.elapsed().as_secs_f64()
73 }
74}
75
76pub(super) trait PartialGraphStat: Send + Sync + 'static {
77 fn observe_barrier_latency(&self, epoch: EpochPair, barrier_latency_secs: f64);
78 fn observe_barrier_num(&self, inflight_barrier_num: usize, collected_barrier_num: usize);
79}
80
81#[derive(Educe)]
82#[educe(Debug)]
83struct PartialGraphRunningState {
84 barrier_item_collector:
85 BarrierItemCollector<WorkerId, BarrierCompleteResponse, PartialGraphBarrierInfo>,
86 completing_epoch: Option<u64>,
87 #[educe(Debug(ignore))]
88 stat: Box<dyn PartialGraphStat>,
89}
90
91impl PartialGraphRunningState {
92 fn new(stat: Box<dyn PartialGraphStat>) -> Self {
93 Self {
94 barrier_item_collector: BarrierItemCollector::new(true),
95 completing_epoch: None,
96 stat,
97 }
98 }
99
100 fn is_empty(&self) -> bool {
101 self.barrier_item_collector.is_empty() && self.completing_epoch.is_none()
102 }
103
104 fn pending_barrier_num(&self) -> usize {
105 self.barrier_item_collector.inflight_barrier_num()
106 + self.barrier_item_collector.collected_barrier_num()
107 + usize::from(self.completing_epoch.is_some())
108 }
109
110 fn enqueue(&mut self, node_to_collect: NodeToCollect, info: PartialGraphBarrierInfo) {
111 let epoch = info.barrier_info.epoch();
112 assert_ne!(info.barrier_info.kind, BarrierKind::Initial);
113 if info.post_collect_command.should_checkpoint() {
114 assert!(info.barrier_info.kind.is_checkpoint());
115 }
116 self.barrier_item_collector
117 .enqueue(epoch, node_to_collect, info);
118 self.stat.observe_barrier_num(
119 self.barrier_item_collector.inflight_barrier_num(),
120 self.barrier_item_collector.collected_barrier_num(),
121 );
122 }
123
124 fn collect(&mut self, resp: BarrierCompleteResponse) {
125 debug!(
126 epoch = resp.epoch,
127 worker_id = %resp.worker_id,
128 partial_graph_id = %resp.partial_graph_id,
129 "collect barrier from worker"
130 );
131 self.barrier_item_collector
132 .collect(resp.epoch, resp.worker_id, resp);
133 }
134
135 fn barrier_collected<'a>(
136 &mut self,
137 temp_ref: &'a CollectedBarrierTempRef,
138 ) -> Option<CollectedBarrier<'a>> {
139 if let Some((epoch, info)) = self.barrier_item_collector.barrier_collected() {
140 self.stat
141 .observe_barrier_latency(epoch, info.elapsed_secs());
142 self.stat.observe_barrier_num(
143 self.barrier_item_collector.inflight_barrier_num(),
144 self.barrier_item_collector.collected_barrier_num(),
145 );
146 Some(temp_ref.collected_barrier(epoch, self.pending_barrier_num()))
147 } else {
148 None
149 }
150 }
151}
152
153#[derive(Debug)]
154struct ResetPartialGraphCollector {
155 remaining_workers: HashSet<WorkerId>,
156 reset_resps: HashMap<WorkerId, ResetPartialGraphResponse>,
157}
158
159impl ResetPartialGraphCollector {
160 fn collect(&mut self, worker_id: WorkerId, resp: ResetPartialGraphResponse) -> bool {
161 assert!(self.remaining_workers.remove(&worker_id));
162 self.reset_resps
163 .try_insert(worker_id, resp)
164 .expect("non-duplicate");
165 self.remaining_workers.is_empty()
166 }
167}
168
169#[derive(Educe)]
170#[educe(Debug)]
171enum PartialGraphStatus {
172 Running {
173 term_id: String,
174 state: PartialGraphRunningState,
175 },
176 Resetting(ResetPartialGraphCollector),
177 Initializing {
178 term_id: String,
179 epoch: EpochPair,
180 node_to_collect: NodeToCollect,
181 #[educe(Debug(ignore))]
182 stat: Option<Box<dyn PartialGraphStat>>,
183 },
184}
185
186impl PartialGraphStatus {
187 fn collect<'a>(
188 &mut self,
189 worker_id: WorkerId,
190 resp: BarrierCompleteResponse,
191 temp_ref: &'a CollectedBarrierTempRef,
192 ) -> Option<PartialGraphEvent<'a>> {
193 assert_eq!(worker_id, resp.worker_id);
194 match self {
195 PartialGraphStatus::Running { state, .. } => {
196 state.collect(resp);
197 state
198 .barrier_collected(temp_ref)
199 .map(PartialGraphEvent::BarrierCollected)
200 }
201 PartialGraphStatus::Resetting(_) => None,
202 PartialGraphStatus::Initializing {
203 term_id,
204 epoch,
205 node_to_collect,
206 stat,
207 } => {
208 assert_eq!(epoch.prev, resp.epoch);
209 assert!(node_to_collect.remove(&worker_id));
210 if node_to_collect.is_empty() {
211 *self = PartialGraphStatus::Running {
212 term_id: take(term_id),
213 state: PartialGraphRunningState::new(
214 stat.take().expect("should be taken for once"),
215 ),
216 };
217 Some(PartialGraphEvent::Initialized)
218 } else {
219 None
220 }
221 }
222 }
223 }
224}
225
226struct CollectedBarrierTempRef {
232 resps: &'static HashMap<WorkerId, BarrierCompleteResponse>,
233}
234
235impl CollectedBarrierTempRef {
236 fn new() -> Self {
237 static EMPTY: std::sync::LazyLock<HashMap<WorkerId, BarrierCompleteResponse>> =
238 std::sync::LazyLock::new(HashMap::new);
239 CollectedBarrierTempRef { resps: &EMPTY }
240 }
241
242 fn collected_barrier(
243 &self,
244 epoch: EpochPair,
245 pending_barrier_num: usize,
246 ) -> CollectedBarrier<'_> {
247 CollectedBarrier {
248 epoch,
249 resps: self.resps,
250 pending_barrier_num,
251 }
252 }
253
254 fn correct_lifetime<'a>(
255 &self,
256 event: PartialGraphManagerEvent<'_>,
257 manager: &'a PartialGraphManager,
258 ) -> PartialGraphManagerEvent<'a> {
259 match event {
260 PartialGraphManagerEvent::PartialGraph(partial_graph_id, event) => {
261 let event = match event {
262 PartialGraphEvent::BarrierCollected(collected) => {
263 let pending_barrier_num = collected.pending_barrier_num;
264 let state = manager.running_graph(partial_graph_id);
265 let (epoch, resps, _) = state
266 .barrier_item_collector
267 .last_collected()
268 .expect("should exist");
269 assert_eq!(epoch, collected.epoch);
270 PartialGraphEvent::BarrierCollected(CollectedBarrier {
271 epoch,
272 resps,
273 pending_barrier_num,
274 })
275 }
276 PartialGraphEvent::Reset(resps) => PartialGraphEvent::Reset(resps),
277 PartialGraphEvent::Initialized => PartialGraphEvent::Initialized,
278 PartialGraphEvent::Error(worker_id) => PartialGraphEvent::Error(worker_id),
279 };
280 PartialGraphManagerEvent::PartialGraph(partial_graph_id, event)
281 }
282 PartialGraphManagerEvent::Worker(worker_id, event) => {
283 PartialGraphManagerEvent::Worker(worker_id, event)
284 }
285 }
286 }
287}
288
289#[derive(Debug)]
290pub(super) struct CollectedBarrier<'a> {
291 pub epoch: EpochPair,
292 pub resps: &'a HashMap<WorkerId, BarrierCompleteResponse>,
293 pub pending_barrier_num: usize,
294}
295
296pub(super) enum PartialGraphEvent<'a> {
297 BarrierCollected(CollectedBarrier<'a>),
298 Reset(HashMap<WorkerId, ResetPartialGraphResponse>),
299 Initialized,
300 Error(WorkerId),
301}
302
303pub(super) enum WorkerEvent {
304 WorkerError {
305 err: MetaError,
306 affected_partial_graphs: HashSet<PartialGraphId>,
307 },
308 WorkerConnected,
309}
310
311fn existing_graphs(
312 graphs: &HashMap<PartialGraphId, PartialGraphStatus>,
313) -> impl Iterator<Item = (PartialGraphId, &str)> + '_ {
314 graphs
315 .iter()
316 .filter_map(|(partial_graph_id, status)| match status {
317 PartialGraphStatus::Running { term_id, .. }
318 | PartialGraphStatus::Initializing { term_id, .. } => {
319 Some((*partial_graph_id, term_id.as_str()))
320 }
321 PartialGraphStatus::Resetting(_) => None,
322 })
323}
324
325pub(super) struct PartialGraphManager {
326 control_stream_manager: ControlStreamManager,
327 graphs: HashMap<PartialGraphId, PartialGraphStatus>,
328}
329
330impl PartialGraphManager {
331 pub(super) fn uninitialized(env: MetaSrvEnv) -> Self {
332 Self {
333 control_stream_manager: ControlStreamManager::new(env),
334 graphs: HashMap::new(),
335 }
336 }
337
338 pub(super) async fn recover(
339 env: MetaSrvEnv,
340 nodes: &HashMap<WorkerId, WorkerNode>,
341 context: Arc<impl GlobalBarrierWorkerContext>,
342 ) -> Self {
343 let control_stream_manager = ControlStreamManager::recover(env, nodes, context).await;
344 Self {
345 control_stream_manager,
346 graphs: Default::default(),
347 }
348 }
349
350 pub(super) fn control_stream_manager(&self) -> &ControlStreamManager {
351 &self.control_stream_manager
352 }
353
354 pub(super) async fn add_worker(
355 &mut self,
356 node: WorkerNode,
357 context: Arc<impl GlobalBarrierWorkerContext>,
358 ) {
359 self.control_stream_manager
360 .add_worker(node, existing_graphs(&self.graphs), context)
361 .await
362 }
363
364 pub(super) fn remove_worker(&mut self, node: WorkerNode) {
365 self.control_stream_manager.remove_worker(node);
366 }
367
368 pub(super) fn clear_worker(&mut self) {
369 self.control_stream_manager.clear();
370 }
371
372 pub(crate) fn notify_all_err(&mut self, err: &MetaError) {
373 for (_, graph) in self.graphs.drain() {
374 if let PartialGraphStatus::Running { state, .. } = graph {
375 for info in state.barrier_item_collector.into_infos() {
376 if let Some(notifier) = info.notifier {
377 notifier.notify_collection_failed(err.clone());
378 }
379 }
380 }
381 }
382 }
383}
384
385#[must_use]
386pub(super) struct PartialGraphAdder<'a> {
387 partial_graph_id: PartialGraphId,
388 manager: &'a mut PartialGraphManager,
389 consumed: bool,
390}
391
392impl PartialGraphAdder<'_> {
393 pub(super) fn added(mut self) {
394 self.consumed = true;
395 }
396
397 pub(super) fn failed(mut self) {
398 self.manager.reset_partial_graphs([self.partial_graph_id]);
399 self.consumed = true;
400 }
401
402 pub(super) fn manager(&mut self) -> &mut PartialGraphManager {
403 self.manager
404 }
405}
406
407impl Drop for PartialGraphAdder<'_> {
408 fn drop(&mut self) {
409 debug_assert!(self.consumed, "unconsumed graph adder");
410 if !self.consumed {
411 warn!(partial_graph_id = %self.partial_graph_id, "unconsumed graph adder");
412 }
413 }
414}
415
416impl PartialGraphManager {
417 pub(super) fn add_partial_graph(
418 &mut self,
419 partial_graph_id: PartialGraphId,
420 term_id: &str,
421 stat: impl PartialGraphStat,
422 ) -> PartialGraphAdder<'_> {
423 self.graphs
424 .try_insert(
425 partial_graph_id,
426 PartialGraphStatus::Running {
427 term_id: term_id.to_owned(),
428 state: PartialGraphRunningState::new(Box::new(stat)),
429 },
430 )
431 .expect("non-duplicated");
432 self.control_stream_manager
433 .add_partial_graph(partial_graph_id, term_id);
434 PartialGraphAdder {
435 partial_graph_id,
436 manager: self,
437 consumed: false,
438 }
439 }
440
441 pub(super) fn remove_partial_graphs(&mut self, partial_graphs: Vec<PartialGraphId>) {
442 for partial_graph_id in &partial_graphs {
443 let graph = self.graphs.remove(partial_graph_id).expect("should exist");
444 let PartialGraphStatus::Running { state, .. } = graph else {
445 panic!("graph to be explicitly removed should be running");
446 };
447 assert!(state.is_empty());
448 }
449 self.control_stream_manager
450 .remove_partial_graphs(partial_graphs);
451 }
452
453 pub(super) fn reset_partial_graphs(
454 &mut self,
455 partial_graph_ids: impl IntoIterator<Item = PartialGraphId>,
456 ) {
457 let partial_graph_ids = partial_graph_ids.into_iter().collect_vec();
458 let remaining_workers = self
459 .control_stream_manager
460 .reset_partial_graphs(partial_graph_ids.clone());
461 let new_collector = || ResetPartialGraphCollector {
462 remaining_workers: remaining_workers.clone(),
463 reset_resps: Default::default(),
464 };
465 for partial_graph_id in partial_graph_ids {
466 match self.graphs.entry(partial_graph_id) {
467 Entry::Vacant(entry) => {
468 entry.insert(PartialGraphStatus::Resetting(new_collector()));
469 }
470 Entry::Occupied(mut entry) => {
471 let graph = entry.get_mut();
472 match graph {
473 PartialGraphStatus::Resetting(_) => {
474 unreachable!("should not reset again")
475 }
476 PartialGraphStatus::Running { .. }
477 | PartialGraphStatus::Initializing { .. } => {
478 *graph = PartialGraphStatus::Resetting(new_collector());
479 }
480 }
481 }
482 }
483 }
484 }
485
486 pub(super) fn assert_resetting(&self, partial_graph_id: PartialGraphId) {
487 let graph = self.graphs.get(&partial_graph_id).expect("should exist");
488 let PartialGraphStatus::Resetting(..) = graph else {
489 panic!("should be at resetting but at {:?}", graph);
490 };
491 }
492
493 pub(super) fn inject_barrier(
494 &mut self,
495 partial_graph_id: PartialGraphId,
496 mutation: Option<Mutation>,
497 node_actors: &HashMap<WorkerId, HashSet<ActorId>>,
498 table_ids_to_sync: impl Iterator<Item = TableId>,
499 nodes_to_sync_table: impl Iterator<Item = WorkerId>,
500 new_actors: Option<StreamJobActorsToCreate>,
501 info: PartialGraphBarrierInfo,
502 ) -> MetaResult<()> {
503 let graph = self
504 .graphs
505 .get_mut(&partial_graph_id)
506 .expect("should exist");
507 let node_to_collect = self.control_stream_manager.inject_barrier(
508 partial_graph_id,
509 mutation,
510 &info.barrier_info,
511 node_actors,
512 table_ids_to_sync,
513 nodes_to_sync_table,
514 new_actors,
515 )?;
516 let PartialGraphStatus::Running { state, .. } = graph else {
517 panic!("should not inject barrier on non-running status: {graph:?}")
518 };
519 state.enqueue(node_to_collect, info);
520 Ok(())
521 }
522
523 fn running_graph(&self, partial_graph_id: PartialGraphId) -> &PartialGraphRunningState {
524 let PartialGraphStatus::Running { state: graph, .. } = &self.graphs[&partial_graph_id]
525 else {
526 unreachable!("should be running")
527 };
528 graph
529 }
530
531 fn running_graph_mut(
532 &mut self,
533 partial_graph_id: PartialGraphId,
534 ) -> &mut PartialGraphRunningState {
535 let PartialGraphStatus::Running { state: graph, .. } = self
536 .graphs
537 .get_mut(&partial_graph_id)
538 .expect("should exist")
539 else {
540 unreachable!("should be running")
541 };
542 graph
543 }
544
545 pub(super) fn pending_barrier_num(&self, partial_graph_id: PartialGraphId) -> usize {
546 self.running_graph(partial_graph_id).pending_barrier_num()
547 }
548
549 pub(super) fn first_inflight_barrier(
550 &self,
551 partial_graph_id: PartialGraphId,
552 ) -> Option<EpochPair> {
553 self.running_graph(partial_graph_id)
554 .barrier_item_collector
555 .first_inflight_epoch()
556 }
557
558 pub(super) fn pending_barrier_infos(
559 &self,
560 partial_graph_id: PartialGraphId,
561 ) -> impl Iterator<Item = &BarrierInfo> {
562 self.running_graph(partial_graph_id)
563 .barrier_item_collector
564 .iter_infos()
565 .map(|info| &info.barrier_info)
566 }
567
568 pub(super) fn start_completing(
569 &mut self,
570 partial_graph_id: PartialGraphId,
571 epoch_end_bound: Bound<u64>,
572 mut on_non_checkpoint_epoch: impl FnMut(
573 EpochPair,
574 HashMap<WorkerId, BarrierCompleteResponse>,
575 PostCollectCommand,
576 ),
577 ) -> Option<(
578 u64,
579 HashMap<WorkerId, BarrierCompleteResponse>,
580 PartialGraphBarrierInfo,
581 )> {
582 let graph = self.running_graph_mut(partial_graph_id);
583 assert!(graph.completing_epoch.is_none());
584 let epoch_range: (Bound<u64>, Bound<u64>) = (Unbounded, epoch_end_bound);
585 while let Some((epoch, resps, info)) = graph
586 .barrier_item_collector
587 .take_collected_if(|epoch| epoch_range.contains(&epoch.prev))
588 {
589 if info.post_collect_command.should_checkpoint() {
590 assert!(info.barrier_info.kind.is_checkpoint());
591 } else if !info.barrier_info.kind.is_checkpoint() {
592 if let Some(notifier) = info.notifier {
593 notifier.notify_collected();
594 }
595 on_non_checkpoint_epoch(epoch, resps, info.post_collect_command);
596 continue;
597 }
598 let prev_epoch = info.barrier_info.prev_epoch();
599 graph.completing_epoch = Some(prev_epoch);
600 return Some((prev_epoch, resps, info));
601 }
602 None
603 }
604
605 pub(super) fn ack_completed(&mut self, partial_graph_id: PartialGraphId, prev_epoch: u64) {
606 assert_eq!(
607 self.running_graph_mut(partial_graph_id)
608 .completing_epoch
609 .take(),
610 Some(prev_epoch)
611 );
612 }
613
614 pub(super) fn has_pending_checkpoint_barrier(&self, partial_graph_id: PartialGraphId) -> bool {
615 self.running_graph(partial_graph_id)
616 .barrier_item_collector
617 .iter_infos()
618 .any(|info| info.barrier_info.kind.is_checkpoint())
619 }
620
621 pub(super) fn start_recover(&mut self) -> PartialGraphRecoverer<'_> {
622 PartialGraphRecoverer {
623 added_partial_graphs: Default::default(),
624 manager: self,
625 consumed: false,
626 }
627 }
628}
629
630#[must_use]
631pub(super) struct PartialGraphRecoverer<'a> {
632 added_partial_graphs: HashSet<PartialGraphId>,
633 manager: &'a mut PartialGraphManager,
634 consumed: bool,
635}
636
637impl PartialGraphRecoverer<'_> {
638 pub(super) fn recover_graph(
639 &mut self,
640 partial_graph_id: PartialGraphId,
641 term_id: &str,
642 mutation: Mutation,
643 barrier_info: &BarrierInfo,
644 node_actors: &HashMap<WorkerId, HashSet<ActorId>>,
645 table_ids_to_sync: impl Iterator<Item = TableId>,
646 new_actors: StreamJobActorsToCreate,
647 stat: impl PartialGraphStat,
648 ) -> MetaResult<()> {
649 assert!(
650 self.added_partial_graphs.insert(partial_graph_id),
651 "duplicated recover graph {partial_graph_id}"
652 );
653 self.manager
654 .control_stream_manager
655 .add_partial_graph(partial_graph_id, term_id);
656 assert!(barrier_info.kind.is_initial());
657 let node_to_collect = self.manager.control_stream_manager.inject_barrier(
658 partial_graph_id,
659 Some(mutation),
660 barrier_info,
661 node_actors,
662 table_ids_to_sync,
663 node_actors.keys().copied(),
664 Some(new_actors),
665 )?;
666 self.manager
667 .graphs
668 .try_insert(
669 partial_graph_id,
670 PartialGraphStatus::Initializing {
671 term_id: term_id.to_owned(),
672 epoch: barrier_info.epoch(),
673 node_to_collect,
674 stat: Some(Box::new(stat)),
675 },
676 )
677 .expect("non-duplicated");
678 Ok(())
679 }
680
681 pub(super) fn control_stream_manager(&self) -> &ControlStreamManager {
682 &self.manager.control_stream_manager
683 }
684
685 pub(super) fn all_initializing(mut self) -> HashSet<PartialGraphId> {
686 self.consumed = true;
687 take(&mut self.added_partial_graphs)
688 }
689
690 pub(super) fn failed(mut self) -> HashSet<PartialGraphId> {
691 self.manager
692 .reset_partial_graphs(self.added_partial_graphs.iter().copied());
693 self.consumed = true;
694 take(&mut self.added_partial_graphs)
695 }
696}
697
698impl Drop for PartialGraphRecoverer<'_> {
699 fn drop(&mut self) {
700 debug_assert!(self.consumed, "unconsumed graph recoverer");
701 if !self.consumed {
702 warn!(partial_graph_ids = ?self.added_partial_graphs, "unconsumed graph recoverer");
703 }
704 }
705}
706
707#[must_use]
708pub(super) enum PartialGraphManagerEvent<'a> {
709 PartialGraph(PartialGraphId, PartialGraphEvent<'a>),
710 Worker(WorkerId, WorkerEvent),
711}
712
713impl PartialGraphManager {
714 pub(super) async fn next_event<'a>(
715 &'a mut self,
716 context: &Arc<impl GlobalBarrierWorkerContext>,
717 ) -> PartialGraphManagerEvent<'a> {
718 let temp_ref = CollectedBarrierTempRef::new();
719 let event = self.next_event_inner(context, &temp_ref).await;
720 temp_ref.correct_lifetime(event, self)
721 }
722
723 async fn next_event_inner<'a>(
724 &mut self,
725 context: &Arc<impl GlobalBarrierWorkerContext>,
726 temp_ref: &'a CollectedBarrierTempRef,
727 ) -> PartialGraphManagerEvent<'a> {
728 for (&partial_graph_id, graph) in &mut self.graphs {
729 match graph {
730 PartialGraphStatus::Running { state, .. } => {
731 if let Some(collected) = state.barrier_collected(temp_ref) {
732 return PartialGraphManagerEvent::PartialGraph(
733 partial_graph_id,
734 PartialGraphEvent::BarrierCollected(collected),
735 );
736 }
737 }
738 PartialGraphStatus::Resetting(collector) => {
739 if collector.remaining_workers.is_empty() {
740 let resps = take(&mut collector.reset_resps);
741 self.graphs.remove(&partial_graph_id);
742 return PartialGraphManagerEvent::PartialGraph(
743 partial_graph_id,
744 PartialGraphEvent::Reset(resps),
745 );
746 }
747 }
748 PartialGraphStatus::Initializing {
749 term_id,
750 node_to_collect,
751 stat,
752 ..
753 } => {
754 if node_to_collect.is_empty() {
755 *graph = PartialGraphStatus::Running {
756 term_id: take(term_id),
757 state: PartialGraphRunningState::new(
758 stat.take().expect("should be taken once"),
759 ),
760 };
761 return PartialGraphManagerEvent::PartialGraph(
762 partial_graph_id,
763 PartialGraphEvent::Initialized,
764 );
765 }
766 }
767 }
768 }
769 loop {
770 let (worker_id, event) = self.control_stream_manager.next_event(context).await;
771 match event {
772 WorkerNodeEvent::Response(result) => match result {
773 Ok(resp) => match resp {
774 Response::CompleteBarrier(resp) => {
775 let partial_graph_id = resp.partial_graph_id;
776 if let Some(event) = self
777 .graphs
778 .get_mut(&partial_graph_id)
779 .expect("should exist")
780 .collect(worker_id, resp, temp_ref)
781 {
782 return PartialGraphManagerEvent::PartialGraph(
783 partial_graph_id,
784 event,
785 );
786 }
787 }
788 Response::ReportPartialGraphFailure(resp) => {
789 let partial_graph_id = resp.partial_graph_id;
790 let graph = self
791 .graphs
792 .get_mut(&partial_graph_id)
793 .expect("should exist");
794 match graph {
795 PartialGraphStatus::Resetting(_) => {
796 }
798 PartialGraphStatus::Running { .. }
799 | PartialGraphStatus::Initializing { .. } => {
800 return PartialGraphManagerEvent::PartialGraph(
801 partial_graph_id,
802 PartialGraphEvent::Error(worker_id),
803 );
804 }
805 }
806 }
807 Response::ResetPartialGraph(resp) => {
808 let partial_graph_id = resp.partial_graph_id;
809 let graph = self
810 .graphs
811 .get_mut(&partial_graph_id)
812 .expect("should exist");
813 match graph {
814 PartialGraphStatus::Running { .. }
815 | PartialGraphStatus::Initializing { .. } => {
816 if cfg!(debug_assertions) {
817 unreachable!(
818 "should not have reset request when not in resetting state"
819 )
820 } else {
821 warn!(
822 ?resp,
823 "ignore reset resp when not in Resetting state"
824 );
825 }
826 }
827 PartialGraphStatus::Resetting(collector) => {
828 if collector.collect(worker_id, resp) {
829 let resps = take(&mut collector.reset_resps);
830 self.graphs.remove(&partial_graph_id);
831 return PartialGraphManagerEvent::PartialGraph(
832 partial_graph_id,
833 PartialGraphEvent::Reset(resps),
834 );
835 }
836 }
837 }
838 }
839 Response::Init(_) | Response::Shutdown(_) => {
840 unreachable!("should be handled in control stream manager")
841 }
842 },
843 Err(error) => {
844 let affected_partial_graphs = self
845 .graphs
846 .iter_mut()
847 .filter_map(|(partial_graph_id, graph)| match graph {
848 PartialGraphStatus::Running { state, .. } => state
849 .barrier_item_collector
850 .iter_to_collect()
851 .any(|to_collect| {
852 !is_valid_after_worker_err(to_collect, worker_id)
853 })
854 .then_some(*partial_graph_id),
855 PartialGraphStatus::Resetting(collector) => {
856 collector.remaining_workers.remove(&worker_id);
857 None
858 }
859 PartialGraphStatus::Initializing {
860 node_to_collect, ..
861 } => (!is_valid_after_worker_err(node_to_collect, worker_id))
862 .then_some(*partial_graph_id),
863 })
864 .collect();
865 return PartialGraphManagerEvent::Worker(
866 worker_id,
867 WorkerEvent::WorkerError {
868 err: error,
869 affected_partial_graphs,
870 },
871 );
872 }
873 },
874 WorkerNodeEvent::Connected(connected) => {
875 connected.initialize(existing_graphs(&self.graphs));
876 return PartialGraphManagerEvent::Worker(
877 worker_id,
878 WorkerEvent::WorkerConnected,
879 );
880 }
881 }
882 }
883 }
884}
885
886#[cfg(test)]
887mod tests {
888 use risingwave_common::util::epoch::Epoch;
889
890 use super::*;
891 use crate::barrier::TracedEpoch;
892
893 struct TestPartialGraphStat;
894
895 impl PartialGraphStat for TestPartialGraphStat {
896 fn observe_barrier_latency(&self, _epoch: EpochPair, _barrier_latency_secs: f64) {}
897
898 fn observe_barrier_num(&self, _inflight_barrier_num: usize, _collected_barrier_num: usize) {
899 }
900 }
901
902 fn barrier_info(prev_epoch: u64, curr_epoch: u64) -> PartialGraphBarrierInfo {
903 PartialGraphBarrierInfo::new(
904 PostCollectCommand::barrier(),
905 BarrierInfo {
906 prev_epoch: TracedEpoch::new(Epoch(prev_epoch)),
907 curr_epoch: TracedEpoch::new(Epoch(curr_epoch)),
908 kind: BarrierKind::Barrier,
909 },
910 None,
911 HashSet::new(),
912 )
913 }
914
915 #[test]
916 fn test_pending_barrier_num_includes_collected_and_completing() {
917 let mut state = PartialGraphRunningState::new(Box::new(TestPartialGraphStat));
918 let worker_id: WorkerId = 1.into();
919 state.barrier_item_collector.enqueue(
920 EpochPair::new(2, 1),
921 HashSet::from([worker_id]),
922 barrier_info(1, 2),
923 );
924 state.barrier_item_collector.enqueue(
925 EpochPair::new(3, 2),
926 HashSet::from([worker_id]),
927 barrier_info(2, 3),
928 );
929 assert_eq!(state.pending_barrier_num(), 2);
930
931 state
932 .barrier_item_collector
933 .collect(1, worker_id, BarrierCompleteResponse::default());
934 state.barrier_item_collector.barrier_collected();
935 assert_eq!(state.pending_barrier_num(), 2);
936
937 state
938 .barrier_item_collector
939 .take_collected_if(|epoch| epoch.prev == 1)
940 .expect("the first barrier should be collected");
941 state.completing_epoch = Some(1);
942 assert_eq!(state.pending_barrier_num(), 2);
943 }
944}