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