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