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<(String, 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 term_id: String,
593 tx: permit::Sender,
594 },
595}
596
597impl ManagedBarrierState {
598 pub(super) fn next_event(&mut self) -> impl Future<Output = ManagedBarrierStateEvent> + '_ {
599 poll_fn(|cx| {
600 for graph in self.partial_graphs.values_mut() {
601 if let Poll::Ready(event) = graph.poll_next_event(cx) {
602 return Poll::Ready(event);
603 }
604 }
605 if let Poll::Ready(Some(result)) = self.resetting_graphs.poll_next_unpin(cx) {
606 let outputs = result.expect("failed to join resetting future");
607 for (partial_graph_id, _) in &outputs {
608 let PartialGraphStatus::Resetting = self
609 .partial_graphs
610 .remove(partial_graph_id)
611 .expect("should exist")
612 else {
613 panic!("should be resetting")
614 };
615 }
616 return Poll::Ready(ManagedBarrierStateEvent::PartialGraphsReset(outputs));
617 }
618 Poll::Pending
619 })
620 }
621}
622
623pub(crate) struct PartialGraphState {
628 partial_graph_id: PartialGraphId,
629 pub(crate) actor_states: HashMap<ActorId, InflightActorState>,
630 pub(super) actor_pending_new_output_requests:
631 HashMap<ActorId, Vec<(ActorId, NewOutputRequest)>>,
632
633 pub(crate) graph_state: PartialGraphManagedBarrierState,
634
635 table_ids: HashSet<TableId>,
636
637 actor_manager: Arc<StreamActorManager>,
638
639 pub(super) local_barrier_manager: LocalBarrierManager,
640
641 barrier_event_rx: UnboundedReceiver<LocalBarrierEvent>,
642 pub(super) actor_failure_rx: UnboundedReceiver<(ActorId, StreamError)>,
643}
644
645impl PartialGraphState {
646 pub(super) fn new(
648 partial_graph_id: PartialGraphId,
649 term_id: String,
650 actor_manager: Arc<StreamActorManager>,
651 ) -> Self {
652 let (local_barrier_manager, barrier_event_rx, actor_failure_rx) =
653 LocalBarrierManager::new(term_id, actor_manager.env.clone());
654 Self {
655 partial_graph_id,
656 actor_states: Default::default(),
657 actor_pending_new_output_requests: Default::default(),
658 graph_state: PartialGraphManagedBarrierState::new(&actor_manager, partial_graph_id),
659 table_ids: Default::default(),
660 actor_manager,
661 local_barrier_manager,
662 barrier_event_rx,
663 actor_failure_rx,
664 }
665 }
666
667 pub(super) fn to_debug_info(&self) -> ManagedBarrierStateDebugInfo<'_> {
668 ManagedBarrierStateDebugInfo {
669 running_actors: self.actor_states.keys().cloned().collect(),
670 graph_state: &self.graph_state,
671 }
672 }
673
674 async fn abort_and_wait_actors(&mut self) {
675 for (actor_id, state) in &self.actor_states {
676 tracing::debug!("force stopping actor {}", actor_id);
677 state.join_handle.abort();
678 if let Some(monitor_task_handle) = &state.monitor_task_handle {
679 monitor_task_handle.abort();
680 }
681 }
682
683 for (actor_id, state) in self.actor_states.drain() {
684 tracing::debug!("join actor {}", actor_id);
685 let result = state.join_handle.await;
686 assert!(result.is_ok() || result.unwrap_err().is_cancelled());
687 }
688 }
689}
690
691impl InflightActorState {
692 pub(super) fn register_barrier_sender(
693 &mut self,
694 tx: mpsc::UnboundedSender<Barrier>,
695 ) -> StreamResult<()> {
696 match &self.status {
697 InflightActorStatus::IssuedFirst(pending_barriers) => {
698 for barrier in pending_barriers {
699 tx.send(barrier.clone()).map_err(|_| {
700 StreamError::barrier_send(
701 barrier.clone(),
702 self.actor_id,
703 "failed to send pending barriers to newly registered sender",
704 )
705 })?;
706 }
707 self.barrier_senders.push(tx);
708 }
709 InflightActorStatus::Running(_) => {
710 unreachable!("should not register barrier sender when entering Running status")
711 }
712 }
713 Ok(())
714 }
715}
716
717impl PartialGraphState {
718 pub(super) fn register_barrier_sender(
719 &mut self,
720 actor_id: ActorId,
721 tx: mpsc::UnboundedSender<Barrier>,
722 ) -> StreamResult<()> {
723 self.actor_states
724 .get_mut(&actor_id)
725 .expect("should exist")
726 .register_barrier_sender(tx)
727 }
728}
729
730impl PartialGraphState {
731 pub(super) fn transform_to_issued(
732 &mut self,
733 barrier: &Barrier,
734 request: InjectBarrierRequest,
735 ) -> StreamResult<()> {
736 assert_eq!(self.partial_graph_id, request.partial_graph_id);
737 let actor_to_stop = barrier.all_stop_actors();
738 let is_stop_actor = |actor_id| {
739 actor_to_stop
740 .map(|actors| actors.contains(&actor_id))
741 .unwrap_or(false)
742 };
743
744 let table_ids = HashSet::from_iter(request.table_ids_to_sync);
745 self.table_ids.extend(table_ids.iter().cloned());
746
747 self.graph_state.transform_to_issued(
748 barrier,
749 request.actor_ids_to_collect.iter().copied(),
750 table_ids,
751 );
752
753 let mut new_actors = HashSet::new();
754 for (node, fragment_id, actor) in
755 request
756 .actors_to_build
757 .into_iter()
758 .flat_map(|fragment_actors| {
759 let node = Arc::new(fragment_actors.node.unwrap());
760 fragment_actors
761 .actors
762 .into_iter()
763 .map(move |actor| (node.clone(), fragment_actors.fragment_id, actor))
764 })
765 {
766 let actor_id = actor.actor_id;
767 assert!(!is_stop_actor(actor_id));
768 assert!(new_actors.insert(actor_id));
769 assert!(request.actor_ids_to_collect.contains(&actor_id));
770 let (new_output_request_tx, new_output_request_rx) = unbounded_channel();
771 if let Some(pending_requests) = self.actor_pending_new_output_requests.remove(&actor_id)
772 {
773 for request in pending_requests {
774 let _ = new_output_request_tx.send(request);
775 }
776 }
777 let (join_handle, monitor_join_handle) = self.actor_manager.spawn_actor(
778 actor,
779 fragment_id,
780 node,
781 self.local_barrier_manager.clone(),
782 new_output_request_rx,
783 );
784 assert!(
785 self.actor_states
786 .try_insert(
787 actor_id,
788 InflightActorState::start(
789 actor_id,
790 barrier,
791 new_output_request_tx,
792 join_handle,
793 monitor_join_handle
794 )
795 )
796 .is_ok()
797 );
798 }
799
800 if cfg!(test) {
804 for &actor_id in &request.actor_ids_to_collect {
805 if !self.actor_states.contains_key(&actor_id) {
806 let (tx, rx) = unbounded_channel();
807 let join_handle = self.actor_manager.runtime.spawn(async move {
808 let _ = rx;
810 pending().await
811 });
812 assert!(
813 self.actor_states
814 .try_insert(
815 actor_id,
816 InflightActorState::start(actor_id, barrier, tx, join_handle, None,)
817 )
818 .is_ok()
819 );
820 new_actors.insert(actor_id);
821 }
822 }
823 }
824
825 for &actor_id in &request.actor_ids_to_collect {
828 if new_actors.contains(&actor_id) {
829 continue;
830 }
831 self.actor_states
832 .get_mut(&actor_id)
833 .unwrap_or_else(|| {
834 panic!(
835 "should exist: {} {:?}",
836 actor_id, request.actor_ids_to_collect
837 );
838 })
839 .issue_barrier(barrier, is_stop_actor(actor_id))?;
840 }
841
842 Ok(())
843 }
844
845 pub(super) fn new_actor_output_request(
846 &mut self,
847 actor_id: ActorId,
848 upstream_actor_id: ActorId,
849 request: TakeReceiverRequest,
850 ) {
851 let request = match request {
852 TakeReceiverRequest::Remote {
853 result_sender,
854 upstream_fragment_id,
855 } => {
856 let upstream_fragment_id_str = upstream_fragment_id.to_string();
857 let fragment_channel_buffered_bytes = self
858 .actor_manager
859 .streaming_metrics
860 .fragment_channel_buffered_bytes
861 .with_guarded_label_values(&[&upstream_fragment_id_str]);
862 let (tx, rx) = permit::channel_from_config_with_metrics(
863 self.local_barrier_manager.env.global_config(),
864 permit::ChannelMetrics {
865 sender_actor_channel_buffered_bytes: fragment_channel_buffered_bytes
866 .clone(),
867 receiver_actor_channel_buffered_bytes: fragment_channel_buffered_bytes,
868 },
869 );
870 let _ = result_sender.send(Ok(rx));
871 NewOutputRequest::Remote(tx)
872 }
873 TakeReceiverRequest::Local(tx) => NewOutputRequest::Local(tx),
874 };
875 if let Some(actor) = self.actor_states.get_mut(&upstream_actor_id) {
876 let _ = actor.new_output_request_tx.send((actor_id, request));
877 } else {
878 self.actor_pending_new_output_requests
879 .entry(upstream_actor_id)
880 .or_default()
881 .push((actor_id, request));
882 }
883 }
884
885 pub(super) fn poll_next_event(
887 &mut self,
888 cx: &mut Context<'_>,
889 ) -> Poll<ManagedBarrierStateEvent> {
890 if let Poll::Ready(option) = self.actor_failure_rx.poll_recv(cx) {
891 let (actor_id, err) = option.expect("non-empty when tx in local_barrier_manager");
892 return Poll::Ready(ManagedBarrierStateEvent::ActorError {
893 actor_id,
894 err,
895 partial_graph_id: self.partial_graph_id,
896 });
897 }
898 {
900 if let Some(barrier) = self.graph_state.may_have_collected_all() {
901 return Poll::Ready(ManagedBarrierStateEvent::BarrierCollected {
902 barrier,
903 partial_graph_id: self.partial_graph_id,
904 });
905 }
906 }
907 while let Poll::Ready(event) = self.barrier_event_rx.poll_recv(cx) {
908 match event.expect("non-empty when tx in local_barrier_manager") {
909 LocalBarrierEvent::ReportActorCollected { actor_id, epoch } => {
910 if let Some(barrier) = self.collect(actor_id, epoch) {
911 return Poll::Ready(ManagedBarrierStateEvent::BarrierCollected {
912 barrier,
913 partial_graph_id: self.partial_graph_id,
914 });
915 }
916 }
917 LocalBarrierEvent::ReportCreateProgress {
918 epoch,
919 fragment_id,
920 actor,
921 state,
922 } => {
923 self.update_create_mview_progress(epoch, fragment_id, actor, state);
924 }
925 LocalBarrierEvent::ReportSourceListFinished {
926 epoch,
927 actor_id,
928 table_id,
929 associated_source_id,
930 } => {
931 self.report_source_list_finished(
932 epoch,
933 actor_id,
934 table_id,
935 associated_source_id,
936 );
937 }
938 LocalBarrierEvent::ReportSourceLoadFinished {
939 epoch,
940 actor_id,
941 table_id,
942 associated_source_id,
943 } => {
944 self.report_source_load_finished(
945 epoch,
946 actor_id,
947 table_id,
948 associated_source_id,
949 );
950 }
951 LocalBarrierEvent::RefreshFinished {
952 epoch,
953 actor_id,
954 table_id,
955 staging_table_id,
956 } => {
957 self.report_refresh_finished(epoch, actor_id, table_id, staging_table_id);
958 }
959 LocalBarrierEvent::RegisterBarrierSender {
960 actor_id,
961 barrier_sender,
962 } => {
963 if let Err(err) = self.register_barrier_sender(actor_id, barrier_sender) {
964 return Poll::Ready(ManagedBarrierStateEvent::ActorError {
965 actor_id,
966 err,
967 partial_graph_id: self.partial_graph_id,
968 });
969 }
970 }
971 LocalBarrierEvent::RegisterLocalUpstreamOutput {
972 actor_id,
973 upstream_actor_id,
974 upstream_partial_graph_id,
975 term_id,
976 tx,
977 } => {
978 return Poll::Ready(ManagedBarrierStateEvent::RegisterLocalUpstreamOutput {
979 actor_id,
980 upstream_actor_id,
981 upstream_partial_graph_id,
982 term_id,
983 tx,
984 });
985 }
986 LocalBarrierEvent::ReportCdcTableBackfillProgress {
987 actor_id,
988 epoch,
989 state,
990 } => {
991 self.update_cdc_table_backfill_progress(epoch, actor_id, state);
992 }
993 LocalBarrierEvent::ReportCdcSourceOffsetUpdated {
994 epoch,
995 actor_id,
996 source_id,
997 } => {
998 self.report_cdc_source_offset_updated(epoch, actor_id, source_id);
999 }
1000 LocalBarrierEvent::ReportIcebergPkIndexSinkMetadata {
1001 epoch,
1002 sink_id,
1003 actor_id,
1004 role,
1005 metadata,
1006 } => {
1007 self.report_iceberg_pk_index_sink_metadata(
1008 epoch, sink_id, actor_id, role, metadata,
1009 );
1010 }
1011 }
1012 }
1013
1014 debug_assert!(self.graph_state.may_have_collected_all().is_none());
1015 Poll::Pending
1016 }
1017}
1018
1019impl PartialGraphState {
1020 #[must_use]
1021 pub(super) fn collect(&mut self, actor_id: ActorId, epoch: EpochPair) -> Option<Barrier> {
1022 let is_finished = self
1023 .actor_states
1024 .get_mut(&actor_id)
1025 .expect("should exist")
1026 .collect(epoch);
1027 if is_finished {
1028 let state = self.actor_states.remove(&actor_id).expect("should exist");
1029 if let Some(monitor_task_handle) = state.monitor_task_handle {
1030 monitor_task_handle.abort();
1031 }
1032 }
1033 self.graph_state.collect(actor_id, epoch);
1034 self.graph_state.may_have_collected_all()
1035 }
1036
1037 pub(super) fn pop_barrier_to_complete(&mut self, prev_epoch: u64) -> BarrierToComplete {
1038 self.graph_state.pop_barrier_to_complete(prev_epoch)
1039 }
1040
1041 async fn try_find_root_actor_failure(
1045 &mut self,
1046 first_failure: Option<(Option<ActorId>, StreamError)>,
1047 ) -> Option<ScoredStreamError> {
1048 let mut later_errs = vec![];
1049 let _ = tokio::time::timeout(Duration::from_secs(3), async {
1051 let mut uncollected_actors: HashSet<_> = self.actor_states.keys().cloned().collect();
1052 if let Some((Some(failed_actor), _)) = &first_failure {
1053 uncollected_actors.remove(failed_actor);
1054 }
1055 while !uncollected_actors.is_empty()
1056 && let Some((actor_id, error)) = self.actor_failure_rx.recv().await
1057 {
1058 uncollected_actors.remove(&actor_id);
1059 later_errs.push(error);
1060 }
1061 })
1062 .await;
1063
1064 first_failure
1065 .into_iter()
1066 .map(|(_, err)| err)
1067 .chain(later_errs)
1068 .map(|e| e.with_score())
1069 .max_by_key(|e| e.score)
1070 }
1071
1072 pub(super) fn report_source_list_finished(
1074 &mut self,
1075 epoch: EpochPair,
1076 actor_id: ActorId,
1077 table_id: TableId,
1078 associated_source_id: SourceId,
1079 ) {
1080 if let Some(actor_state) = self.actor_states.get(&actor_id)
1082 && actor_state.inflight_barriers.contains(&epoch.prev)
1083 {
1084 self.graph_state
1085 .list_finished_source_ids
1086 .entry(epoch.curr)
1087 .or_default()
1088 .push(PbListFinishedSource {
1089 reporter_actor_id: actor_id,
1090 table_id,
1091 associated_source_id,
1092 });
1093 } else {
1094 warn!(
1095 ?epoch,
1096 %actor_id, %table_id, %associated_source_id, "ignore source list finished"
1097 );
1098 }
1099 }
1100
1101 pub(super) fn report_source_load_finished(
1103 &mut self,
1104 epoch: EpochPair,
1105 actor_id: ActorId,
1106 table_id: TableId,
1107 associated_source_id: SourceId,
1108 ) {
1109 if let Some(actor_state) = self.actor_states.get(&actor_id)
1111 && actor_state.inflight_barriers.contains(&epoch.prev)
1112 {
1113 self.graph_state
1114 .load_finished_source_ids
1115 .entry(epoch.curr)
1116 .or_default()
1117 .push(PbLoadFinishedSource {
1118 reporter_actor_id: actor_id,
1119 table_id,
1120 associated_source_id,
1121 });
1122 } else {
1123 warn!(
1124 ?epoch,
1125 %actor_id, %table_id, %associated_source_id, "ignore source load finished"
1126 );
1127 }
1128 }
1129
1130 pub(super) fn report_cdc_source_offset_updated(
1132 &mut self,
1133 epoch: EpochPair,
1134 actor_id: ActorId,
1135 source_id: SourceId,
1136 ) {
1137 if let Some(actor_state) = self.actor_states.get(&actor_id)
1138 && actor_state.inflight_barriers.contains(&epoch.prev)
1139 {
1140 self.graph_state
1141 .cdc_source_offset_updated
1142 .entry(epoch.curr)
1143 .or_default()
1144 .push(PbCdcSourceOffsetUpdated {
1145 reporter_actor_id: actor_id,
1146 source_id,
1147 });
1148 } else {
1149 warn!(
1150 ?epoch,
1151 %actor_id, %source_id, "ignore cdc source offset updated"
1152 );
1153 }
1154 }
1155
1156 pub(super) fn report_iceberg_pk_index_sink_metadata(
1158 &mut self,
1159 epoch: EpochPair,
1160 sink_id: SinkId,
1161 actor_id: ActorId,
1162 role: PbIcebergPkIndexSinkRole,
1163 metadata: Option<SinkMetadata>,
1164 ) {
1165 if let Some(actor_state) = self.actor_states.get(&actor_id)
1166 && actor_state.inflight_barriers.contains(&epoch.prev)
1167 {
1168 self.graph_state
1169 .iceberg_pk_index_sink_metadata
1170 .entry(epoch.curr)
1171 .or_default()
1172 .push(PbIcebergPkIndexSinkMetadata {
1173 sink_id,
1174 reporter_actor_id: actor_id,
1175 prev_epoch: epoch.prev,
1176 role: role as i32,
1177 metadata,
1178 });
1179 } else {
1180 tracing::warn!(
1181 ?epoch,
1182 %actor_id,
1183 %sink_id,
1184 ?role,
1185 "ignore iceberg v3 sink metadata report from non-inflight actor"
1186 );
1187 }
1188 }
1189
1190 pub(super) fn report_refresh_finished(
1192 &mut self,
1193 epoch: EpochPair,
1194 actor_id: ActorId,
1195 table_id: TableId,
1196 staging_table_id: TableId,
1197 ) {
1198 let Some(actor_state) = self.actor_states.get(&actor_id) else {
1200 warn!(
1201 ?epoch,
1202 %actor_id, %table_id, "ignore refresh finished table: actor_state not found"
1203 );
1204 return;
1205 };
1206 if !actor_state.inflight_barriers.contains(&epoch.prev) {
1207 warn!(
1208 ?epoch,
1209 %actor_id,
1210 %table_id,
1211 inflight_barriers = ?actor_state.inflight_barriers,
1212 "ignore refresh finished table: partial_graph_id not found in inflight_barriers"
1213 );
1214 return;
1215 };
1216 self.graph_state
1217 .refresh_finished_tables
1218 .entry(epoch.curr)
1219 .or_default()
1220 .insert(table_id);
1221 self.graph_state
1222 .truncate_tables
1223 .entry(epoch.curr)
1224 .or_default()
1225 .insert(staging_table_id);
1226 }
1227}
1228
1229impl PartialGraphManagedBarrierState {
1230 fn may_have_collected_all(&mut self) -> Option<Barrier> {
1233 for barrier_state in self.epoch_barrier_state_map.values_mut() {
1234 match &barrier_state.inner {
1235 ManagedBarrierStateInner::Issued(IssuedState {
1236 remaining_actors, ..
1237 }) if remaining_actors.is_empty() => {}
1238 ManagedBarrierStateInner::AllCollected { .. } => {
1239 continue;
1240 }
1241 ManagedBarrierStateInner::Issued(_) => {
1242 break;
1243 }
1244 }
1245
1246 self.barrier_manager_progress.inc();
1247
1248 let create_mview_progress = self
1249 .create_mview_progress
1250 .remove(&barrier_state.barrier.epoch.curr)
1251 .unwrap_or_default()
1252 .into_iter()
1253 .map(|(actor, (fragment_id, state))| state.to_pb(fragment_id, actor))
1254 .collect();
1255
1256 let list_finished_source_ids = self
1257 .list_finished_source_ids
1258 .remove(&barrier_state.barrier.epoch.curr)
1259 .unwrap_or_default();
1260
1261 let load_finished_source_ids = self
1262 .load_finished_source_ids
1263 .remove(&barrier_state.barrier.epoch.curr)
1264 .unwrap_or_default();
1265
1266 let cdc_table_backfill_progress = self
1267 .cdc_table_backfill_progress
1268 .remove(&barrier_state.barrier.epoch.curr)
1269 .unwrap_or_default()
1270 .into_iter()
1271 .map(|(actor, state)| state.to_pb(actor, barrier_state.barrier.epoch.curr))
1272 .collect();
1273
1274 let cdc_source_offset_updated = self
1275 .cdc_source_offset_updated
1276 .remove(&barrier_state.barrier.epoch.curr)
1277 .unwrap_or_default();
1278
1279 let iceberg_pk_index_sink_metadata = self
1280 .iceberg_pk_index_sink_metadata
1281 .remove(&barrier_state.barrier.epoch.curr)
1282 .unwrap_or_default();
1283
1284 let truncate_tables = self
1285 .truncate_tables
1286 .remove(&barrier_state.barrier.epoch.curr)
1287 .unwrap_or_default()
1288 .into_iter()
1289 .collect();
1290
1291 let refresh_finished_tables = self
1292 .refresh_finished_tables
1293 .remove(&barrier_state.barrier.epoch.curr)
1294 .unwrap_or_default()
1295 .into_iter()
1296 .collect();
1297 let prev_state = replace(
1298 &mut barrier_state.inner,
1299 ManagedBarrierStateInner::AllCollected {
1300 create_mview_progress,
1301 list_finished_source_ids,
1302 load_finished_source_ids,
1303 truncate_tables,
1304 refresh_finished_tables,
1305 cdc_table_backfill_progress,
1306 cdc_source_offset_updated,
1307 iceberg_pk_index_sink_metadata,
1308 },
1309 );
1310
1311 must_match!(prev_state, ManagedBarrierStateInner::Issued(IssuedState {
1312 barrier_inflight_latency: timer,
1313 ..
1314 }) => {
1315 timer.observe_duration();
1316 });
1317
1318 return Some(barrier_state.barrier.clone());
1319 }
1320 None
1321 }
1322
1323 fn pop_barrier_to_complete(&mut self, prev_epoch: u64) -> BarrierToComplete {
1324 let (popped_prev_epoch, barrier_state) = self
1325 .epoch_barrier_state_map
1326 .pop_first()
1327 .expect("should exist");
1328
1329 assert_eq!(prev_epoch, popped_prev_epoch);
1330
1331 let (
1332 create_mview_progress,
1333 list_finished_source_ids,
1334 load_finished_source_ids,
1335 cdc_table_backfill_progress,
1336 cdc_source_offset_updated,
1337 iceberg_pk_index_sink_metadata,
1338 truncate_tables,
1339 refresh_finished_tables,
1340 ) = must_match!(barrier_state.inner, ManagedBarrierStateInner::AllCollected {
1341 create_mview_progress,
1342 list_finished_source_ids,
1343 load_finished_source_ids,
1344 truncate_tables,
1345 refresh_finished_tables,
1346 cdc_table_backfill_progress,
1347 cdc_source_offset_updated,
1348 iceberg_pk_index_sink_metadata,
1349 } => {
1350 (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)
1351 });
1352 BarrierToComplete {
1353 barrier: barrier_state.barrier,
1354 table_ids: barrier_state.table_ids,
1355 create_mview_progress,
1356 list_finished_source_ids,
1357 load_finished_source_ids,
1358 truncate_tables,
1359 refresh_finished_tables,
1360 cdc_table_backfill_progress,
1361 cdc_source_offset_updated,
1362 iceberg_pk_index_sink_metadata,
1363 }
1364 }
1365
1366 pub(super) fn barrier_sync_latency(&self) -> LabelGuardedHistogram {
1367 self.barrier_sync_latency.clone()
1368 }
1369}
1370
1371pub(crate) struct BarrierToComplete {
1372 pub barrier: Barrier,
1373 pub table_ids: Option<HashSet<TableId>>,
1374 pub create_mview_progress: Vec<PbCreateMviewProgress>,
1375 pub list_finished_source_ids: Vec<PbListFinishedSource>,
1376 pub load_finished_source_ids: Vec<PbLoadFinishedSource>,
1377 pub truncate_tables: Vec<TableId>,
1378 pub refresh_finished_tables: Vec<TableId>,
1379 pub cdc_table_backfill_progress: Vec<PbCdcTableBackfillProgress>,
1380 pub cdc_source_offset_updated: Vec<PbCdcSourceOffsetUpdated>,
1381 pub iceberg_pk_index_sink_metadata: Vec<PbIcebergPkIndexSinkMetadata>,
1382}
1383
1384impl PartialGraphManagedBarrierState {
1385 pub(super) fn collect(&mut self, actor_id: impl Into<ActorId>, epoch: EpochPair) {
1387 let actor_id = actor_id.into();
1388 tracing::debug!(
1389 target: "events::stream::barrier::manager::collect",
1390 ?epoch, %actor_id, state = ?self.epoch_barrier_state_map,
1391 "collect_barrier",
1392 );
1393
1394 match self.epoch_barrier_state_map.get_mut(&epoch.prev) {
1395 None => {
1396 panic!(
1400 "cannot collect new actor barrier {:?} at current state: None",
1401 epoch,
1402 )
1403 }
1404 Some(&mut BarrierState {
1405 ref barrier,
1406 inner:
1407 ManagedBarrierStateInner::Issued(IssuedState {
1408 ref mut remaining_actors,
1409 ..
1410 }),
1411 ..
1412 }) => {
1413 let exist = remaining_actors.remove(&actor_id);
1414 assert!(
1415 exist,
1416 "the actor doesn't exist. actor_id: {:?}, curr_epoch: {:?}",
1417 actor_id, epoch.curr
1418 );
1419 assert_eq!(barrier.epoch.curr, epoch.curr);
1420 }
1421 Some(BarrierState { inner, .. }) => {
1422 panic!(
1423 "cannot collect new actor barrier {:?} at current state: {:?}",
1424 epoch, inner
1425 )
1426 }
1427 }
1428 }
1429
1430 pub(super) fn transform_to_issued(
1433 &mut self,
1434 barrier: &Barrier,
1435 actor_ids_to_collect: impl IntoIterator<Item = ActorId>,
1436 table_ids: HashSet<TableId>,
1437 ) {
1438 let timer = self.barrier_inflight_latency.start_timer();
1439
1440 if let Some(hummock) = self.state_store.as_hummock() {
1441 hummock.start_epoch(barrier.epoch.curr, table_ids.clone());
1442 }
1443
1444 let table_ids = match barrier.kind {
1445 BarrierKind::Unspecified => {
1446 unreachable!()
1447 }
1448 BarrierKind::Initial => {
1449 assert!(
1450 self.prev_barrier_table_ids.is_none(),
1451 "non empty table_ids at initial barrier: {:?}",
1452 self.prev_barrier_table_ids
1453 );
1454 info!(epoch = ?barrier.epoch, "initialize at Initial barrier");
1455 self.prev_barrier_table_ids = Some((barrier.epoch, table_ids));
1456 None
1457 }
1458 BarrierKind::Barrier => {
1459 if let Some((prev_epoch, prev_table_ids)) = self.prev_barrier_table_ids.as_mut() {
1460 assert_eq!(prev_epoch.curr, barrier.epoch.prev);
1461 assert_eq!(prev_table_ids, &table_ids);
1462 *prev_epoch = barrier.epoch;
1463 } else {
1464 info!(epoch = ?barrier.epoch, "initialize at non-checkpoint barrier");
1465 self.prev_barrier_table_ids = Some((barrier.epoch, table_ids));
1466 }
1467 None
1468 }
1469 BarrierKind::Checkpoint => Some(
1470 if let Some((prev_epoch, prev_table_ids)) = self
1471 .prev_barrier_table_ids
1472 .replace((barrier.epoch, table_ids))
1473 && prev_epoch.curr == barrier.epoch.prev
1474 {
1475 prev_table_ids
1476 } else {
1477 debug!(epoch = ?barrier.epoch, "reinitialize at Checkpoint barrier");
1478 HashSet::new()
1479 },
1480 ),
1481 };
1482
1483 if let Some(&mut BarrierState { ref inner, .. }) =
1484 self.epoch_barrier_state_map.get_mut(&barrier.epoch.prev)
1485 {
1486 {
1487 panic!(
1488 "barrier epochs{:?} state has already been `Issued`. Current state: {:?}",
1489 barrier.epoch, inner
1490 );
1491 }
1492 };
1493
1494 self.epoch_barrier_state_map.insert(
1495 barrier.epoch.prev,
1496 BarrierState {
1497 barrier: barrier.clone(),
1498 inner: ManagedBarrierStateInner::Issued(IssuedState {
1499 remaining_actors: BTreeSet::from_iter(actor_ids_to_collect),
1500 barrier_inflight_latency: timer,
1501 }),
1502 table_ids,
1503 },
1504 );
1505 }
1506
1507 #[cfg(test)]
1508 async fn pop_next_completed_epoch(&mut self) -> u64 {
1509 if let Some(barrier) = self.may_have_collected_all() {
1510 self.pop_barrier_to_complete(barrier.epoch.prev);
1511 return barrier.epoch.prev;
1512 }
1513 pending().await
1514 }
1515}
1516
1517#[cfg(test)]
1518mod tests {
1519 use std::collections::HashSet;
1520
1521 use risingwave_common::util::epoch::test_epoch;
1522
1523 use crate::executor::Barrier;
1524 use crate::task::barrier_worker::managed_state::PartialGraphManagedBarrierState;
1525
1526 #[tokio::test]
1527 async fn test_managed_state_add_actor() {
1528 let mut managed_barrier_state = PartialGraphManagedBarrierState::for_test();
1529 let barrier1 = Barrier::new_test_barrier(test_epoch(1));
1530 let barrier2 = Barrier::new_test_barrier(test_epoch(2));
1531 let barrier3 = Barrier::new_test_barrier(test_epoch(3));
1532 let actor_ids_to_collect1 = HashSet::from([1.into(), 2.into()]);
1533 let actor_ids_to_collect2 = HashSet::from([1.into(), 2.into()]);
1534 let actor_ids_to_collect3 = HashSet::from([1.into(), 2.into(), 3.into()]);
1535 managed_barrier_state.transform_to_issued(&barrier1, actor_ids_to_collect1, HashSet::new());
1536 managed_barrier_state.transform_to_issued(&barrier2, actor_ids_to_collect2, HashSet::new());
1537 managed_barrier_state.transform_to_issued(&barrier3, actor_ids_to_collect3, HashSet::new());
1538 managed_barrier_state.collect(1, barrier1.epoch);
1539 managed_barrier_state.collect(2, barrier1.epoch);
1540 assert_eq!(
1541 managed_barrier_state.pop_next_completed_epoch().await,
1542 test_epoch(0)
1543 );
1544 assert_eq!(
1545 managed_barrier_state
1546 .epoch_barrier_state_map
1547 .first_key_value()
1548 .unwrap()
1549 .0,
1550 &test_epoch(1)
1551 );
1552 managed_barrier_state.collect(1, barrier2.epoch);
1553 managed_barrier_state.collect(1, barrier3.epoch);
1554 managed_barrier_state.collect(2, barrier2.epoch);
1555 assert_eq!(
1556 managed_barrier_state.pop_next_completed_epoch().await,
1557 test_epoch(1)
1558 );
1559 assert_eq!(
1560 managed_barrier_state
1561 .epoch_barrier_state_map
1562 .first_key_value()
1563 .unwrap()
1564 .0,
1565 &test_epoch(2)
1566 );
1567 managed_barrier_state.collect(2, barrier3.epoch);
1568 managed_barrier_state.collect(3, barrier3.epoch);
1569 assert_eq!(
1570 managed_barrier_state.pop_next_completed_epoch().await,
1571 test_epoch(2)
1572 );
1573 assert!(managed_barrier_state.epoch_barrier_state_map.is_empty());
1574 }
1575
1576 #[tokio::test]
1577 async fn test_managed_state_stop_actor() {
1578 let mut managed_barrier_state = PartialGraphManagedBarrierState::for_test();
1579 let barrier1 = Barrier::new_test_barrier(test_epoch(1));
1580 let barrier2 = Barrier::new_test_barrier(test_epoch(2));
1581 let barrier3 = Barrier::new_test_barrier(test_epoch(3));
1582 let actor_ids_to_collect1 = HashSet::from([1.into(), 2.into(), 3.into(), 4.into()]);
1583 let actor_ids_to_collect2 = HashSet::from([1.into(), 2.into(), 3.into()]);
1584 let actor_ids_to_collect3 = HashSet::from([1.into(), 2.into()]);
1585 managed_barrier_state.transform_to_issued(&barrier1, actor_ids_to_collect1, HashSet::new());
1586 managed_barrier_state.transform_to_issued(&barrier2, actor_ids_to_collect2, HashSet::new());
1587 managed_barrier_state.transform_to_issued(&barrier3, actor_ids_to_collect3, HashSet::new());
1588
1589 managed_barrier_state.collect(1, barrier1.epoch);
1590 managed_barrier_state.collect(1, barrier2.epoch);
1591 managed_barrier_state.collect(1, barrier3.epoch);
1592 managed_barrier_state.collect(2, barrier1.epoch);
1593 managed_barrier_state.collect(2, barrier2.epoch);
1594 managed_barrier_state.collect(2, barrier3.epoch);
1595 assert_eq!(
1596 managed_barrier_state
1597 .epoch_barrier_state_map
1598 .first_key_value()
1599 .unwrap()
1600 .0,
1601 &0
1602 );
1603 managed_barrier_state.collect(3, barrier1.epoch);
1604 managed_barrier_state.collect(3, barrier2.epoch);
1605 assert_eq!(
1606 managed_barrier_state
1607 .epoch_barrier_state_map
1608 .first_key_value()
1609 .unwrap()
1610 .0,
1611 &0
1612 );
1613 managed_barrier_state.collect(4, barrier1.epoch);
1614 assert_eq!(
1615 managed_barrier_state.pop_next_completed_epoch().await,
1616 test_epoch(0)
1617 );
1618 assert_eq!(
1619 managed_barrier_state.pop_next_completed_epoch().await,
1620 test_epoch(1)
1621 );
1622 assert_eq!(
1623 managed_barrier_state.pop_next_completed_epoch().await,
1624 test_epoch(2)
1625 );
1626 assert!(managed_barrier_state.epoch_barrier_state_map.is_empty());
1627 }
1628}