1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
16use std::fmt::{Debug, Display, Formatter};
17use std::future::{Future, pending, poll_fn};
18use std::mem::replace;
19use std::sync::Arc;
20use std::task::{Context, Poll};
21use std::time::{Duration, Instant};
22
23use anyhow::anyhow;
24use futures::future::BoxFuture;
25use futures::stream::{FuturesOrdered, FuturesUnordered};
26use futures::{FutureExt, StreamExt};
27use prometheus::HistogramTimer;
28use risingwave_common::catalog::TableId;
29use risingwave_common::id::{SinkId, SourceId};
30use risingwave_common::metrics::{LabelGuardedHistogram, LabelGuardedIntCounter};
31use risingwave_common::util::epoch::EpochPair;
32use risingwave_pb::connector_service::SinkMetadata;
33use risingwave_pb::stream_plan::barrier::BarrierKind;
34use risingwave_pb::stream_service::barrier_complete_response::{
35 IcebergPkIndexSinkMetadata as PbIcebergPkIndexSinkMetadata, PbCdcSourceOffsetUpdated,
36 PbListFinishedSource, PbLoadFinishedSource, PbRefreshFinishedActor,
37};
38use risingwave_storage::StateStoreImpl;
39use tokio::sync::mpsc;
40use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
41use tokio::task::JoinHandle;
42
43use crate::error::{StreamError, StreamResult};
44use crate::executor::Barrier;
45use crate::executor::monitor::StreamingMetrics;
46use crate::task::progress::{BackfillState, TableChangeLogProgress};
47use crate::task::{
48 ActorId, LocalBarrierEvent, LocalBarrierManager, NewOutputRequest, PartialGraphId,
49 StreamActorManager, UpDownActorIds,
50};
51
52struct IssuedState {
53 pub remaining_actors: BTreeSet<ActorId>,
55
56 pub barrier_inflight_latency: HistogramTimer,
57}
58
59impl Debug for IssuedState {
60 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("IssuedState")
62 .field("remaining_actors", &self.remaining_actors)
63 .finish()
64 }
65}
66
67#[derive(Debug)]
69enum ManagedBarrierStateInner {
70 Issued(IssuedState),
72
73 AllCollected(EpochBarrierEvent),
75}
76
77#[derive(Debug)]
78struct BarrierState {
79 barrier: Barrier,
80 table_ids: Option<HashSet<TableId>>,
82 inner: ManagedBarrierStateInner,
83}
84
85use risingwave_common::must_match;
86use risingwave_pb::id::FragmentId;
87use risingwave_pb::stream_service::{InjectBarrierRequest, PbIcebergPkIndexSinkRole};
88
89use crate::executor::exchange::permit;
90use crate::task::barrier_worker::await_epoch_completed_future::AwaitEpochCompletedFuture;
91use crate::task::barrier_worker::{ScoredStreamError, TakeReceiverRequest};
92use crate::task::cdc_progress::CdcTableBackfillState;
93
94pub(super) struct ManagedBarrierStateDebugInfo<'a> {
95 running_actors: BTreeSet<ActorId>,
96 graph_state: &'a PartialGraphManagedBarrierState,
97}
98
99impl Display for ManagedBarrierStateDebugInfo<'_> {
100 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101 write!(f, "running_actors: ")?;
102 for actor_id in &self.running_actors {
103 write!(f, "{}, ", actor_id)?;
104 }
105 {
106 writeln!(f, "graph states")?;
107 write!(f, "{}", self.graph_state)?;
108 }
109 Ok(())
110 }
111}
112
113impl Display for &'_ PartialGraphManagedBarrierState {
114 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115 let mut prev_epoch = 0u64;
116 for (epoch, barrier_state) in &self.epoch_barrier_state_map {
117 write!(f, "> Epoch {}: ", epoch)?;
118 match &barrier_state.inner {
119 ManagedBarrierStateInner::Issued(state) => {
120 write!(
121 f,
122 "Issued [{:?}]. Remaining actors: [",
123 barrier_state.barrier.kind
124 )?;
125 let mut is_prev_epoch_issued = false;
126 if prev_epoch != 0 {
127 let bs = &self.epoch_barrier_state_map[&prev_epoch];
128 if let ManagedBarrierStateInner::Issued(IssuedState {
129 remaining_actors: remaining_actors_prev,
130 ..
131 }) = &bs.inner
132 {
133 is_prev_epoch_issued = true;
135 let mut duplicates = 0usize;
136 for actor_id in &state.remaining_actors {
137 if !remaining_actors_prev.contains(actor_id) {
138 write!(f, "{}, ", actor_id)?;
139 } else {
140 duplicates += 1;
141 }
142 }
143 if duplicates > 0 {
144 write!(f, "...and {} actors in prev epoch", duplicates)?;
145 }
146 }
147 }
148 if !is_prev_epoch_issued {
149 for actor_id in &state.remaining_actors {
150 write!(f, "{}, ", actor_id)?;
151 }
152 }
153 write!(f, "]")?;
154 }
155 ManagedBarrierStateInner::AllCollected(_) => {
156 write!(f, "AllCollected")?;
157 }
158 }
159 prev_epoch = *epoch;
160 writeln!(f)?;
161 }
162
163 if self
164 .barrier_events
165 .values()
166 .any(|event| !event.create_mview_progress.is_empty())
167 {
168 writeln!(f, "Create MView Progress:")?;
169 for (epoch, event) in self
170 .barrier_events
171 .iter()
172 .filter(|(_, event)| !event.create_mview_progress.is_empty())
173 {
174 write!(f, "> Epoch {}:", epoch)?;
175 for (actor_id, (_, state)) in &event.create_mview_progress {
176 write!(f, ">> Actor {}: {}, ", actor_id, state)?;
177 }
178 }
179 }
180
181 Ok(())
182 }
183}
184
185enum InflightActorStatus {
186 IssuedFirst(Vec<Barrier>),
188 Running(u64),
190}
191
192impl InflightActorStatus {
193 fn max_issued_epoch(&self) -> u64 {
194 match self {
195 InflightActorStatus::Running(epoch) => *epoch,
196 InflightActorStatus::IssuedFirst(issued_barriers) => {
197 issued_barriers.last().expect("non-empty").epoch.prev
198 }
199 }
200 }
201}
202
203pub(crate) struct InflightActorState {
204 actor_id: ActorId,
205 barrier_senders: Vec<mpsc::UnboundedSender<Barrier>>,
206 pub(in crate::task) inflight_barriers: VecDeque<u64>,
208 status: InflightActorStatus,
209 is_stopping: bool,
211
212 new_output_request_tx: UnboundedSender<(ActorId, NewOutputRequest)>,
213 join_handle: JoinHandle<()>,
214 monitor_task_handle: Option<JoinHandle<()>>,
215}
216
217impl InflightActorState {
218 pub(super) fn start(
219 actor_id: ActorId,
220 initial_barrier: &Barrier,
221 new_output_request_tx: UnboundedSender<(ActorId, NewOutputRequest)>,
222 join_handle: JoinHandle<()>,
223 monitor_task_handle: Option<JoinHandle<()>>,
224 ) -> Self {
225 Self {
226 actor_id,
227 barrier_senders: vec![],
228 inflight_barriers: VecDeque::from_iter([initial_barrier.epoch.prev]),
229 status: InflightActorStatus::IssuedFirst(vec![initial_barrier.clone()]),
230 is_stopping: false,
231 new_output_request_tx,
232 join_handle,
233 monitor_task_handle,
234 }
235 }
236
237 pub(super) fn issue_barrier(&mut self, barrier: &Barrier, is_stop: bool) -> StreamResult<()> {
238 assert!(barrier.epoch.prev > self.status.max_issued_epoch());
239
240 for barrier_sender in &self.barrier_senders {
241 barrier_sender.send(barrier.clone()).map_err(|_| {
242 StreamError::barrier_send(
243 barrier.clone(),
244 self.actor_id,
245 "failed to send to registered sender",
246 )
247 })?;
248 }
249
250 if let Some(prev_epoch) = self.inflight_barriers.back() {
251 assert!(*prev_epoch < barrier.epoch.prev);
252 }
253 self.inflight_barriers.push_back(barrier.epoch.prev);
254
255 match &mut self.status {
256 InflightActorStatus::IssuedFirst(pending_barriers) => {
257 pending_barriers.push(barrier.clone());
258 }
259 InflightActorStatus::Running(prev_epoch) => {
260 *prev_epoch = barrier.epoch.prev;
261 }
262 };
263
264 if is_stop {
265 assert!(!self.is_stopping, "stopped actor should not issue barrier");
266 self.is_stopping = true;
267 }
268 Ok(())
269 }
270
271 pub(super) fn collect(&mut self, epoch: EpochPair) -> bool {
272 let prev_epoch = self.inflight_barriers.pop_front().expect("should exist");
273 assert_eq!(prev_epoch, epoch.prev);
274 match &self.status {
275 InflightActorStatus::IssuedFirst(pending_barriers) => {
276 assert_eq!(
277 prev_epoch,
278 pending_barriers.first().expect("non-empty").epoch.prev
279 );
280 self.status = InflightActorStatus::Running(
281 pending_barriers.last().expect("non-empty").epoch.prev,
282 );
283 }
284 InflightActorStatus::Running(_) => {}
285 }
286
287 self.inflight_barriers.is_empty() && self.is_stopping
288 }
289}
290
291#[derive(Debug)]
293pub(crate) struct EpochBarrierEvent {
294 pub(crate) epoch: EpochPair,
297 pub(crate) create_mview_progress: HashMap<ActorId, (FragmentId, BackfillState)>,
299 pub(crate) table_change_log_progress: Vec<TableChangeLogProgress>,
301 pub(crate) list_finished_source_ids: Vec<PbListFinishedSource>,
303 pub(crate) load_finished_source_ids: Vec<PbLoadFinishedSource>,
305 pub(crate) cdc_table_backfill_progress: HashMap<ActorId, CdcTableBackfillState>,
307 pub(crate) cdc_source_offset_updated: Vec<PbCdcSourceOffsetUpdated>,
309 pub(crate) iceberg_pk_index_sink_metadata: Vec<PbIcebergPkIndexSinkMetadata>,
311 pub(crate) refresh_finished_actors: Vec<PbRefreshFinishedActor>,
313}
314
315impl EpochBarrierEvent {
316 fn new(epoch: EpochPair) -> Self {
317 Self {
318 epoch,
319 create_mview_progress: Default::default(),
320 table_change_log_progress: Default::default(),
321 list_finished_source_ids: Default::default(),
322 load_finished_source_ids: Default::default(),
323 cdc_table_backfill_progress: Default::default(),
324 cdc_source_offset_updated: Default::default(),
325 iceberg_pk_index_sink_metadata: Default::default(),
326 refresh_finished_actors: Default::default(),
327 }
328 }
329}
330
331pub(crate) struct PartialGraphManagedBarrierState {
332 epoch_barrier_state_map: BTreeMap<u64, BarrierState>,
336
337 prev_barrier_table_ids: Option<(EpochPair, HashSet<TableId>)>,
338
339 barrier_events: HashMap<u64, EpochBarrierEvent>,
347
348 state_store: StateStoreImpl,
349
350 barrier_inflight_latency: LabelGuardedHistogram,
351 barrier_sync_latency: LabelGuardedHistogram,
352 barrier_manager_progress: LabelGuardedIntCounter,
353}
354
355impl PartialGraphManagedBarrierState {
356 pub(super) fn new(
357 actor_manager: &StreamActorManager,
358 partial_graph_id: PartialGraphId,
359 ) -> Self {
360 Self::new_inner(
361 actor_manager.env.state_store(),
362 actor_manager.streaming_metrics.clone(),
363 partial_graph_id,
364 )
365 }
366
367 fn new_inner(
368 state_store: StateStoreImpl,
369 streaming_metrics: Arc<StreamingMetrics>,
370 partial_graph_id: PartialGraphId,
371 ) -> Self {
372 fn partial_graph_name(partial_graph_id: PartialGraphId) -> String {
373 let raw_partial_graph_id = partial_graph_id.as_raw_id();
376 let database_id = raw_partial_graph_id >> 32;
377 let raw_job_id = raw_partial_graph_id as u32;
378 if raw_job_id == u32::MAX {
379 format!("database {database_id}")
380 } else {
381 format!("database {database_id} job {raw_job_id}")
382 }
383 }
384
385 let partial_graph_name = partial_graph_name(partial_graph_id);
386 let barrier_inflight_latency = streaming_metrics
387 .barrier_inflight_latency
388 .with_guarded_label_values(&[&partial_graph_name]);
389 let barrier_sync_latency = streaming_metrics
390 .barrier_sync_latency
391 .with_guarded_label_values(&[&partial_graph_name]);
392 let barrier_manager_progress = streaming_metrics
393 .barrier_manager_progress
394 .with_guarded_label_values(&[&partial_graph_name]);
395 Self {
396 epoch_barrier_state_map: Default::default(),
397 prev_barrier_table_ids: None,
398 barrier_events: Default::default(),
399 state_store,
400 barrier_inflight_latency,
401 barrier_sync_latency,
402 barrier_manager_progress,
403 }
404 }
405
406 #[cfg(test)]
407 pub(crate) fn for_test() -> Self {
408 Self::new_inner(
409 StateStoreImpl::for_test(),
410 Arc::new(StreamingMetrics::unused()),
411 PartialGraphId::new(0),
412 )
413 }
414
415 pub(crate) fn event_mut(&mut self, epoch: EpochPair) -> &mut EpochBarrierEvent {
416 let event = self
417 .barrier_events
418 .entry(epoch.curr)
419 .or_insert_with(|| EpochBarrierEvent::new(epoch));
420 assert_eq!(
421 event.epoch, epoch,
422 "events for the same current epoch must have the same previous epoch"
423 );
424 event
425 }
426
427 pub(super) fn is_empty(&self) -> bool {
428 self.epoch_barrier_state_map.is_empty()
429 }
430}
431
432pub(crate) struct SuspendedPartialGraphState {
433 pub(super) suspend_time: Instant,
434 inner: PartialGraphState,
435 failure: Option<(Option<ActorId>, StreamError)>,
436}
437
438impl SuspendedPartialGraphState {
439 fn new(
440 state: PartialGraphState,
441 failure: Option<(Option<ActorId>, StreamError)>,
442 _completing_futures: Option<FuturesOrdered<AwaitEpochCompletedFuture>>, ) -> Self {
444 Self {
445 suspend_time: Instant::now(),
446 inner: state,
447 failure,
448 }
449 }
450
451 async fn reset(mut self) -> ResetPartialGraphOutput {
452 let root_err = self.inner.try_find_root_actor_failure(self.failure).await;
453 self.inner.abort_and_wait_actors().await;
454 ResetPartialGraphOutput { root_err }
455 }
456}
457
458pub(crate) struct ResetPartialGraphOutput {
459 pub(crate) root_err: Option<ScoredStreamError>,
460}
461
462pub(in crate::task) enum PartialGraphStatus {
463 ReceivedExchangeRequest(Vec<(String, UpDownActorIds, TakeReceiverRequest)>),
464 Running(PartialGraphState),
465 Suspended(SuspendedPartialGraphState),
466 Resetting,
467 Unspecified,
469}
470
471impl PartialGraphStatus {
472 pub(crate) async fn abort(&mut self) {
473 match self {
474 PartialGraphStatus::ReceivedExchangeRequest(pending_requests) => {
475 for (_, _, request) in pending_requests.drain(..) {
476 if let TakeReceiverRequest::Remote { result_sender, .. } = request {
477 let _ = result_sender.send(Err(anyhow!("partial graph aborted").into()));
478 }
479 }
480 }
481 PartialGraphStatus::Running(state) => {
482 state.abort_and_wait_actors().await;
483 }
484 PartialGraphStatus::Suspended(SuspendedPartialGraphState { inner: state, .. }) => {
485 state.abort_and_wait_actors().await;
486 }
487 PartialGraphStatus::Resetting => {}
488 PartialGraphStatus::Unspecified => {
489 unreachable!()
490 }
491 }
492 }
493
494 pub(crate) fn state_for_request(&mut self) -> Option<&mut PartialGraphState> {
495 match self {
496 PartialGraphStatus::ReceivedExchangeRequest(_) => {
497 unreachable!("should not handle request")
498 }
499 PartialGraphStatus::Running(state) => Some(state),
500 PartialGraphStatus::Suspended(_) => None,
501 PartialGraphStatus::Resetting => {
502 unreachable!("should not receive further request during cleaning")
503 }
504 PartialGraphStatus::Unspecified => {
505 unreachable!()
506 }
507 }
508 }
509
510 pub(super) fn poll_next_event(
511 &mut self,
512 cx: &mut Context<'_>,
513 ) -> Poll<ManagedBarrierStateEvent> {
514 match self {
515 PartialGraphStatus::ReceivedExchangeRequest(_) => Poll::Pending,
516 PartialGraphStatus::Running(state) => state.poll_next_event(cx),
517 PartialGraphStatus::Suspended(_) | PartialGraphStatus::Resetting => Poll::Pending,
518 PartialGraphStatus::Unspecified => {
519 unreachable!()
520 }
521 }
522 }
523
524 pub(super) fn suspend(
525 &mut self,
526 failed_actor: Option<ActorId>,
527 err: StreamError,
528 completing_futures: Option<FuturesOrdered<AwaitEpochCompletedFuture>>,
529 ) {
530 let state = must_match!(replace(self, PartialGraphStatus::Unspecified), PartialGraphStatus::Running(state) => state);
531 *self = PartialGraphStatus::Suspended(SuspendedPartialGraphState::new(
532 state,
533 Some((failed_actor, err)),
534 completing_futures,
535 ));
536 }
537
538 pub(super) fn start_reset(
539 &mut self,
540 partial_graph_id: PartialGraphId,
541 completing_futures: Option<FuturesOrdered<AwaitEpochCompletedFuture>>,
542 table_ids_to_clear: &mut HashSet<TableId>,
543 ) -> BoxFuture<'static, ResetPartialGraphOutput> {
544 match replace(self, PartialGraphStatus::Resetting) {
545 PartialGraphStatus::ReceivedExchangeRequest(pending_requests) => {
546 for (_, _, request) in pending_requests {
547 if let TakeReceiverRequest::Remote { result_sender, .. } = request {
548 let _ = result_sender.send(Err(anyhow!("partial graph reset").into()));
549 }
550 }
551 async move { ResetPartialGraphOutput { root_err: None } }.boxed()
552 }
553 PartialGraphStatus::Running(state) => {
554 assert_eq!(partial_graph_id, state.partial_graph_id);
555 info!(
556 %partial_graph_id,
557 "start partial graph reset from Running"
558 );
559 table_ids_to_clear.extend(state.table_ids.iter().copied());
560 SuspendedPartialGraphState::new(state, None, completing_futures)
561 .reset()
562 .boxed()
563 }
564 PartialGraphStatus::Suspended(state) => {
565 assert!(
566 completing_futures.is_none(),
567 "should have been clear when suspended"
568 );
569 assert_eq!(partial_graph_id, state.inner.partial_graph_id);
570 info!(
571 %partial_graph_id,
572 suspend_elapsed = ?state.suspend_time.elapsed(),
573 "start partial graph reset after suspended"
574 );
575 table_ids_to_clear.extend(state.inner.table_ids.iter().copied());
576 state.reset().boxed()
577 }
578 PartialGraphStatus::Resetting => {
579 unreachable!("should not reset for twice");
580 }
581 PartialGraphStatus::Unspecified => {
582 unreachable!()
583 }
584 }
585 }
586}
587
588#[derive(Default)]
589pub(in crate::task) struct ManagedBarrierState {
590 pub(super) partial_graphs: HashMap<PartialGraphId, PartialGraphStatus>,
591 pub(super) resetting_graphs:
592 FuturesUnordered<JoinHandle<Vec<(PartialGraphId, ResetPartialGraphOutput)>>>,
593}
594
595pub(super) enum ManagedBarrierStateEvent {
596 BarrierCollected {
597 partial_graph_id: PartialGraphId,
598 barrier: Barrier,
599 },
600 ActorError {
601 partial_graph_id: PartialGraphId,
602 actor_id: ActorId,
603 err: StreamError,
604 },
605 PartialGraphsReset(Vec<(PartialGraphId, ResetPartialGraphOutput)>),
606 RegisterLocalUpstreamOutput {
607 actor_id: ActorId,
608 upstream_actor_id: ActorId,
609 upstream_partial_graph_id: PartialGraphId,
610 term_id: String,
611 tx: permit::Sender,
612 },
613}
614
615impl ManagedBarrierState {
616 pub(super) fn next_event(&mut self) -> impl Future<Output = ManagedBarrierStateEvent> + '_ {
617 poll_fn(|cx| {
618 for graph in self.partial_graphs.values_mut() {
619 if let Poll::Ready(event) = graph.poll_next_event(cx) {
620 return Poll::Ready(event);
621 }
622 }
623 if let Poll::Ready(Some(result)) = self.resetting_graphs.poll_next_unpin(cx) {
624 let outputs = result.expect("failed to join resetting future");
625 for (partial_graph_id, _) in &outputs {
626 let PartialGraphStatus::Resetting = self
627 .partial_graphs
628 .remove(partial_graph_id)
629 .expect("should exist")
630 else {
631 panic!("should be resetting")
632 };
633 }
634 return Poll::Ready(ManagedBarrierStateEvent::PartialGraphsReset(outputs));
635 }
636 Poll::Pending
637 })
638 }
639}
640
641pub(crate) struct PartialGraphState {
646 partial_graph_id: PartialGraphId,
647 pub(crate) actor_states: HashMap<ActorId, InflightActorState>,
648 pub(super) actor_pending_new_output_requests:
649 HashMap<ActorId, Vec<(ActorId, NewOutputRequest)>>,
650
651 pub(crate) graph_state: PartialGraphManagedBarrierState,
652
653 table_ids: HashSet<TableId>,
654
655 actor_manager: Arc<StreamActorManager>,
656
657 pub(super) local_barrier_manager: LocalBarrierManager,
658
659 barrier_event_rx: UnboundedReceiver<LocalBarrierEvent>,
660 pub(super) actor_failure_rx: UnboundedReceiver<(ActorId, StreamError)>,
661}
662
663impl PartialGraphState {
664 pub(super) fn new(
666 partial_graph_id: PartialGraphId,
667 term_id: String,
668 actor_manager: Arc<StreamActorManager>,
669 ) -> Self {
670 let (local_barrier_manager, barrier_event_rx, actor_failure_rx) =
671 LocalBarrierManager::new(term_id, actor_manager.env.clone());
672 Self {
673 partial_graph_id,
674 actor_states: Default::default(),
675 actor_pending_new_output_requests: Default::default(),
676 graph_state: PartialGraphManagedBarrierState::new(&actor_manager, partial_graph_id),
677 table_ids: Default::default(),
678 actor_manager,
679 local_barrier_manager,
680 barrier_event_rx,
681 actor_failure_rx,
682 }
683 }
684
685 pub(super) fn to_debug_info(&self) -> ManagedBarrierStateDebugInfo<'_> {
686 ManagedBarrierStateDebugInfo {
687 running_actors: self.actor_states.keys().cloned().collect(),
688 graph_state: &self.graph_state,
689 }
690 }
691
692 async fn abort_and_wait_actors(&mut self) {
693 for (actor_id, state) in &self.actor_states {
694 tracing::debug!("force stopping actor {}", actor_id);
695 state.join_handle.abort();
696 if let Some(monitor_task_handle) = &state.monitor_task_handle {
697 monitor_task_handle.abort();
698 }
699 }
700
701 for (actor_id, state) in self.actor_states.drain() {
702 tracing::debug!("join actor {}", actor_id);
703 let result = state.join_handle.await;
704 assert!(result.is_ok() || result.unwrap_err().is_cancelled());
705 }
706 }
707}
708
709impl InflightActorState {
710 pub(super) fn register_barrier_sender(
711 &mut self,
712 tx: mpsc::UnboundedSender<Barrier>,
713 ) -> StreamResult<()> {
714 match &self.status {
715 InflightActorStatus::IssuedFirst(pending_barriers) => {
716 for barrier in pending_barriers {
717 tx.send(barrier.clone()).map_err(|_| {
718 StreamError::barrier_send(
719 barrier.clone(),
720 self.actor_id,
721 "failed to send pending barriers to newly registered sender",
722 )
723 })?;
724 }
725 self.barrier_senders.push(tx);
726 }
727 InflightActorStatus::Running(_) => {
728 unreachable!("should not register barrier sender when entering Running status")
729 }
730 }
731 Ok(())
732 }
733}
734
735impl PartialGraphState {
736 pub(super) fn register_barrier_sender(
737 &mut self,
738 actor_id: ActorId,
739 tx: mpsc::UnboundedSender<Barrier>,
740 ) -> StreamResult<()> {
741 self.actor_states
742 .get_mut(&actor_id)
743 .expect("should exist")
744 .register_barrier_sender(tx)
745 }
746}
747
748impl PartialGraphState {
749 pub(super) fn transform_to_issued(
750 &mut self,
751 barrier: &Barrier,
752 request: InjectBarrierRequest,
753 ) -> StreamResult<()> {
754 assert_eq!(self.partial_graph_id, request.partial_graph_id);
755 let actor_to_stop = barrier.all_stop_actors();
756 let is_stop_actor = |actor_id| {
757 actor_to_stop
758 .map(|actors| actors.contains(&actor_id))
759 .unwrap_or(false)
760 };
761
762 let table_ids = HashSet::from_iter(request.table_ids_to_sync);
763 self.table_ids.extend(table_ids.iter().cloned());
764
765 self.graph_state.transform_to_issued(
766 barrier,
767 request.actor_ids_to_collect.iter().copied(),
768 table_ids,
769 );
770
771 let mut new_actors = HashSet::new();
772 for (node, fragment_id, actor) in
773 request
774 .actors_to_build
775 .into_iter()
776 .flat_map(|fragment_actors| {
777 let node = Arc::new(fragment_actors.node.unwrap());
778 fragment_actors
779 .actors
780 .into_iter()
781 .map(move |actor| (node.clone(), fragment_actors.fragment_id, actor))
782 })
783 {
784 let actor_id = actor.actor_id;
785 assert!(!is_stop_actor(actor_id));
786 assert!(new_actors.insert(actor_id));
787 assert!(request.actor_ids_to_collect.contains(&actor_id));
788 let (new_output_request_tx, new_output_request_rx) = unbounded_channel();
789 if let Some(pending_requests) = self.actor_pending_new_output_requests.remove(&actor_id)
790 {
791 for request in pending_requests {
792 let _ = new_output_request_tx.send(request);
793 }
794 }
795 let (join_handle, monitor_join_handle) = self.actor_manager.spawn_actor(
796 actor,
797 fragment_id,
798 node,
799 self.local_barrier_manager.clone(),
800 new_output_request_rx,
801 );
802 assert!(
803 self.actor_states
804 .try_insert(
805 actor_id,
806 InflightActorState::start(
807 actor_id,
808 barrier,
809 new_output_request_tx,
810 join_handle,
811 monitor_join_handle
812 )
813 )
814 .is_ok()
815 );
816 }
817
818 if cfg!(test) {
822 for &actor_id in &request.actor_ids_to_collect {
823 if !self.actor_states.contains_key(&actor_id) {
824 let (tx, rx) = unbounded_channel();
825 let join_handle = self.actor_manager.runtime.spawn(async move {
826 let _ = rx;
828 pending().await
829 });
830 assert!(
831 self.actor_states
832 .try_insert(
833 actor_id,
834 InflightActorState::start(actor_id, barrier, tx, join_handle, None,)
835 )
836 .is_ok()
837 );
838 new_actors.insert(actor_id);
839 }
840 }
841 }
842
843 for &actor_id in &request.actor_ids_to_collect {
846 if new_actors.contains(&actor_id) {
847 continue;
848 }
849 self.actor_states
850 .get_mut(&actor_id)
851 .unwrap_or_else(|| {
852 panic!(
853 "should exist: {} {:?}",
854 actor_id, request.actor_ids_to_collect
855 );
856 })
857 .issue_barrier(barrier, is_stop_actor(actor_id))?;
858 }
859
860 Ok(())
861 }
862
863 pub(super) fn new_actor_output_request(
864 &mut self,
865 actor_id: ActorId,
866 upstream_actor_id: ActorId,
867 request: TakeReceiverRequest,
868 ) {
869 let request = match request {
870 TakeReceiverRequest::Remote {
871 result_sender,
872 upstream_fragment_id,
873 } => {
874 let upstream_fragment_id_str = upstream_fragment_id.to_string();
875 let fragment_channel_buffered_bytes = self
876 .actor_manager
877 .streaming_metrics
878 .fragment_channel_buffered_bytes
879 .with_guarded_label_values(&[&upstream_fragment_id_str]);
880 let (tx, rx) = permit::channel_from_config_with_metrics(
881 self.local_barrier_manager.env.global_config(),
882 permit::ChannelMetrics {
883 sender_actor_channel_buffered_bytes: fragment_channel_buffered_bytes
884 .clone(),
885 receiver_actor_channel_buffered_bytes: fragment_channel_buffered_bytes,
886 },
887 );
888 let _ = result_sender.send(Ok(rx));
889 NewOutputRequest::Remote(tx)
890 }
891 TakeReceiverRequest::Local(tx) => NewOutputRequest::Local(tx),
892 };
893 if let Some(actor) = self.actor_states.get_mut(&upstream_actor_id) {
894 let _ = actor.new_output_request_tx.send((actor_id, request));
895 } else {
896 self.actor_pending_new_output_requests
897 .entry(upstream_actor_id)
898 .or_default()
899 .push((actor_id, request));
900 }
901 }
902
903 pub(super) fn poll_next_event(
905 &mut self,
906 cx: &mut Context<'_>,
907 ) -> Poll<ManagedBarrierStateEvent> {
908 if let Poll::Ready(option) = self.actor_failure_rx.poll_recv(cx) {
909 let (actor_id, err) = option.expect("non-empty when tx in local_barrier_manager");
910 return Poll::Ready(ManagedBarrierStateEvent::ActorError {
911 actor_id,
912 err,
913 partial_graph_id: self.partial_graph_id,
914 });
915 }
916 {
918 if let Some(barrier) = self.graph_state.may_have_collected_all() {
919 return Poll::Ready(ManagedBarrierStateEvent::BarrierCollected {
920 barrier,
921 partial_graph_id: self.partial_graph_id,
922 });
923 }
924 }
925 while let Poll::Ready(event) = self.barrier_event_rx.poll_recv(cx) {
926 match event.expect("non-empty when tx in local_barrier_manager") {
927 LocalBarrierEvent::ReportActorCollected { actor_id, epoch } => {
928 if let Some(barrier) = self.collect(actor_id, epoch) {
929 return Poll::Ready(ManagedBarrierStateEvent::BarrierCollected {
930 barrier,
931 partial_graph_id: self.partial_graph_id,
932 });
933 }
934 }
935 LocalBarrierEvent::ReportCreateProgress {
936 epoch,
937 fragment_id,
938 actor,
939 state,
940 } => {
941 self.update_create_mview_progress(epoch, fragment_id, actor, state);
942 }
943 LocalBarrierEvent::ReportTableChangeLogProgress { epoch, progress } => {
944 self.update_table_change_log_progress(epoch, progress);
945 }
946 LocalBarrierEvent::ReportSourceListFinished {
947 epoch,
948 actor_id,
949 table_id,
950 associated_source_id,
951 } => {
952 self.report_source_list_finished(
953 epoch,
954 actor_id,
955 table_id,
956 associated_source_id,
957 );
958 }
959 LocalBarrierEvent::ReportSourceLoadFinished {
960 epoch,
961 actor_id,
962 table_id,
963 associated_source_id,
964 } => {
965 self.report_source_load_finished(
966 epoch,
967 actor_id,
968 table_id,
969 associated_source_id,
970 );
971 }
972 LocalBarrierEvent::RefreshFinished {
973 epoch,
974 actor_id,
975 table_id,
976 } => {
977 self.report_refresh_finished(epoch, actor_id, table_id);
978 }
979 LocalBarrierEvent::RegisterBarrierSender {
980 actor_id,
981 barrier_sender,
982 } => {
983 if let Err(err) = self.register_barrier_sender(actor_id, barrier_sender) {
984 return Poll::Ready(ManagedBarrierStateEvent::ActorError {
985 actor_id,
986 err,
987 partial_graph_id: self.partial_graph_id,
988 });
989 }
990 }
991 LocalBarrierEvent::RegisterLocalUpstreamOutput {
992 actor_id,
993 upstream_actor_id,
994 upstream_partial_graph_id,
995 term_id,
996 tx,
997 } => {
998 return Poll::Ready(ManagedBarrierStateEvent::RegisterLocalUpstreamOutput {
999 actor_id,
1000 upstream_actor_id,
1001 upstream_partial_graph_id,
1002 term_id,
1003 tx,
1004 });
1005 }
1006 LocalBarrierEvent::ReportCdcTableBackfillProgress {
1007 actor_id,
1008 epoch,
1009 state,
1010 } => {
1011 self.update_cdc_table_backfill_progress(epoch, actor_id, state);
1012 }
1013 LocalBarrierEvent::ReportCdcSourceOffsetUpdated {
1014 epoch,
1015 actor_id,
1016 source_id,
1017 } => {
1018 self.report_cdc_source_offset_updated(epoch, actor_id, source_id);
1019 }
1020 LocalBarrierEvent::ReportIcebergPkIndexSinkMetadata {
1021 epoch,
1022 sink_id,
1023 actor_id,
1024 role,
1025 metadata,
1026 } => {
1027 self.report_iceberg_pk_index_sink_metadata(
1028 epoch, sink_id, actor_id, role, metadata,
1029 );
1030 }
1031 }
1032 }
1033
1034 debug_assert!(self.graph_state.may_have_collected_all().is_none());
1035 Poll::Pending
1036 }
1037}
1038
1039impl PartialGraphState {
1040 #[must_use]
1041 pub(super) fn collect(&mut self, actor_id: ActorId, epoch: EpochPair) -> Option<Barrier> {
1042 let is_finished = self
1043 .actor_states
1044 .get_mut(&actor_id)
1045 .expect("should exist")
1046 .collect(epoch);
1047 if is_finished {
1048 let state = self.actor_states.remove(&actor_id).expect("should exist");
1049 if let Some(monitor_task_handle) = state.monitor_task_handle {
1050 monitor_task_handle.abort();
1051 }
1052 }
1053 self.graph_state.collect(actor_id, epoch);
1054 self.graph_state.may_have_collected_all()
1055 }
1056
1057 pub(super) fn pop_barrier_to_complete(&mut self, prev_epoch: u64) -> BarrierToComplete {
1058 self.graph_state.pop_barrier_to_complete(prev_epoch)
1059 }
1060
1061 async fn try_find_root_actor_failure(
1065 &mut self,
1066 first_failure: Option<(Option<ActorId>, StreamError)>,
1067 ) -> Option<ScoredStreamError> {
1068 let mut later_errs = vec![];
1069 let _ = tokio::time::timeout(Duration::from_secs(3), async {
1071 let mut uncollected_actors: HashSet<_> = self.actor_states.keys().cloned().collect();
1072 if let Some((Some(failed_actor), _)) = &first_failure {
1073 uncollected_actors.remove(failed_actor);
1074 }
1075 while !uncollected_actors.is_empty()
1076 && let Some((actor_id, error)) = self.actor_failure_rx.recv().await
1077 {
1078 uncollected_actors.remove(&actor_id);
1079 later_errs.push(error);
1080 }
1081 })
1082 .await;
1083
1084 first_failure
1085 .into_iter()
1086 .map(|(_, err)| err)
1087 .chain(later_errs)
1088 .map(|e| e.with_score())
1089 .max_by_key(|e| e.score)
1090 }
1091
1092 pub(super) fn report_source_list_finished(
1094 &mut self,
1095 epoch: EpochPair,
1096 actor_id: ActorId,
1097 table_id: TableId,
1098 associated_source_id: SourceId,
1099 ) {
1100 if let Some(actor_state) = self.actor_states.get(&actor_id)
1102 && actor_state.inflight_barriers.contains(&epoch.prev)
1103 {
1104 self.graph_state
1105 .event_mut(epoch)
1106 .list_finished_source_ids
1107 .push(PbListFinishedSource {
1108 reporter_actor_id: actor_id,
1109 table_id,
1110 associated_source_id,
1111 });
1112 } else {
1113 warn!(
1114 ?epoch,
1115 %actor_id, %table_id, %associated_source_id, "ignore source list finished"
1116 );
1117 }
1118 }
1119
1120 pub(super) fn report_source_load_finished(
1122 &mut self,
1123 epoch: EpochPair,
1124 actor_id: ActorId,
1125 table_id: TableId,
1126 associated_source_id: SourceId,
1127 ) {
1128 if let Some(actor_state) = self.actor_states.get(&actor_id)
1130 && actor_state.inflight_barriers.contains(&epoch.prev)
1131 {
1132 self.graph_state
1133 .event_mut(epoch)
1134 .load_finished_source_ids
1135 .push(PbLoadFinishedSource {
1136 reporter_actor_id: actor_id,
1137 table_id,
1138 associated_source_id,
1139 });
1140 } else {
1141 warn!(
1142 ?epoch,
1143 %actor_id, %table_id, %associated_source_id, "ignore source load finished"
1144 );
1145 }
1146 }
1147
1148 pub(super) fn report_cdc_source_offset_updated(
1150 &mut self,
1151 epoch: EpochPair,
1152 actor_id: ActorId,
1153 source_id: SourceId,
1154 ) {
1155 if let Some(actor_state) = self.actor_states.get(&actor_id)
1156 && actor_state.inflight_barriers.contains(&epoch.prev)
1157 {
1158 self.graph_state
1159 .event_mut(epoch)
1160 .cdc_source_offset_updated
1161 .push(PbCdcSourceOffsetUpdated {
1162 reporter_actor_id: actor_id,
1163 source_id,
1164 });
1165 } else {
1166 warn!(
1167 ?epoch,
1168 %actor_id, %source_id, "ignore cdc source offset updated"
1169 );
1170 }
1171 }
1172
1173 pub(super) fn report_iceberg_pk_index_sink_metadata(
1175 &mut self,
1176 epoch: EpochPair,
1177 sink_id: SinkId,
1178 actor_id: ActorId,
1179 role: PbIcebergPkIndexSinkRole,
1180 metadata: Option<SinkMetadata>,
1181 ) {
1182 if let Some(actor_state) = self.actor_states.get(&actor_id)
1183 && actor_state.inflight_barriers.contains(&epoch.prev)
1184 {
1185 self.graph_state
1186 .event_mut(epoch)
1187 .iceberg_pk_index_sink_metadata
1188 .push(PbIcebergPkIndexSinkMetadata {
1189 sink_id,
1190 reporter_actor_id: actor_id,
1191 prev_epoch: epoch.prev,
1192 role: role as i32,
1193 metadata,
1194 });
1195 } else {
1196 tracing::warn!(
1197 ?epoch,
1198 %actor_id,
1199 %sink_id,
1200 ?role,
1201 "ignore iceberg v3 sink metadata report from non-inflight actor"
1202 );
1203 }
1204 }
1205
1206 pub(super) fn report_refresh_finished(
1208 &mut self,
1209 epoch: EpochPair,
1210 actor_id: ActorId,
1211 table_id: TableId,
1212 ) {
1213 if let Some(actor_state) = self.actor_states.get(&actor_id)
1214 && actor_state.inflight_barriers.contains(&epoch.prev)
1215 {
1216 self.graph_state
1217 .event_mut(epoch)
1218 .refresh_finished_actors
1219 .push(PbRefreshFinishedActor {
1220 reporter_actor_id: actor_id,
1221 table_id,
1222 });
1223 } else {
1224 warn!(
1225 ?epoch,
1226 %actor_id, %table_id, "ignore refresh finished table"
1227 );
1228 }
1229 }
1230}
1231
1232impl PartialGraphManagedBarrierState {
1233 fn may_have_collected_all(&mut self) -> Option<Barrier> {
1236 for barrier_state in self.epoch_barrier_state_map.values_mut() {
1237 match &barrier_state.inner {
1238 ManagedBarrierStateInner::Issued(IssuedState {
1239 remaining_actors, ..
1240 }) if remaining_actors.is_empty() => {}
1241 ManagedBarrierStateInner::AllCollected(_) => {
1242 continue;
1243 }
1244 ManagedBarrierStateInner::Issued(_) => {
1245 break;
1246 }
1247 }
1248
1249 self.barrier_manager_progress.inc();
1250
1251 let event = self
1252 .barrier_events
1253 .remove(&barrier_state.barrier.epoch.curr)
1254 .unwrap_or_else(|| EpochBarrierEvent::new(barrier_state.barrier.epoch));
1255 assert_eq!(event.epoch, barrier_state.barrier.epoch);
1256 let prev_state = replace(
1257 &mut barrier_state.inner,
1258 ManagedBarrierStateInner::AllCollected(event),
1259 );
1260
1261 must_match!(prev_state, ManagedBarrierStateInner::Issued(IssuedState {
1262 barrier_inflight_latency: timer,
1263 ..
1264 }) => {
1265 timer.observe_duration();
1266 });
1267
1268 return Some(barrier_state.barrier.clone());
1269 }
1270 None
1271 }
1272
1273 fn pop_barrier_to_complete(&mut self, prev_epoch: u64) -> BarrierToComplete {
1274 let (popped_prev_epoch, barrier_state) = self
1275 .epoch_barrier_state_map
1276 .pop_first()
1277 .expect("should exist");
1278
1279 assert_eq!(prev_epoch, popped_prev_epoch);
1280
1281 let event = must_match!(barrier_state.inner, ManagedBarrierStateInner::AllCollected(event) => event);
1282 assert_eq!(event.epoch, barrier_state.barrier.epoch);
1283 BarrierToComplete {
1284 barrier: barrier_state.barrier,
1285 table_ids: barrier_state.table_ids,
1286 event,
1287 }
1288 }
1289
1290 pub(super) fn barrier_sync_latency(&self) -> LabelGuardedHistogram {
1291 self.barrier_sync_latency.clone()
1292 }
1293}
1294
1295pub(crate) struct BarrierToComplete {
1296 pub barrier: Barrier,
1297 pub table_ids: Option<HashSet<TableId>>,
1298 pub event: EpochBarrierEvent,
1299}
1300
1301impl PartialGraphManagedBarrierState {
1302 pub(super) fn collect(&mut self, actor_id: impl Into<ActorId>, epoch: EpochPair) {
1304 let actor_id = actor_id.into();
1305 tracing::debug!(
1306 target: "events::stream::barrier::manager::collect",
1307 ?epoch, %actor_id, state = ?self.epoch_barrier_state_map,
1308 "collect_barrier",
1309 );
1310
1311 match self.epoch_barrier_state_map.get_mut(&epoch.prev) {
1312 None => {
1313 panic!(
1317 "cannot collect new actor barrier {:?} at current state: None",
1318 epoch,
1319 )
1320 }
1321 Some(&mut BarrierState {
1322 ref barrier,
1323 inner:
1324 ManagedBarrierStateInner::Issued(IssuedState {
1325 ref mut remaining_actors,
1326 ..
1327 }),
1328 ..
1329 }) => {
1330 let exist = remaining_actors.remove(&actor_id);
1331 assert!(
1332 exist,
1333 "the actor doesn't exist. actor_id: {:?}, curr_epoch: {:?}",
1334 actor_id, epoch.curr
1335 );
1336 assert_eq!(barrier.epoch.curr, epoch.curr);
1337 }
1338 Some(BarrierState { inner, .. }) => {
1339 panic!(
1340 "cannot collect new actor barrier {:?} at current state: {:?}",
1341 epoch, inner
1342 )
1343 }
1344 }
1345 }
1346
1347 pub(super) fn transform_to_issued(
1350 &mut self,
1351 barrier: &Barrier,
1352 actor_ids_to_collect: impl IntoIterator<Item = ActorId>,
1353 table_ids: HashSet<TableId>,
1354 ) {
1355 let timer = self.barrier_inflight_latency.start_timer();
1356
1357 if let Some(hummock) = self.state_store.as_hummock() {
1358 hummock.start_epoch(barrier.epoch.curr, table_ids.clone());
1359 }
1360
1361 let table_ids = match barrier.kind {
1362 BarrierKind::Unspecified => {
1363 unreachable!()
1364 }
1365 BarrierKind::Initial => {
1366 assert!(
1367 self.prev_barrier_table_ids.is_none(),
1368 "non empty table_ids at initial barrier: {:?}",
1369 self.prev_barrier_table_ids
1370 );
1371 info!(epoch = ?barrier.epoch, "initialize at Initial barrier");
1372 self.prev_barrier_table_ids = Some((barrier.epoch, table_ids));
1373 None
1374 }
1375 BarrierKind::Barrier => {
1376 if let Some((prev_epoch, prev_table_ids)) = self.prev_barrier_table_ids.as_mut() {
1377 assert_eq!(prev_epoch.curr, barrier.epoch.prev);
1378 assert_eq!(prev_table_ids, &table_ids);
1379 *prev_epoch = barrier.epoch;
1380 } else {
1381 info!(epoch = ?barrier.epoch, "initialize at non-checkpoint barrier");
1382 self.prev_barrier_table_ids = Some((barrier.epoch, table_ids));
1383 }
1384 None
1385 }
1386 BarrierKind::Checkpoint => Some(
1387 if let Some((prev_epoch, prev_table_ids)) = self
1388 .prev_barrier_table_ids
1389 .replace((barrier.epoch, table_ids))
1390 && prev_epoch.curr == barrier.epoch.prev
1391 {
1392 prev_table_ids
1393 } else {
1394 debug!(epoch = ?barrier.epoch, "reinitialize at Checkpoint barrier");
1395 HashSet::new()
1396 },
1397 ),
1398 };
1399
1400 if let Some(&mut BarrierState { ref inner, .. }) =
1401 self.epoch_barrier_state_map.get_mut(&barrier.epoch.prev)
1402 {
1403 {
1404 panic!(
1405 "barrier epochs{:?} state has already been `Issued`. Current state: {:?}",
1406 barrier.epoch, inner
1407 );
1408 }
1409 };
1410
1411 self.epoch_barrier_state_map.insert(
1412 barrier.epoch.prev,
1413 BarrierState {
1414 barrier: barrier.clone(),
1415 inner: ManagedBarrierStateInner::Issued(IssuedState {
1416 remaining_actors: BTreeSet::from_iter(actor_ids_to_collect),
1417 barrier_inflight_latency: timer,
1418 }),
1419 table_ids,
1420 },
1421 );
1422 }
1423
1424 #[cfg(test)]
1425 async fn pop_next_completed_epoch(&mut self) -> u64 {
1426 if let Some(barrier) = self.may_have_collected_all() {
1427 self.pop_barrier_to_complete(barrier.epoch.prev);
1428 return barrier.epoch.prev;
1429 }
1430 pending().await
1431 }
1432}
1433
1434#[cfg(test)]
1435mod tests {
1436 use std::collections::HashSet;
1437
1438 use risingwave_common::util::epoch::test_epoch;
1439
1440 use crate::executor::Barrier;
1441 use crate::task::barrier_worker::managed_state::PartialGraphManagedBarrierState;
1442
1443 #[tokio::test]
1444 async fn test_managed_state_add_actor() {
1445 let mut managed_barrier_state = PartialGraphManagedBarrierState::for_test();
1446 let barrier1 = Barrier::new_test_barrier(test_epoch(1));
1447 let barrier2 = Barrier::new_test_barrier(test_epoch(2));
1448 let barrier3 = Barrier::new_test_barrier(test_epoch(3));
1449 let actor_ids_to_collect1 = HashSet::from([1.into(), 2.into()]);
1450 let actor_ids_to_collect2 = HashSet::from([1.into(), 2.into()]);
1451 let actor_ids_to_collect3 = HashSet::from([1.into(), 2.into(), 3.into()]);
1452 managed_barrier_state.transform_to_issued(&barrier1, actor_ids_to_collect1, HashSet::new());
1453 managed_barrier_state.transform_to_issued(&barrier2, actor_ids_to_collect2, HashSet::new());
1454 managed_barrier_state.transform_to_issued(&barrier3, actor_ids_to_collect3, HashSet::new());
1455 managed_barrier_state.collect(1, barrier1.epoch);
1456 managed_barrier_state.collect(2, barrier1.epoch);
1457 assert_eq!(
1458 managed_barrier_state.pop_next_completed_epoch().await,
1459 test_epoch(0)
1460 );
1461 assert_eq!(
1462 managed_barrier_state
1463 .epoch_barrier_state_map
1464 .first_key_value()
1465 .unwrap()
1466 .0,
1467 &test_epoch(1)
1468 );
1469 managed_barrier_state.collect(1, barrier2.epoch);
1470 managed_barrier_state.collect(1, barrier3.epoch);
1471 managed_barrier_state.collect(2, barrier2.epoch);
1472 assert_eq!(
1473 managed_barrier_state.pop_next_completed_epoch().await,
1474 test_epoch(1)
1475 );
1476 assert_eq!(
1477 managed_barrier_state
1478 .epoch_barrier_state_map
1479 .first_key_value()
1480 .unwrap()
1481 .0,
1482 &test_epoch(2)
1483 );
1484 managed_barrier_state.collect(2, barrier3.epoch);
1485 managed_barrier_state.collect(3, barrier3.epoch);
1486 assert_eq!(
1487 managed_barrier_state.pop_next_completed_epoch().await,
1488 test_epoch(2)
1489 );
1490 assert!(managed_barrier_state.epoch_barrier_state_map.is_empty());
1491 }
1492
1493 #[tokio::test]
1494 async fn test_managed_state_stop_actor() {
1495 let mut managed_barrier_state = PartialGraphManagedBarrierState::for_test();
1496 let barrier1 = Barrier::new_test_barrier(test_epoch(1));
1497 let barrier2 = Barrier::new_test_barrier(test_epoch(2));
1498 let barrier3 = Barrier::new_test_barrier(test_epoch(3));
1499 let actor_ids_to_collect1 = HashSet::from([1.into(), 2.into(), 3.into(), 4.into()]);
1500 let actor_ids_to_collect2 = HashSet::from([1.into(), 2.into(), 3.into()]);
1501 let actor_ids_to_collect3 = HashSet::from([1.into(), 2.into()]);
1502 managed_barrier_state.transform_to_issued(&barrier1, actor_ids_to_collect1, HashSet::new());
1503 managed_barrier_state.transform_to_issued(&barrier2, actor_ids_to_collect2, HashSet::new());
1504 managed_barrier_state.transform_to_issued(&barrier3, actor_ids_to_collect3, HashSet::new());
1505
1506 managed_barrier_state.collect(1, barrier1.epoch);
1507 managed_barrier_state.collect(1, barrier2.epoch);
1508 managed_barrier_state.collect(1, barrier3.epoch);
1509 managed_barrier_state.collect(2, barrier1.epoch);
1510 managed_barrier_state.collect(2, barrier2.epoch);
1511 managed_barrier_state.collect(2, barrier3.epoch);
1512 assert_eq!(
1513 managed_barrier_state
1514 .epoch_barrier_state_map
1515 .first_key_value()
1516 .unwrap()
1517 .0,
1518 &0
1519 );
1520 managed_barrier_state.collect(3, barrier1.epoch);
1521 managed_barrier_state.collect(3, barrier2.epoch);
1522 assert_eq!(
1523 managed_barrier_state
1524 .epoch_barrier_state_map
1525 .first_key_value()
1526 .unwrap()
1527 .0,
1528 &0
1529 );
1530 managed_barrier_state.collect(4, barrier1.epoch);
1531 assert_eq!(
1532 managed_barrier_state.pop_next_completed_epoch().await,
1533 test_epoch(0)
1534 );
1535 assert_eq!(
1536 managed_barrier_state.pop_next_completed_epoch().await,
1537 test_epoch(1)
1538 );
1539 assert_eq!(
1540 managed_barrier_state.pop_next_completed_epoch().await,
1541 test_epoch(2)
1542 );
1543 assert!(managed_barrier_state.epoch_barrier_state_map.is_empty());
1544 }
1545}