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