Skip to main content

risingwave_stream/executor/
mod.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod prelude;
16
17use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
18use std::fmt::Debug;
19use std::future::pending;
20use std::hash::Hash;
21use std::pin::Pin;
22use std::sync::Arc;
23use std::task::Poll;
24use std::vec;
25
26use await_tree::InstrumentAwait;
27use enum_as_inner::EnumAsInner;
28use futures::future::try_join_all;
29use futures::stream::{BoxStream, FusedStream, FuturesUnordered, StreamFuture};
30use futures::{FutureExt, Stream, StreamExt, TryStreamExt};
31use itertools::Itertools;
32use prometheus::core::{AtomicU64, GenericCounter};
33use risingwave_common::array::StreamChunk;
34use risingwave_common::bitmap::Bitmap;
35use risingwave_common::catalog::{Schema, TableId};
36use risingwave_common::config::StreamingConfig;
37use risingwave_common::metrics::LabelGuardedMetric;
38use risingwave_common::row::OwnedRow;
39use risingwave_common::types::{DataType, Datum, DefaultOrd, ScalarImpl};
40use risingwave_common::util::epoch::{Epoch, EpochPair};
41use risingwave_common::util::tracing::TracingContext;
42use risingwave_common::util::value_encoding::{DatumFromProtoExt, DatumToProtoExt};
43use risingwave_common_estimate_size::EstimateSize;
44use risingwave_connector::source::SplitImpl;
45use risingwave_expr::expr::NonStrictExpression;
46use risingwave_pb::data::PbEpoch;
47use risingwave_pb::expr::PbInputRef;
48use risingwave_pb::stream_plan::add_mutation::PbNewUpstreamSink;
49use risingwave_pb::stream_plan::barrier::BarrierKind;
50use risingwave_pb::stream_plan::barrier_mutation::Mutation as PbMutation;
51use risingwave_pb::stream_plan::stream_node::PbStreamKind;
52use risingwave_pb::stream_plan::throttle_mutation::ThrottleConfig;
53use risingwave_pb::stream_plan::update_mutation::{DispatcherUpdate, MergeUpdate};
54use risingwave_pb::stream_plan::{
55    PbBarrier, PbBarrierMutation, PbDispatcher, PbSinkSchemaChange, PbStreamMessageBatch,
56    PbWatermark, SubscriptionUpstreamInfo,
57};
58use smallvec::SmallVec;
59use tokio::sync::mpsc;
60use tokio::time::{Duration, Instant};
61
62use crate::error::StreamResult;
63use crate::executor::exchange::input::{
64    BoxedActorInput, BoxedInput, assert_equal_dispatcher_barrier, new_input,
65};
66use crate::executor::monitor::ActorInputMetrics;
67use crate::executor::prelude::StreamingMetrics;
68use crate::executor::watermark::BufferedWatermarks;
69use crate::task::{ActorId, FragmentId, LocalBarrierManager};
70
71mod actor;
72mod barrier_align;
73pub mod exchange;
74pub mod monitor;
75
76pub mod aggregate;
77pub mod asof_join;
78mod backfill;
79mod barrier_recv;
80mod batch_query;
81mod chain;
82mod changelog;
83mod dedup;
84mod dispatch;
85pub mod dml;
86mod dynamic_filter;
87pub mod eowc;
88pub mod error;
89mod expand;
90mod filter;
91mod gap_fill;
92pub mod hash_join;
93mod hop_window;
94pub(crate) mod iceberg_with_pk_index;
95mod join;
96pub mod locality_provider;
97mod lookup;
98mod lookup_union;
99mod merge;
100mod mview;
101mod nested_loop_temporal_join;
102mod no_op;
103mod now;
104mod over_window;
105pub mod project;
106mod receiver;
107pub mod row_id_gen;
108mod sink;
109pub mod source;
110mod stream_reader;
111pub mod subtask;
112mod temporal_join;
113mod top_n;
114mod troublemaker;
115mod union;
116mod upstream_sink_union;
117mod values;
118mod watermark;
119mod watermark_filter;
120mod wrapper;
121
122mod approx_percentile;
123
124mod row_merge;
125
126#[cfg(test)]
127mod integration_tests;
128mod sync_kv_log_store;
129#[cfg(any(test, feature = "test"))]
130pub mod test_utils;
131mod utils;
132mod vector;
133
134pub use actor::{Actor, ActorContext, ActorContextRef};
135use anyhow::{Context, anyhow};
136pub use approx_percentile::global::GlobalApproxPercentileExecutor;
137pub use approx_percentile::local::LocalApproxPercentileExecutor;
138pub use backfill::arrangement_backfill::*;
139pub use backfill::cdc::{
140    CdcBackfillExecutor, ExternalStorageTable, ParallelizedCdcBackfillExecutor,
141};
142pub use backfill::no_shuffle_backfill::*;
143pub use backfill::snapshot_backfill::*;
144pub use barrier_recv::BarrierRecvExecutor;
145pub use batch_query::BatchQueryExecutor;
146pub use chain::ChainExecutor;
147pub use changelog::ChangeLogExecutor;
148pub use dedup::AppendOnlyDedupExecutor;
149pub use dispatch::{DispatchExecutor, SyncLogStoreDispatchExecutor};
150pub use dynamic_filter::DynamicFilterExecutor;
151pub use error::{StreamExecutorError, StreamExecutorResult};
152pub use expand::ExpandExecutor;
153pub use filter::{FilterExecutor, UpsertFilterExecutor};
154pub use gap_fill::{GapFillExecutor, GapFillExecutorArgs};
155pub use hash_join::*;
156pub use hop_window::HopWindowExecutor;
157pub use iceberg_with_pk_index::{
158    IcebergWriterImpl, PositionDeleteHandlerImpl, PositionDeleteMergerExecutor, WriterExecutor,
159};
160pub use join::asof_join::{AsOfCpuEncoding, AsOfMemoryEncoding};
161pub use join::row::{CachedJoinRow, CpuEncoding, JoinEncoding, MemoryEncoding};
162pub use join::{AsOfDesc, AsOfJoinType, JoinType};
163pub use lookup::*;
164pub use lookup_union::LookupUnionExecutor;
165pub use merge::MergeExecutor;
166pub(crate) use merge::{MergeExecutorInput, MergeExecutorUpstream};
167pub use mview::{MaterializeExecutor, RefreshableMaterializeArgs};
168pub use nested_loop_temporal_join::NestedLoopTemporalJoinExecutor;
169pub use no_op::NoOpExecutor;
170pub use now::*;
171pub use over_window::*;
172pub use receiver::ReceiverExecutor;
173use risingwave_common::id::SourceId;
174pub use row_merge::RowMergeExecutor;
175pub use sink::SinkExecutor;
176pub use sync_kv_log_store::SyncedKvLogStoreExecutor;
177pub use sync_kv_log_store::metrics::SyncedKvLogStoreMetrics;
178pub use temporal_join::TemporalJoinExecutor;
179pub use top_n::{
180    AppendOnlyGroupTopNExecutor, AppendOnlyTopNExecutor, GroupTopNExecutor, TopNExecutor,
181};
182pub use troublemaker::TroublemakerExecutor;
183pub use union::UnionExecutor;
184pub use upstream_sink_union::{UpstreamFragmentInfo, UpstreamSinkUnionExecutor};
185pub use utils::DummyExecutor;
186pub use values::ValuesExecutor;
187pub use vector::*;
188pub use watermark_filter::{UpsertWatermarkFilterExecutor, WatermarkFilterExecutor};
189pub use wrapper::WrapperExecutor;
190
191use self::barrier_align::AlignedMessageStream;
192
193pub type MessageStreamItemInner<M> = StreamExecutorResult<MessageInner<M>>;
194pub type MessageStreamItem = MessageStreamItemInner<BarrierMutationType>;
195pub type DispatcherMessageStreamItem = StreamExecutorResult<DispatcherMessage>;
196pub type BoxedMessageStream = BoxStream<'static, MessageStreamItem>;
197
198pub use risingwave_common::util::epoch::task_local::{curr_epoch, epoch, prev_epoch};
199use risingwave_connector::sink::catalog::SinkId;
200use risingwave_connector::source::cdc::{
201    CdcTableSnapshotSplitAssignmentWithGeneration,
202    build_actor_cdc_table_snapshot_splits_with_generation,
203};
204use risingwave_pb::id::{ExecutorId, SubscriberId};
205use risingwave_pb::stream_plan::stream_message_batch::{BarrierBatch, StreamMessageBatch};
206
207pub trait MessageStreamInner<M> = Stream<Item = MessageStreamItemInner<M>> + Send;
208pub trait MessageStream = Stream<Item = MessageStreamItem> + Send;
209pub trait DispatcherMessageStream = Stream<Item = DispatcherMessageStreamItem> + Send;
210
211/// Static information of an executor.
212#[derive(Debug, Default, Clone)]
213pub struct ExecutorInfo {
214    /// The schema of the OUTPUT of the executor.
215    pub schema: Schema,
216
217    /// The stream key indices of the OUTPUT of the executor.
218    pub stream_key: StreamKey,
219
220    /// The stream kind of the OUTPUT of the executor.
221    pub stream_kind: PbStreamKind,
222
223    /// Identity of the executor.
224    pub identity: String,
225
226    /// The executor id of the executor.
227    pub id: ExecutorId,
228}
229
230impl ExecutorInfo {
231    pub fn for_test(schema: Schema, stream_key: StreamKey, identity: String, id: u64) -> Self {
232        Self {
233            schema,
234            stream_key,
235            stream_kind: PbStreamKind::Retract, // dummy value for test
236            identity,
237            id: id.into(),
238        }
239    }
240}
241
242/// [`Execute`] describes the methods an executor should implement to handle control messages.
243pub trait Execute: Send + 'static {
244    fn execute(self: Box<Self>) -> BoxedMessageStream;
245
246    fn boxed(self) -> Box<dyn Execute>
247    where
248        Self: Sized + Send + 'static,
249    {
250        Box::new(self)
251    }
252}
253
254/// [`Executor`] combines the static information ([`ExecutorInfo`]) and the executable object to
255/// handle messages ([`Execute`]).
256pub struct Executor {
257    info: ExecutorInfo,
258    execute: Box<dyn Execute>,
259}
260
261impl Executor {
262    pub fn new(info: ExecutorInfo, execute: Box<dyn Execute>) -> Self {
263        Self { info, execute }
264    }
265
266    pub fn info(&self) -> &ExecutorInfo {
267        &self.info
268    }
269
270    pub fn schema(&self) -> &Schema {
271        &self.info.schema
272    }
273
274    pub fn stream_key(&self) -> StreamKeyRef<'_> {
275        &self.info.stream_key
276    }
277
278    pub fn stream_kind(&self) -> PbStreamKind {
279        self.info.stream_kind
280    }
281
282    pub fn identity(&self) -> &str {
283        &self.info.identity
284    }
285
286    pub fn execute(self) -> BoxedMessageStream {
287        self.execute.execute()
288    }
289}
290
291impl std::fmt::Debug for Executor {
292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293        f.write_str(self.identity())
294    }
295}
296
297impl From<(ExecutorInfo, Box<dyn Execute>)> for Executor {
298    fn from((info, execute): (ExecutorInfo, Box<dyn Execute>)) -> Self {
299        Self::new(info, execute)
300    }
301}
302
303impl<E> From<(ExecutorInfo, E)> for Executor
304where
305    E: Execute,
306{
307    fn from((info, execute): (ExecutorInfo, E)) -> Self {
308        Self::new(info, execute.boxed())
309    }
310}
311
312pub const INVALID_EPOCH: u64 = 0;
313
314type UpstreamFragmentId = FragmentId;
315type SplitAssignments = HashMap<ActorId, Vec<SplitImpl>>;
316
317#[derive(Debug, Clone)]
318#[cfg_attr(any(test, feature = "test"), derive(Default, PartialEq))]
319pub struct UpdateMutation {
320    pub dispatchers: HashMap<ActorId, Vec<DispatcherUpdate>>,
321    pub merges: HashMap<(ActorId, UpstreamFragmentId), MergeUpdate>,
322    pub vnode_bitmaps: HashMap<ActorId, Arc<Bitmap>>,
323    pub dropped_actors: HashSet<ActorId>,
324    pub actor_splits: SplitAssignments,
325    pub actor_new_dispatchers: HashMap<ActorId, Vec<PbDispatcher>>,
326    pub actor_cdc_table_snapshot_splits: CdcTableSnapshotSplitAssignmentWithGeneration,
327    pub sink_schema_change: HashMap<SinkId, PbSinkSchemaChange>,
328    pub subscriptions_to_drop: Vec<SubscriptionUpstreamInfo>,
329}
330
331#[derive(Debug, Clone)]
332#[cfg_attr(any(test, feature = "test"), derive(Default, PartialEq))]
333pub struct AddMutation {
334    pub adds: HashMap<ActorId, Vec<PbDispatcher>>,
335    pub added_actors: HashSet<ActorId>,
336    pub dropped_actors: HashSet<ActorId>,
337    // TODO: remove this and use `SourceChangesSplit` after we support multiple mutations.
338    pub splits: SplitAssignments,
339    pub pause: bool,
340    /// (`upstream_mv_table_id`,  `subscriber_id`)
341    pub subscriptions_to_add: Vec<(TableId, SubscriberId)>,
342    /// nodes which should start backfill
343    pub backfill_nodes_to_pause: HashSet<FragmentId>,
344    pub actor_cdc_table_snapshot_splits: CdcTableSnapshotSplitAssignmentWithGeneration,
345    pub new_upstream_sinks: HashMap<FragmentId, PbNewUpstreamSink>,
346    pub sink_log_store_flush: HashSet<SinkId>,
347}
348
349#[derive(Debug, Clone)]
350#[cfg_attr(any(test, feature = "test"), derive(Default, PartialEq))]
351pub struct StopMutation {
352    pub dropped_actors: HashSet<ActorId>,
353    pub dropped_sink_fragments: HashSet<FragmentId>,
354}
355
356/// See [`PbMutation`] for the semantics of each mutation.
357#[cfg_attr(any(test, feature = "test"), derive(PartialEq))]
358#[derive(Debug, Clone)]
359pub enum Mutation {
360    Stop(StopMutation),
361    Update(UpdateMutation),
362    Add(AddMutation),
363    SourceChangeSplit(SplitAssignments),
364    Pause,
365    Resume,
366    Throttle(HashMap<FragmentId, ThrottleConfig>),
367    ConnectorPropsChange(HashMap<u32, HashMap<String, String>>),
368    DropSubscriptions {
369        /// `subscriber` -> `upstream_mv_table_id`
370        subscriptions_to_drop: Vec<SubscriptionUpstreamInfo>,
371    },
372    StartFragmentBackfill {
373        fragment_ids: HashSet<FragmentId>,
374    },
375    RefreshStart {
376        table_id: TableId,
377        associated_source_id: SourceId,
378    },
379    ListFinish {
380        associated_source_id: SourceId,
381    },
382    LoadFinish {
383        associated_source_id: SourceId,
384    },
385    ResetSource {
386        source_id: SourceId,
387    },
388    InjectSourceOffsets {
389        source_id: SourceId,
390        /// Split ID -> offset (JSON-encoded based on connector type)
391        split_offsets: HashMap<String, String>,
392    },
393}
394
395/// The generic type `M` is the mutation type of the barrier.
396///
397/// For barrier of in the dispatcher, `M` is `()`, which means the mutation is erased.
398/// For barrier flowing within the streaming actor, `M` is the normal `BarrierMutationType`.
399#[derive(Debug, Clone)]
400pub struct BarrierInner<M> {
401    pub epoch: EpochPair,
402    pub mutation: M,
403    pub kind: BarrierKind,
404
405    /// Tracing context for the **current** epoch of this barrier.
406    pub tracing_context: TracingContext,
407}
408
409pub type BarrierMutationType = Option<Arc<Mutation>>;
410pub type Barrier = BarrierInner<BarrierMutationType>;
411pub type DispatcherBarrier = BarrierInner<()>;
412
413impl<M: Default> BarrierInner<M> {
414    /// Create a plain barrier.
415    pub fn new_test_barrier(epoch: u64) -> Self {
416        Self {
417            epoch: EpochPair::new_test_epoch(epoch),
418            kind: BarrierKind::Checkpoint,
419            tracing_context: TracingContext::none(),
420            mutation: Default::default(),
421        }
422    }
423
424    pub fn with_prev_epoch_for_test(epoch: u64, prev_epoch: u64) -> Self {
425        Self {
426            epoch: EpochPair::new(epoch, prev_epoch),
427            kind: BarrierKind::Checkpoint,
428            tracing_context: TracingContext::none(),
429            mutation: Default::default(),
430        }
431    }
432}
433
434impl Barrier {
435    pub fn into_dispatcher(self) -> DispatcherBarrier {
436        DispatcherBarrier {
437            epoch: self.epoch,
438            mutation: (),
439            kind: self.kind,
440            tracing_context: self.tracing_context,
441        }
442    }
443
444    #[must_use]
445    pub fn with_mutation(self, mutation: Mutation) -> Self {
446        Self {
447            mutation: Some(Arc::new(mutation)),
448            ..self
449        }
450    }
451
452    #[must_use]
453    pub fn with_stop(self) -> Self {
454        self.with_mutation(Mutation::Stop(StopMutation {
455            dropped_actors: Default::default(),
456            dropped_sink_fragments: Default::default(),
457        }))
458    }
459
460    /// Whether this barrier carries stop mutation.
461    pub fn is_with_stop_mutation(&self) -> bool {
462        matches!(self.mutation.as_deref(), Some(Mutation::Stop(_)))
463    }
464
465    /// Whether this barrier is to stop the actor with `actor_id`.
466    pub fn is_stop(&self, actor_id: ActorId) -> bool {
467        self.all_stop_actors()
468            .is_some_and(|actors| actors.contains(&actor_id))
469    }
470
471    pub fn is_checkpoint(&self) -> bool {
472        self.kind == BarrierKind::Checkpoint
473    }
474
475    /// Get the initial split assignments for the actor with `actor_id`.
476    ///
477    /// This should only be called on the initial barrier received by the executor. It must be
478    ///
479    /// - `Add` mutation when it's a new streaming job, or recovery.
480    /// - `Update` mutation when it's created for scaling.
481    ///
482    /// Note that `SourceChangeSplit` is **not** included, because it's only used for changing splits
483    /// of existing executors.
484    pub fn initial_split_assignment(&self, actor_id: ActorId) -> Option<&[SplitImpl]> {
485        match self.mutation.as_deref()? {
486            Mutation::Update(UpdateMutation { actor_splits, .. })
487            | Mutation::Add(AddMutation {
488                splits: actor_splits,
489                ..
490            }) => actor_splits.get(&actor_id),
491
492            _ => {
493                if cfg!(debug_assertions) {
494                    panic!(
495                        "the initial mutation of the barrier should not be {:?}",
496                        self.mutation
497                    );
498                }
499                None
500            }
501        }
502        .map(|s| s.as_slice())
503    }
504
505    /// Get all actors that to be stopped (dropped) by this barrier.
506    pub fn all_stop_actors(&self) -> Option<&HashSet<ActorId>> {
507        self.mutation.as_deref()?.all_stop_actors()
508    }
509
510    /// Whether this barrier is to newly add the actor with `actor_id`. This is used for `Chain` and
511    /// `Values` to decide whether to output the existing (historical) data.
512    ///
513    /// By "newly", we mean the actor belongs to a subgraph of a new streaming job. That is, actors
514    /// added for scaling are not included.
515    pub fn is_newly_added(&self, actor_id: ActorId) -> bool {
516        match self.mutation.as_deref() {
517            Some(Mutation::Add(AddMutation { added_actors, .. })) => {
518                added_actors.contains(&actor_id)
519            }
520            _ => false,
521        }
522    }
523
524    pub fn should_start_fragment_backfill(&self, fragment_id: FragmentId) -> bool {
525        if let Some(Mutation::StartFragmentBackfill { fragment_ids }) = self.mutation.as_deref() {
526            fragment_ids.contains(&fragment_id)
527        } else {
528            false
529        }
530    }
531
532    /// Whether this barrier adds new downstream fragment for the actor with `upstream_actor_id`.
533    ///
534    /// # Use case
535    /// Some optimizations are applied when an actor doesn't have any downstreams ("standalone" actors).
536    /// * Pause a standalone shared `SourceExecutor`.
537    /// * Disable a standalone `MaterializeExecutor`'s conflict check.
538    ///
539    /// This is implemented by checking `actor_context.initial_dispatch_num` on startup, and
540    /// check `has_more_downstream_fragments` on barrier to see whether the optimization
541    /// needs to be turned off.
542    ///
543    /// ## Some special cases not included
544    ///
545    /// Note that this is not `has_new_downstream_actor/fragment`. For our use case, we only
546    /// care about **number of downstream fragments** (more precisely, existence).
547    /// - When scaling, the number of downstream actors is changed, and they are "new", but downstream fragments is not changed.
548    /// - When `ALTER TABLE sink_into_table`, the fragment is replaced with a "new" one, but the number is not changed.
549    pub fn has_more_downstream_fragments(&self, upstream_actor_id: ActorId) -> bool {
550        let Some(mutation) = self.mutation.as_deref() else {
551            return false;
552        };
553        match mutation {
554            // Add is for mv, index and sink creation.
555            Mutation::Add(AddMutation { adds, .. }) => adds.get(&upstream_actor_id).is_some(),
556            Mutation::Update(_)
557            | Mutation::Stop(_)
558            | Mutation::Pause
559            | Mutation::Resume
560            | Mutation::SourceChangeSplit(_)
561            | Mutation::Throttle { .. }
562            | Mutation::DropSubscriptions { .. }
563            | Mutation::ConnectorPropsChange(_)
564            | Mutation::StartFragmentBackfill { .. }
565            | Mutation::RefreshStart { .. }
566            | Mutation::ListFinish { .. }
567            | Mutation::LoadFinish { .. }
568            | Mutation::ResetSource { .. }
569            | Mutation::InjectSourceOffsets { .. } => false,
570        }
571    }
572
573    /// Whether this barrier requires the executor to pause its data stream on startup.
574    pub fn is_pause_on_startup(&self) -> bool {
575        match self.mutation.as_deref() {
576            Some(Mutation::Add(AddMutation { pause, .. })) => *pause,
577            _ => false,
578        }
579    }
580
581    pub fn is_backfill_pause_on_startup(&self, backfill_fragment_id: FragmentId) -> bool {
582        match self.mutation.as_deref() {
583            Some(Mutation::Add(AddMutation {
584                backfill_nodes_to_pause,
585                ..
586            })) => backfill_nodes_to_pause.contains(&backfill_fragment_id),
587            Some(Mutation::Update(_)) => false,
588            _ => {
589                tracing::warn!(
590                    "expected an AddMutation or UpdateMutation on Startup, instead got {:?}",
591                    self
592                );
593                false
594            }
595        }
596    }
597
598    /// Whether this barrier is for resume.
599    pub fn is_resume(&self) -> bool {
600        matches!(self.mutation.as_deref(), Some(Mutation::Resume))
601    }
602
603    /// Returns the [`MergeUpdate`] if this barrier is to update the merge executors for the actor
604    /// with `actor_id`.
605    pub fn as_update_merge(
606        &self,
607        actor_id: ActorId,
608        upstream_fragment_id: UpstreamFragmentId,
609    ) -> Option<&MergeUpdate> {
610        self.mutation
611            .as_deref()
612            .and_then(|mutation| match mutation {
613                Mutation::Update(UpdateMutation { merges, .. }) => {
614                    merges.get(&(actor_id, upstream_fragment_id))
615                }
616                _ => None,
617            })
618    }
619
620    /// Returns the new upstream sink information if this barrier is to add a new upstream sink for
621    /// the specified downstream fragment.
622    pub fn as_new_upstream_sink(&self, fragment_id: FragmentId) -> Option<&PbNewUpstreamSink> {
623        self.mutation
624            .as_deref()
625            .and_then(|mutation| match mutation {
626                Mutation::Add(AddMutation {
627                    new_upstream_sinks, ..
628                }) => new_upstream_sinks.get(&fragment_id),
629                _ => None,
630            })
631    }
632
633    /// Returns the dropped upstream sink-fragment if this barrier is to drop any sink.
634    pub fn as_dropped_upstream_sinks(&self) -> Option<&HashSet<FragmentId>> {
635        self.mutation
636            .as_deref()
637            .and_then(|mutation| match mutation {
638                Mutation::Stop(StopMutation {
639                    dropped_sink_fragments,
640                    ..
641                }) => Some(dropped_sink_fragments),
642                _ => None,
643            })
644    }
645
646    /// Returns the new vnode bitmap if this barrier is to update the vnode bitmap for the actor
647    /// with `actor_id`.
648    ///
649    /// Actually, this vnode bitmap update is only useful for the record accessing validation for
650    /// distributed executors, since the read/write pattern will never be across multiple vnodes.
651    pub fn as_update_vnode_bitmap(&self, actor_id: ActorId) -> Option<Arc<Bitmap>> {
652        self.mutation
653            .as_deref()
654            .and_then(|mutation| match mutation {
655                Mutation::Update(UpdateMutation { vnode_bitmaps, .. }) => {
656                    vnode_bitmaps.get(&actor_id).cloned()
657                }
658                _ => None,
659            })
660    }
661
662    pub fn assume_no_update_vnode_bitmap(&self, actor_id: ActorId) -> StreamExecutorResult<()> {
663        if self.as_update_vnode_bitmap(actor_id).is_some() {
664            return Err(anyhow!("updating vnode bitmap in place is not supported").into());
665        }
666        Ok(())
667    }
668
669    pub fn as_sink_schema_change(&self, sink_id: SinkId) -> Option<PbSinkSchemaChange> {
670        self.mutation
671            .as_deref()
672            .and_then(|mutation| match mutation {
673                Mutation::Update(UpdateMutation {
674                    sink_schema_change, ..
675                }) => sink_schema_change.get(&sink_id).cloned(),
676                _ => None,
677            })
678    }
679
680    pub fn should_flush_sink_log_store(&self, sink_id: SinkId) -> bool {
681        self.mutation
682            .as_deref()
683            .is_some_and(|mutation| match mutation {
684                Mutation::Add(AddMutation {
685                    sink_log_store_flush,
686                    ..
687                }) => sink_log_store_flush.contains(&sink_id),
688                _ => false,
689            })
690    }
691
692    pub fn as_subscriptions_to_drop(&self) -> Option<&[SubscriptionUpstreamInfo]> {
693        match self.mutation.as_deref() {
694            Some(Mutation::DropSubscriptions {
695                subscriptions_to_drop,
696            })
697            | Some(Mutation::Update(UpdateMutation {
698                subscriptions_to_drop,
699                ..
700            })) => Some(subscriptions_to_drop.as_slice()),
701            _ => None,
702        }
703    }
704
705    pub fn get_curr_epoch(&self) -> Epoch {
706        Epoch(self.epoch.curr)
707    }
708
709    /// Retrieve the tracing context for the **current** epoch of this barrier.
710    pub fn tracing_context(&self) -> &TracingContext {
711        &self.tracing_context
712    }
713
714    pub fn added_subscriber_on_mv_table(
715        &self,
716        mv_table_id: TableId,
717    ) -> impl Iterator<Item = SubscriberId> + '_ {
718        if let Some(Mutation::Add(add)) = self.mutation.as_deref() {
719            Some(add)
720        } else {
721            None
722        }
723        .into_iter()
724        .flat_map(move |add| {
725            add.subscriptions_to_add.iter().filter_map(
726                move |(upstream_mv_table_id, subscriber_id)| {
727                    if *upstream_mv_table_id == mv_table_id {
728                        Some(*subscriber_id)
729                    } else {
730                        None
731                    }
732                },
733            )
734        })
735    }
736}
737
738impl<M: PartialEq> PartialEq for BarrierInner<M> {
739    fn eq(&self, other: &Self) -> bool {
740        self.epoch == other.epoch && self.mutation == other.mutation
741    }
742}
743
744impl Mutation {
745    /// Get all actors to be stopped (dropped) by this mutation.
746    pub fn all_stop_actors(&self) -> Option<&HashSet<ActorId>> {
747        match self {
748            Mutation::Stop(StopMutation { dropped_actors, .. })
749            | Mutation::Update(UpdateMutation { dropped_actors, .. })
750            | Mutation::Add(AddMutation { dropped_actors, .. }) => Some(dropped_actors),
751            _ => None,
752        }
753    }
754
755    /// Return true if the mutation stops the given actor.
756    pub fn is_stop(&self, actor_id: ActorId) -> bool {
757        self.all_stop_actors()
758            .is_some_and(|actors| actors.contains(&actor_id))
759    }
760
761    /// Return true if the mutation is stop.
762    ///
763    /// Note that this does not mean we will stop the current actor.
764    #[cfg(test)]
765    pub fn is_stop_mutation(&self) -> bool {
766        matches!(self, Mutation::Stop(_))
767    }
768
769    #[cfg(test)]
770    fn to_protobuf(&self) -> PbMutation {
771        use risingwave_pb::source::{
772            ConnectorSplit, ConnectorSplits, PbCdcTableSnapshotSplitsWithGeneration,
773        };
774        use risingwave_pb::stream_plan::connector_props_change_mutation::ConnectorPropsInfo;
775        use risingwave_pb::stream_plan::{
776            PbAddMutation, PbConnectorPropsChangeMutation, PbDispatchers,
777            PbDropSubscriptionsMutation, PbPauseMutation, PbResumeMutation,
778            PbSourceChangeSplitMutation, PbStartFragmentBackfillMutation, PbStopMutation,
779            PbThrottleMutation, PbUpdateMutation,
780        };
781        let actor_splits_to_protobuf = |actor_splits: &SplitAssignments| {
782            actor_splits
783                .iter()
784                .map(|(&actor_id, splits)| {
785                    (
786                        actor_id,
787                        ConnectorSplits {
788                            splits: splits.clone().iter().map(ConnectorSplit::from).collect(),
789                        },
790                    )
791                })
792                .collect::<HashMap<_, _>>()
793        };
794
795        match self {
796            Mutation::Stop(StopMutation {
797                dropped_actors,
798                dropped_sink_fragments,
799            }) => PbMutation::Stop(PbStopMutation {
800                actors: dropped_actors.iter().copied().collect(),
801                dropped_sink_fragments: dropped_sink_fragments.iter().copied().collect(),
802            }),
803            Mutation::Update(UpdateMutation {
804                dispatchers,
805                merges,
806                vnode_bitmaps,
807                dropped_actors,
808                actor_splits,
809                actor_new_dispatchers,
810                actor_cdc_table_snapshot_splits,
811                sink_schema_change,
812                subscriptions_to_drop,
813            }) => PbMutation::Update(PbUpdateMutation {
814                dispatcher_update: dispatchers.values().flatten().cloned().collect(),
815                merge_update: merges.values().cloned().collect(),
816                actor_vnode_bitmap_update: vnode_bitmaps
817                    .iter()
818                    .map(|(&actor_id, bitmap)| (actor_id, bitmap.to_protobuf()))
819                    .collect(),
820                dropped_actors: dropped_actors.iter().copied().collect(),
821                actor_splits: actor_splits_to_protobuf(actor_splits),
822                actor_new_dispatchers: actor_new_dispatchers
823                    .iter()
824                    .map(|(&actor_id, dispatchers)| {
825                        (
826                            actor_id,
827                            PbDispatchers {
828                                dispatchers: dispatchers.clone(),
829                            },
830                        )
831                    })
832                    .collect(),
833                actor_cdc_table_snapshot_splits: Some(PbCdcTableSnapshotSplitsWithGeneration {
834                    splits:actor_cdc_table_snapshot_splits.splits.iter().map(|(actor_id,(splits, generation))| {
835                        (*actor_id, risingwave_pb::source::PbCdcTableSnapshotSplits {
836                            splits: splits.iter().map(risingwave_connector::source::cdc::build_cdc_table_snapshot_split).collect(),
837                            generation: *generation,
838                        })
839                    }).collect()
840                }),
841                sink_schema_change: sink_schema_change
842                    .iter()
843                    .map(|(sink_id, change)| ((*sink_id).as_raw_id(), change.clone()))
844                    .collect(),
845                subscriptions_to_drop: subscriptions_to_drop.clone(),
846            }),
847            Mutation::Add(AddMutation {
848                adds,
849                added_actors,
850                dropped_actors,
851                splits,
852                pause,
853                subscriptions_to_add,
854                backfill_nodes_to_pause,
855                actor_cdc_table_snapshot_splits,
856                new_upstream_sinks,
857                sink_log_store_flush,
858            }) => PbMutation::Add(PbAddMutation {
859                actor_dispatchers: adds
860                    .iter()
861                    .map(|(&actor_id, dispatchers)| {
862                        (
863                            actor_id,
864                            PbDispatchers {
865                                dispatchers: dispatchers.clone(),
866                            },
867                        )
868                    })
869                    .collect(),
870                added_actors: added_actors.iter().copied().collect(),
871                actor_splits: actor_splits_to_protobuf(splits),
872                pause: *pause,
873                subscriptions_to_add: subscriptions_to_add
874                    .iter()
875                    .map(|(table_id, subscriber_id)| SubscriptionUpstreamInfo {
876                        subscriber_id: *subscriber_id,
877                        upstream_mv_table_id: *table_id,
878                    })
879                    .collect(),
880                backfill_nodes_to_pause: backfill_nodes_to_pause.iter().copied().collect(),
881                actor_cdc_table_snapshot_splits:
882                Some(PbCdcTableSnapshotSplitsWithGeneration {
883                    splits:actor_cdc_table_snapshot_splits.splits.iter().map(|(actor_id,(splits, generation))| {
884                        (*actor_id, risingwave_pb::source::PbCdcTableSnapshotSplits {
885                            splits: splits.iter().map(risingwave_connector::source::cdc::build_cdc_table_snapshot_split).collect(),
886                            generation: *generation,
887                        })
888                    }).collect()
889                }),
890                new_upstream_sinks: new_upstream_sinks
891                    .iter()
892                    .map(|(k, v)| (*k, v.clone()))
893                    .collect(),
894                dropped_actors: dropped_actors.iter().copied().collect(),
895                sink_log_store_flush: sink_log_store_flush.iter().copied().collect(),
896            }),
897            Mutation::SourceChangeSplit(changes) => {
898                PbMutation::Splits(PbSourceChangeSplitMutation {
899                    actor_splits: changes
900                        .iter()
901                        .map(|(&actor_id, splits)| {
902                            (
903                                actor_id,
904                                ConnectorSplits {
905                                    splits: splits
906                                        .clone()
907                                        .iter()
908                                        .map(ConnectorSplit::from)
909                                        .collect(),
910                                },
911                            )
912                        })
913                        .collect(),
914                })
915            }
916            Mutation::Pause => PbMutation::Pause(PbPauseMutation {}),
917            Mutation::Resume => PbMutation::Resume(PbResumeMutation {}),
918            Mutation::Throttle (changes) => PbMutation::Throttle(PbThrottleMutation {
919                fragment_throttle: changes.clone(),
920            }),
921            Mutation::DropSubscriptions {
922                subscriptions_to_drop,
923            } => PbMutation::DropSubscriptions(PbDropSubscriptionsMutation {
924                info: subscriptions_to_drop.clone(),
925            }),
926            Mutation::ConnectorPropsChange(map) => {
927                PbMutation::ConnectorPropsChange(PbConnectorPropsChangeMutation {
928                    connector_props_infos: map
929                        .iter()
930                        .map(|(actor_id, options)| {
931                            (
932                                *actor_id,
933                                ConnectorPropsInfo {
934                                    connector_props_info: options
935                                        .iter()
936                                        .map(|(k, v)| (k.clone(), v.clone()))
937                                        .collect(),
938                                },
939                            )
940                        })
941                        .collect(),
942                })
943            }
944            Mutation::StartFragmentBackfill { fragment_ids } => {
945                PbMutation::StartFragmentBackfill(PbStartFragmentBackfillMutation {
946                    fragment_ids: fragment_ids.iter().copied().collect(),
947                })
948            }
949            Mutation::RefreshStart {
950                table_id,
951                associated_source_id,
952            } => PbMutation::RefreshStart(risingwave_pb::stream_plan::RefreshStartMutation {
953                table_id: *table_id,
954                associated_source_id: *associated_source_id,
955            }),
956            Mutation::ListFinish {
957                associated_source_id,
958            } => PbMutation::ListFinish(risingwave_pb::stream_plan::ListFinishMutation {
959                associated_source_id: *associated_source_id,
960            }),
961            Mutation::LoadFinish {
962                associated_source_id,
963            } => PbMutation::LoadFinish(risingwave_pb::stream_plan::LoadFinishMutation {
964                associated_source_id: *associated_source_id,
965            }),
966            Mutation::ResetSource { source_id } => {
967                PbMutation::ResetSource(risingwave_pb::stream_plan::ResetSourceMutation {
968                    source_id: source_id.as_raw_id(),
969                })
970            }
971            Mutation::InjectSourceOffsets {
972                source_id,
973                split_offsets,
974            } => PbMutation::InjectSourceOffsets(
975                risingwave_pb::stream_plan::InjectSourceOffsetsMutation {
976                    source_id: source_id.as_raw_id(),
977                    split_offsets: split_offsets.clone(),
978                },
979            ),
980        }
981    }
982
983    fn from_protobuf(prost: &PbMutation) -> StreamExecutorResult<Self> {
984        let mutation = match prost {
985            PbMutation::Stop(stop) => Mutation::Stop(StopMutation {
986                dropped_actors: stop.actors.iter().copied().collect(),
987                dropped_sink_fragments: stop.dropped_sink_fragments.iter().copied().collect(),
988            }),
989
990            PbMutation::Update(update) => Mutation::Update(UpdateMutation {
991                dispatchers: update
992                    .dispatcher_update
993                    .iter()
994                    .map(|u| (u.actor_id, u.clone()))
995                    .into_group_map(),
996                merges: update
997                    .merge_update
998                    .iter()
999                    .map(|u| ((u.actor_id, u.upstream_fragment_id), u.clone()))
1000                    .collect(),
1001                vnode_bitmaps: update
1002                    .actor_vnode_bitmap_update
1003                    .iter()
1004                    .map(|(&actor_id, bitmap)| (actor_id, Arc::new(bitmap.into())))
1005                    .collect(),
1006                dropped_actors: update.dropped_actors.iter().copied().collect(),
1007                actor_splits: update
1008                    .actor_splits
1009                    .iter()
1010                    .map(|(&actor_id, splits)| {
1011                        (
1012                            actor_id,
1013                            splits
1014                                .splits
1015                                .iter()
1016                                .map(|split| split.try_into().unwrap())
1017                                .collect(),
1018                        )
1019                    })
1020                    .collect(),
1021                actor_new_dispatchers: update
1022                    .actor_new_dispatchers
1023                    .iter()
1024                    .map(|(&actor_id, dispatchers)| (actor_id, dispatchers.dispatchers.clone()))
1025                    .collect(),
1026                actor_cdc_table_snapshot_splits:
1027                    build_actor_cdc_table_snapshot_splits_with_generation(
1028                        update
1029                            .actor_cdc_table_snapshot_splits
1030                            .clone()
1031                            .unwrap_or_default(),
1032                    ),
1033                sink_schema_change: update
1034                    .sink_schema_change
1035                    .iter()
1036                    .map(|(sink_id, change)| (SinkId::from(*sink_id), change.clone()))
1037                    .collect(),
1038                subscriptions_to_drop: update.subscriptions_to_drop.clone(),
1039            }),
1040
1041            PbMutation::Add(add) => Mutation::Add(AddMutation {
1042                adds: add
1043                    .actor_dispatchers
1044                    .iter()
1045                    .map(|(&actor_id, dispatchers)| (actor_id, dispatchers.dispatchers.clone()))
1046                    .collect(),
1047                added_actors: add.added_actors.iter().copied().collect(),
1048                dropped_actors: add.dropped_actors.iter().copied().collect(),
1049                // TODO: remove this and use `SourceChangesSplit` after we support multiple
1050                // mutations.
1051                splits: add
1052                    .actor_splits
1053                    .iter()
1054                    .map(|(&actor_id, splits)| {
1055                        (
1056                            actor_id,
1057                            splits
1058                                .splits
1059                                .iter()
1060                                .map(|split| split.try_into().unwrap())
1061                                .collect(),
1062                        )
1063                    })
1064                    .collect(),
1065                pause: add.pause,
1066                subscriptions_to_add: add
1067                    .subscriptions_to_add
1068                    .iter()
1069                    .map(
1070                        |SubscriptionUpstreamInfo {
1071                             subscriber_id,
1072                             upstream_mv_table_id,
1073                         }| { (*upstream_mv_table_id, *subscriber_id) },
1074                    )
1075                    .collect(),
1076                backfill_nodes_to_pause: add.backfill_nodes_to_pause.iter().copied().collect(),
1077                actor_cdc_table_snapshot_splits:
1078                    build_actor_cdc_table_snapshot_splits_with_generation(
1079                        add.actor_cdc_table_snapshot_splits
1080                            .clone()
1081                            .unwrap_or_default(),
1082                    ),
1083                new_upstream_sinks: add
1084                    .new_upstream_sinks
1085                    .iter()
1086                    .map(|(k, v)| (*k, v.clone()))
1087                    .collect(),
1088                sink_log_store_flush: add.sink_log_store_flush.iter().copied().collect(),
1089            }),
1090
1091            PbMutation::Splits(s) => {
1092                let mut change_splits: Vec<(ActorId, Vec<SplitImpl>)> =
1093                    Vec::with_capacity(s.actor_splits.len());
1094                for (&actor_id, splits) in &s.actor_splits {
1095                    if !splits.splits.is_empty() {
1096                        change_splits.push((
1097                            actor_id,
1098                            splits
1099                                .splits
1100                                .iter()
1101                                .map(SplitImpl::try_from)
1102                                .try_collect()?,
1103                        ));
1104                    }
1105                }
1106                Mutation::SourceChangeSplit(change_splits.into_iter().collect())
1107            }
1108            PbMutation::Pause(_) => Mutation::Pause,
1109            PbMutation::Resume(_) => Mutation::Resume,
1110            PbMutation::Throttle(changes) => Mutation::Throttle(changes.fragment_throttle.clone()),
1111            PbMutation::DropSubscriptions(drop) => Mutation::DropSubscriptions {
1112                subscriptions_to_drop: drop.info.clone(),
1113            },
1114            PbMutation::ConnectorPropsChange(alter_connector_props) => {
1115                Mutation::ConnectorPropsChange(
1116                    alter_connector_props
1117                        .connector_props_infos
1118                        .iter()
1119                        .map(|(connector_id, options)| {
1120                            (
1121                                *connector_id,
1122                                options
1123                                    .connector_props_info
1124                                    .iter()
1125                                    .map(|(k, v)| (k.clone(), v.clone()))
1126                                    .collect(),
1127                            )
1128                        })
1129                        .collect(),
1130                )
1131            }
1132            PbMutation::StartFragmentBackfill(start_fragment_backfill) => {
1133                Mutation::StartFragmentBackfill {
1134                    fragment_ids: start_fragment_backfill
1135                        .fragment_ids
1136                        .iter()
1137                        .copied()
1138                        .collect(),
1139                }
1140            }
1141            PbMutation::RefreshStart(refresh_start) => Mutation::RefreshStart {
1142                table_id: refresh_start.table_id,
1143                associated_source_id: refresh_start.associated_source_id,
1144            },
1145            PbMutation::ListFinish(list_finish) => Mutation::ListFinish {
1146                associated_source_id: list_finish.associated_source_id,
1147            },
1148            PbMutation::LoadFinish(load_finish) => Mutation::LoadFinish {
1149                associated_source_id: load_finish.associated_source_id,
1150            },
1151            PbMutation::ResetSource(reset_source) => Mutation::ResetSource {
1152                source_id: SourceId::from(reset_source.source_id),
1153            },
1154            PbMutation::InjectSourceOffsets(inject) => Mutation::InjectSourceOffsets {
1155                source_id: SourceId::from(inject.source_id),
1156                split_offsets: inject.split_offsets.clone(),
1157            },
1158        };
1159        Ok(mutation)
1160    }
1161}
1162
1163impl<M> BarrierInner<M> {
1164    fn to_protobuf_inner(&self, barrier_fn: impl FnOnce(&M) -> Option<PbMutation>) -> PbBarrier {
1165        let Self {
1166            epoch,
1167            mutation,
1168            kind,
1169            tracing_context,
1170            ..
1171        } = self;
1172
1173        PbBarrier {
1174            epoch: Some(PbEpoch {
1175                curr: epoch.curr,
1176                prev: epoch.prev,
1177            }),
1178            mutation: barrier_fn(mutation).map(|mutation| PbBarrierMutation {
1179                mutation: Some(mutation),
1180            }),
1181            tracing_context: tracing_context.to_protobuf(),
1182            kind: *kind as _,
1183        }
1184    }
1185
1186    fn from_protobuf_inner(
1187        prost: &PbBarrier,
1188        mutation_from_pb: impl FnOnce(Option<&PbMutation>) -> StreamExecutorResult<M>,
1189    ) -> StreamExecutorResult<Self> {
1190        let epoch = prost.get_epoch()?;
1191
1192        Ok(Self {
1193            kind: prost.kind(),
1194            epoch: EpochPair::new(epoch.curr, epoch.prev),
1195            mutation: mutation_from_pb(
1196                (prost.mutation.as_ref()).and_then(|mutation| mutation.mutation.as_ref()),
1197            )?,
1198            tracing_context: TracingContext::from_protobuf(&prost.tracing_context),
1199        })
1200    }
1201
1202    pub fn map_mutation<M2>(self, f: impl FnOnce(M) -> M2) -> BarrierInner<M2> {
1203        BarrierInner {
1204            epoch: self.epoch,
1205            mutation: f(self.mutation),
1206            kind: self.kind,
1207            tracing_context: self.tracing_context,
1208        }
1209    }
1210}
1211
1212impl DispatcherBarrier {
1213    pub fn to_protobuf(&self) -> PbBarrier {
1214        self.to_protobuf_inner(|_| None)
1215    }
1216}
1217
1218impl Barrier {
1219    #[cfg(test)]
1220    pub fn to_protobuf(&self) -> PbBarrier {
1221        self.to_protobuf_inner(|mutation| mutation.as_ref().map(|mutation| mutation.to_protobuf()))
1222    }
1223
1224    pub fn from_protobuf(prost: &PbBarrier) -> StreamExecutorResult<Self> {
1225        Self::from_protobuf_inner(prost, |mutation| {
1226            mutation
1227                .map(|m| Mutation::from_protobuf(m).map(Arc::new))
1228                .transpose()
1229        })
1230    }
1231}
1232
1233#[derive(Debug, PartialEq, Eq, Clone, EstimateSize)]
1234pub struct Watermark {
1235    pub col_idx: usize,
1236    #[estimate_size(ignore)]
1237    pub data_type: DataType,
1238    pub val: ScalarImpl,
1239}
1240
1241impl PartialOrd for Watermark {
1242    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1243        Some(self.cmp(other))
1244    }
1245}
1246
1247impl Ord for Watermark {
1248    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1249        self.val.default_cmp(&other.val)
1250    }
1251}
1252
1253impl Watermark {
1254    pub fn new(col_idx: usize, data_type: DataType, val: ScalarImpl) -> Self {
1255        Self {
1256            col_idx,
1257            data_type,
1258            val,
1259        }
1260    }
1261
1262    pub async fn transform_with_expr(
1263        self,
1264        expr: &NonStrictExpression,
1265        new_col_idx: usize,
1266    ) -> Option<Self> {
1267        let Self { col_idx, val, .. } = self;
1268        let row = {
1269            let mut row = vec![None; col_idx + 1];
1270            row[col_idx] = Some(val);
1271            OwnedRow::new(row)
1272        };
1273        let val = expr.eval_row_infallible(&row).await?;
1274        Some(Self::new(new_col_idx, expr.inner().return_type(), val))
1275    }
1276
1277    /// Transform the watermark with the given output indices. If this watermark is not in the
1278    /// output, return `None`.
1279    pub fn transform_with_indices(self, output_indices: &[usize]) -> Option<Self> {
1280        output_indices
1281            .iter()
1282            .position(|p| *p == self.col_idx)
1283            .map(|new_col_idx| self.with_idx(new_col_idx))
1284    }
1285
1286    pub fn to_protobuf(&self) -> PbWatermark {
1287        PbWatermark {
1288            column: Some(PbInputRef {
1289                index: self.col_idx as _,
1290                r#type: Some(self.data_type.to_protobuf()),
1291            }),
1292            val: Some(&self.val).to_protobuf().into(),
1293        }
1294    }
1295
1296    pub fn from_protobuf(prost: &PbWatermark) -> StreamExecutorResult<Self> {
1297        let col_ref = prost.get_column()?;
1298        let data_type = DataType::from(col_ref.get_type()?);
1299        let val = Datum::from_protobuf(prost.get_val()?, &data_type)?
1300            .expect("watermark value cannot be null");
1301        Ok(Self::new(col_ref.get_index() as _, data_type, val))
1302    }
1303
1304    pub fn with_idx(self, idx: usize) -> Self {
1305        Self::new(idx, self.data_type, self.val)
1306    }
1307}
1308
1309#[cfg_attr(any(test, feature = "test"), derive(PartialEq))]
1310#[derive(Debug, EnumAsInner, Clone)]
1311pub enum MessageInner<M> {
1312    Chunk(StreamChunk),
1313    Barrier(BarrierInner<M>),
1314    Watermark(Watermark),
1315}
1316
1317impl<M> MessageInner<M> {
1318    pub fn map_mutation<M2>(self, f: impl FnOnce(M) -> M2) -> MessageInner<M2> {
1319        match self {
1320            MessageInner::Chunk(chunk) => MessageInner::Chunk(chunk),
1321            MessageInner::Barrier(barrier) => MessageInner::Barrier(barrier.map_mutation(f)),
1322            MessageInner::Watermark(watermark) => MessageInner::Watermark(watermark),
1323        }
1324    }
1325}
1326
1327pub type Message = MessageInner<BarrierMutationType>;
1328pub type DispatcherMessage = MessageInner<()>;
1329
1330/// `MessageBatchInner` is used exclusively by `Dispatcher` and the `Merger`/`Receiver` for exchanging messages between them.
1331/// It shares the same message type as the fundamental `MessageInner`, but batches multiple barriers into a single message.
1332#[derive(Debug, EnumAsInner, Clone)]
1333pub enum MessageBatchInner<M> {
1334    Chunk(StreamChunk),
1335    BarrierBatch(Vec<BarrierInner<M>>),
1336    Watermark(Watermark),
1337}
1338pub type MessageBatch = MessageBatchInner<BarrierMutationType>;
1339pub type DispatcherBarriers = Vec<DispatcherBarrier>;
1340pub type DispatcherMessageBatch = MessageBatchInner<()>;
1341
1342impl<M> From<MessageInner<M>> for MessageBatchInner<M> {
1343    fn from(m: MessageInner<M>) -> Self {
1344        match m {
1345            MessageInner::Chunk(c) => Self::Chunk(c),
1346            MessageInner::Barrier(b) => Self::BarrierBatch(vec![b]),
1347            MessageInner::Watermark(w) => Self::Watermark(w),
1348        }
1349    }
1350}
1351
1352impl From<StreamChunk> for Message {
1353    fn from(chunk: StreamChunk) -> Self {
1354        Message::Chunk(chunk)
1355    }
1356}
1357
1358impl<'a> TryFrom<&'a Message> for &'a Barrier {
1359    type Error = ();
1360
1361    fn try_from(m: &'a Message) -> std::result::Result<Self, Self::Error> {
1362        match m {
1363            Message::Chunk(_) => Err(()),
1364            Message::Barrier(b) => Ok(b),
1365            Message::Watermark(_) => Err(()),
1366        }
1367    }
1368}
1369
1370impl Message {
1371    /// Return true if the message is a stop barrier, meaning the stream
1372    /// will not continue, false otherwise.
1373    ///
1374    /// Note that this does not mean we will stop the current actor.
1375    #[cfg(test)]
1376    pub fn is_stop(&self) -> bool {
1377        matches!(
1378            self,
1379            Message::Barrier(Barrier {
1380                mutation,
1381                ..
1382            }) if mutation.as_ref().unwrap().is_stop_mutation()
1383        )
1384    }
1385}
1386
1387impl DispatcherMessageBatch {
1388    pub fn to_protobuf(&self) -> PbStreamMessageBatch {
1389        let prost = match self {
1390            Self::Chunk(stream_chunk) => {
1391                let prost_stream_chunk = stream_chunk.to_protobuf();
1392                StreamMessageBatch::StreamChunk(prost_stream_chunk)
1393            }
1394            Self::BarrierBatch(barrier_batch) => StreamMessageBatch::BarrierBatch(BarrierBatch {
1395                barriers: barrier_batch.iter().map(|b| b.to_protobuf()).collect(),
1396            }),
1397            Self::Watermark(watermark) => StreamMessageBatch::Watermark(watermark.to_protobuf()),
1398        };
1399        PbStreamMessageBatch {
1400            stream_message_batch: Some(prost),
1401        }
1402    }
1403
1404    pub fn from_protobuf(prost: &PbStreamMessageBatch) -> StreamExecutorResult<Self> {
1405        let res = match prost.get_stream_message_batch()? {
1406            StreamMessageBatch::StreamChunk(chunk) => {
1407                Self::Chunk(StreamChunk::from_protobuf(chunk)?)
1408            }
1409            StreamMessageBatch::BarrierBatch(barrier_batch) => {
1410                let barriers = barrier_batch
1411                    .barriers
1412                    .iter()
1413                    .map(|barrier| {
1414                        DispatcherBarrier::from_protobuf_inner(barrier, |mutation| {
1415                            if mutation.is_some() {
1416                                if cfg!(debug_assertions) {
1417                                    panic!("should not receive message of barrier with mutation");
1418                                } else {
1419                                    warn!(?barrier, "receive message of barrier with mutation");
1420                                }
1421                            }
1422                            Ok(())
1423                        })
1424                    })
1425                    .try_collect()?;
1426                Self::BarrierBatch(barriers)
1427            }
1428            StreamMessageBatch::Watermark(watermark) => {
1429                Self::Watermark(Watermark::from_protobuf(watermark)?)
1430            }
1431        };
1432        Ok(res)
1433    }
1434
1435    pub fn get_encoded_len(msg: &impl ::prost::Message) -> usize {
1436        ::prost::Message::encoded_len(msg)
1437    }
1438}
1439
1440pub type StreamKey = Vec<usize>;
1441pub type StreamKeyRef<'a> = &'a [usize];
1442pub type StreamKeyDataTypes = SmallVec<[DataType; 1]>;
1443
1444/// Expect the first message of the given `stream` as a barrier.
1445pub async fn expect_first_barrier<M: Debug>(
1446    stream: &mut (impl MessageStreamInner<M> + Unpin),
1447) -> StreamExecutorResult<BarrierInner<M>> {
1448    let message = stream
1449        .next()
1450        .instrument_await("expect_first_barrier")
1451        .await
1452        .context("failed to extract the first message: stream closed unexpectedly")??;
1453    let barrier = message
1454        .into_barrier()
1455        .expect("the first message must be a barrier");
1456    // TODO: Is this check correct?
1457    assert!(matches!(
1458        barrier.kind,
1459        BarrierKind::Checkpoint | BarrierKind::Initial
1460    ));
1461    Ok(barrier)
1462}
1463
1464/// Expect the first message of the given `stream` as a barrier.
1465pub async fn expect_first_barrier_from_aligned_stream(
1466    stream: &mut (impl AlignedMessageStream + Unpin),
1467) -> StreamExecutorResult<Barrier> {
1468    let message = stream
1469        .next()
1470        .instrument_await("expect_first_barrier")
1471        .await
1472        .context("failed to extract the first message: stream closed unexpectedly")??;
1473    let barrier = message
1474        .into_barrier()
1475        .expect("the first message must be a barrier");
1476    Ok(barrier)
1477}
1478
1479/// `StreamConsumer` is the last step in an actor.
1480pub trait StreamConsumer: Send + 'static {
1481    type BarrierStream: Stream<Item = StreamResult<Barrier>> + Send;
1482
1483    fn execute(self: Box<Self>) -> Self::BarrierStream;
1484}
1485
1486type BoxedMessageInput<InputId, M> = BoxedInput<InputId, MessageStreamItemInner<M>>;
1487
1488/// A stream for merging messages from multiple upstreams.
1489/// Can dynamically add and delete upstream streams.
1490/// For the meaning of the generic parameter `M` used, refer to `BarrierInner<M>`.
1491pub struct DynamicReceivers<InputId, M> {
1492    /// The barrier we're aligning to. If this is `None`, then `blocked_upstreams` is empty.
1493    barrier: Option<BarrierInner<M>>,
1494    /// The start timestamp of the current barrier. Used for measuring the alignment duration.
1495    start_ts: Option<Instant>,
1496    /// The upstreams that're blocked by the `barrier`.
1497    blocked: Vec<BoxedMessageInput<InputId, M>>,
1498    /// The upstreams that're not blocked and can be polled.
1499    active: FuturesUnordered<StreamFuture<BoxedMessageInput<InputId, M>>>,
1500    /// watermark column index -> `BufferedWatermarks`
1501    buffered_watermarks: BTreeMap<usize, BufferedWatermarks<InputId>>,
1502    /// Currently only used for union.
1503    barrier_align_duration: Option<LabelGuardedMetric<GenericCounter<AtomicU64>>>,
1504    /// Only for merge. If None, then we don't take `Instant::now()` and `observe` during `poll_next`
1505    merge_barrier_align_duration: Option<LabelGuardedMetric<GenericCounter<AtomicU64>>>,
1506}
1507
1508impl<InputId: Clone + Ord + Hash + std::fmt::Debug + Unpin, M: Clone + Unpin> Stream
1509    for DynamicReceivers<InputId, M>
1510{
1511    type Item = MessageStreamItemInner<M>;
1512
1513    fn poll_next(
1514        mut self: Pin<&mut Self>,
1515        cx: &mut std::task::Context<'_>,
1516    ) -> Poll<Option<Self::Item>> {
1517        if self.is_empty() {
1518            return Poll::Ready(None);
1519        }
1520
1521        loop {
1522            match futures::ready!(self.active.poll_next_unpin(cx)) {
1523                // Directly forward the error.
1524                Some((Some(Err(e)), _)) => {
1525                    return Poll::Ready(Some(Err(e)));
1526                }
1527                // Handle the message from some upstream.
1528                Some((Some(Ok(message)), remaining)) => {
1529                    let input_id = remaining.id();
1530                    match message {
1531                        MessageInner::Chunk(chunk) => {
1532                            // Continue polling this upstream by pushing it back to `active`.
1533                            self.active.push(remaining.into_future());
1534                            return Poll::Ready(Some(Ok(MessageInner::Chunk(chunk))));
1535                        }
1536                        MessageInner::Watermark(watermark) => {
1537                            // Continue polling this upstream by pushing it back to `active`.
1538                            self.active.push(remaining.into_future());
1539                            if let Some(watermark) = self.handle_watermark(input_id, watermark) {
1540                                return Poll::Ready(Some(Ok(MessageInner::Watermark(watermark))));
1541                            }
1542                        }
1543                        MessageInner::Barrier(barrier) => {
1544                            // Block this upstream by pushing it to `blocked`.
1545                            if self.blocked.is_empty() {
1546                                self.start_ts = Some(Instant::now());
1547                            }
1548                            self.blocked.push(remaining);
1549                            if let Some(current_barrier) = self.barrier.as_ref() {
1550                                if current_barrier.epoch != barrier.epoch {
1551                                    return Poll::Ready(Some(Err(
1552                                        StreamExecutorError::align_barrier(
1553                                            current_barrier.clone().map_mutation(|_| None),
1554                                            barrier.map_mutation(|_| None),
1555                                        ),
1556                                    )));
1557                                }
1558                            } else {
1559                                self.barrier = Some(barrier);
1560                            }
1561                        }
1562                    }
1563                }
1564                // We use barrier as the control message of the stream. That is, we always stop the
1565                // actors actively when we receive a `Stop` mutation, instead of relying on the stream
1566                // termination.
1567                //
1568                // Besides, in abnormal cases when the other side of the `Input` closes unexpectedly,
1569                // we also yield an `Err(ExchangeChannelClosed)`, which will hit the `Err` arm above.
1570                // So this branch will never be reached in all cases.
1571                Some((None, remaining)) => {
1572                    return Poll::Ready(Some(Err(StreamExecutorError::channel_closed(format!(
1573                        "upstream input {:?} unexpectedly closed",
1574                        remaining.id()
1575                    )))));
1576                }
1577                // There's no active upstreams. Process the barrier and resume the blocked ones.
1578                None => {
1579                    assert!(!self.blocked.is_empty());
1580
1581                    let start_ts = self
1582                        .start_ts
1583                        .take()
1584                        .expect("should have received at least one barrier");
1585                    if let Some(barrier_align_duration) = &self.barrier_align_duration {
1586                        barrier_align_duration.inc_by(start_ts.elapsed().as_nanos() as u64);
1587                    }
1588                    if let Some(merge_barrier_align_duration) = &self.merge_barrier_align_duration {
1589                        merge_barrier_align_duration.inc_by(start_ts.elapsed().as_nanos() as u64);
1590                    }
1591
1592                    break;
1593                }
1594            }
1595        }
1596
1597        assert!(self.active.is_terminated());
1598
1599        let barrier = self.barrier.take().unwrap();
1600
1601        let upstreams = std::mem::take(&mut self.blocked);
1602        self.extend_active(upstreams);
1603        assert!(!self.active.is_terminated());
1604
1605        Poll::Ready(Some(Ok(MessageInner::Barrier(barrier))))
1606    }
1607}
1608
1609impl<InputId: Clone + Ord + Hash + std::fmt::Debug, M> DynamicReceivers<InputId, M> {
1610    pub fn new(
1611        upstreams: Vec<BoxedMessageInput<InputId, M>>,
1612        barrier_align_duration: Option<LabelGuardedMetric<GenericCounter<AtomicU64>>>,
1613        merge_barrier_align_duration: Option<LabelGuardedMetric<GenericCounter<AtomicU64>>>,
1614    ) -> Self {
1615        let mut this = Self {
1616            barrier: None,
1617            start_ts: None,
1618            blocked: Vec::with_capacity(upstreams.len()),
1619            active: Default::default(),
1620            buffered_watermarks: Default::default(),
1621            merge_barrier_align_duration,
1622            barrier_align_duration,
1623        };
1624        this.extend_active(upstreams);
1625        this
1626    }
1627
1628    /// Extend the active upstreams with the given upstreams. The current stream must be at the
1629    /// clean state right after a barrier.
1630    pub fn extend_active(
1631        &mut self,
1632        upstreams: impl IntoIterator<Item = BoxedMessageInput<InputId, M>>,
1633    ) {
1634        assert!(self.blocked.is_empty() && self.barrier.is_none());
1635
1636        self.active
1637            .extend(upstreams.into_iter().map(|s| s.into_future()));
1638    }
1639
1640    /// Handle a new watermark message. Optionally returns the watermark message to emit.
1641    pub fn handle_watermark(
1642        &mut self,
1643        input_id: InputId,
1644        watermark: Watermark,
1645    ) -> Option<Watermark> {
1646        let col_idx = watermark.col_idx;
1647        // Insert a buffer watermarks when first received from a column.
1648        let upstream_ids: Vec<_> = self.upstream_input_ids().collect();
1649        let watermarks = self
1650            .buffered_watermarks
1651            .entry(col_idx)
1652            .or_insert_with(|| BufferedWatermarks::with_ids(upstream_ids));
1653        watermarks.handle_watermark(input_id, watermark)
1654    }
1655
1656    /// Consume `other` and add its upstreams to `self`. The two streams must be at the clean state
1657    /// right after a barrier.
1658    pub fn add_upstreams_from(
1659        &mut self,
1660        new_inputs: impl IntoIterator<Item = BoxedMessageInput<InputId, M>>,
1661    ) {
1662        assert!(self.blocked.is_empty() && self.barrier.is_none());
1663
1664        let new_inputs: Vec<_> = new_inputs.into_iter().collect();
1665        let input_ids = new_inputs.iter().map(|input| input.id());
1666        self.buffered_watermarks.values_mut().for_each(|buffers| {
1667            // Add buffers to the buffered watermarks for all cols
1668            buffers.add_buffers(input_ids.clone());
1669        });
1670        self.active
1671            .extend(new_inputs.into_iter().map(|s| s.into_future()));
1672    }
1673
1674    /// Remove upstreams from `self` in `upstream_input_ids`. The current stream must be at the
1675    /// clean state right after a barrier.
1676    /// The current container does not necessarily contain all the input ids passed in.
1677    pub fn remove_upstreams(&mut self, upstream_input_ids: &HashSet<InputId>) {
1678        assert!(self.blocked.is_empty() && self.barrier.is_none());
1679
1680        let new_upstreams = std::mem::take(&mut self.active)
1681            .into_iter()
1682            .map(|s| s.into_inner().unwrap())
1683            .filter(|u| !upstream_input_ids.contains(&u.id()));
1684        self.extend_active(new_upstreams);
1685        self.buffered_watermarks.values_mut().for_each(|buffers| {
1686            // Call `check_heap` in case the only upstream(s) that does not have
1687            // watermark in heap is removed
1688            buffers.remove_buffer(upstream_input_ids.clone());
1689        });
1690    }
1691
1692    pub fn merge_barrier_align_duration(
1693        &self,
1694    ) -> Option<LabelGuardedMetric<GenericCounter<AtomicU64>>> {
1695        self.merge_barrier_align_duration.clone()
1696    }
1697
1698    pub fn flush_buffered_watermarks(&mut self) {
1699        self.buffered_watermarks
1700            .values_mut()
1701            .for_each(|buffers| buffers.clear());
1702    }
1703
1704    pub fn upstream_input_ids(&self) -> impl Iterator<Item = InputId> + '_ {
1705        self.blocked
1706            .iter()
1707            .map(|s| s.id())
1708            .chain(self.active.iter().map(|s| s.get_ref().unwrap().id()))
1709    }
1710
1711    pub fn is_empty(&self) -> bool {
1712        self.blocked.is_empty() && self.active.is_empty()
1713    }
1714}
1715
1716// Explanation of why we need `DispatchBarrierBuffer`:
1717//
1718// When we need to create or replace an upstream fragment for the current fragment, the `Merge` operator must
1719// add some new upstream actor inputs. However, the `Merge` operator may still have old upstreams. We must wait
1720// for these old upstreams to completely process their barriers and align before we can safely update the
1721// `upstream-input-set`.
1722//
1723// Meanwhile, the creation of a new upstream actor can only succeed after the channel to the downstream `Merge`
1724// operator has been established. This creates a potential dependency chain: [new_actor_creation ->
1725// downstream_merge_update -> old_actor_processing]
1726//
1727// To address this, we split the application of a barrier's `Mutation` into two steps:
1728// 1. Parse the `Mutation`. If there is an addition on the upstream-set, establish a channel with the upstream
1729//    and cache it.
1730// 2. When the upstream barrier actually arrives, apply the cached upstream changes to the upstream-set
1731//
1732// Additionally, since receiving a barrier from current upstream input and from the `barrier_rx` are
1733// asynchronous, we cannot determine which will arrive first. Therefore, when a barrier is received from an
1734// upstream: if a cached mutation is present, we apply it. Otherwise, we must fetch a new barrier from
1735// `barrier_rx`.
1736pub(crate) struct DispatchBarrierBuffer {
1737    buffer: VecDeque<(Barrier, Option<Vec<BoxedActorInput>>)>,
1738    barrier_rx: mpsc::UnboundedReceiver<Barrier>,
1739    recv_state: BarrierReceiverState,
1740    curr_upstream_fragment_id: FragmentId,
1741    actor_id: ActorId,
1742    // read-only context for building new inputs
1743    build_input_ctx: Arc<BuildInputContext>,
1744}
1745
1746struct BuildInputContext {
1747    pub actor_id: ActorId,
1748    pub local_barrier_manager: LocalBarrierManager,
1749    pub metrics: Arc<StreamingMetrics>,
1750    pub fragment_id: FragmentId,
1751    pub actor_config: Arc<StreamingConfig>,
1752}
1753
1754type BoxedNewInputsFuture =
1755    Pin<Box<dyn Future<Output = StreamExecutorResult<Vec<BoxedActorInput>>> + Send>>;
1756
1757enum BarrierReceiverState {
1758    ReceivingBarrier,
1759    CreatingNewInput(Barrier, BoxedNewInputsFuture),
1760}
1761
1762impl DispatchBarrierBuffer {
1763    pub fn new(
1764        barrier_rx: mpsc::UnboundedReceiver<Barrier>,
1765        actor_id: ActorId,
1766        curr_upstream_fragment_id: FragmentId,
1767        local_barrier_manager: LocalBarrierManager,
1768        metrics: Arc<StreamingMetrics>,
1769        fragment_id: FragmentId,
1770        actor_config: Arc<StreamingConfig>,
1771    ) -> Self {
1772        Self {
1773            buffer: VecDeque::new(),
1774            barrier_rx,
1775            recv_state: BarrierReceiverState::ReceivingBarrier,
1776            curr_upstream_fragment_id,
1777            actor_id,
1778            build_input_ctx: Arc::new(BuildInputContext {
1779                actor_id,
1780                local_barrier_manager,
1781                metrics,
1782                fragment_id,
1783                actor_config,
1784            }),
1785        }
1786    }
1787
1788    pub async fn await_next_message(
1789        &mut self,
1790        stream: &mut (impl Stream<Item = StreamExecutorResult<DispatcherMessage>> + Unpin),
1791        metrics: &ActorInputMetrics,
1792    ) -> StreamExecutorResult<DispatcherMessage> {
1793        let mut start_time = Instant::now();
1794        let interval_duration = Duration::from_secs(15);
1795        let mut interval =
1796            tokio::time::interval_at(start_time + interval_duration, interval_duration);
1797
1798        loop {
1799            tokio::select! {
1800                biased;
1801                msg = stream.try_next() => {
1802                    metrics
1803                        .actor_input_buffer_blocking_duration_ns
1804                        .inc_by(start_time.elapsed().as_nanos() as u64);
1805                    return msg?.ok_or_else(
1806                        || StreamExecutorError::channel_closed("upstream executor closed unexpectedly")
1807                    );
1808                }
1809
1810                e = self.continuously_fetch_barrier_rx() => {
1811                    return Err(e);
1812                }
1813
1814                _ = interval.tick() => {
1815                    start_time = Instant::now();
1816                    metrics.actor_input_buffer_blocking_duration_ns.inc_by(interval_duration.as_nanos() as u64);
1817                    continue;
1818                }
1819            }
1820        }
1821    }
1822
1823    pub async fn pop_barrier_with_inputs(
1824        &mut self,
1825        barrier: DispatcherBarrier,
1826    ) -> StreamExecutorResult<(Barrier, Option<Vec<BoxedActorInput>>)> {
1827        while self.buffer.is_empty() {
1828            self.try_fetch_barrier_rx(false).await?;
1829        }
1830        let (recv_barrier, inputs) = self.buffer.pop_front().unwrap();
1831        assert_equal_dispatcher_barrier(&recv_barrier, &barrier);
1832
1833        Ok((recv_barrier, inputs))
1834    }
1835
1836    async fn continuously_fetch_barrier_rx(&mut self) -> StreamExecutorError {
1837        loop {
1838            if let Err(e) = self.try_fetch_barrier_rx(true).await {
1839                return e;
1840            }
1841        }
1842    }
1843
1844    async fn try_fetch_barrier_rx(&mut self, pending_on_end: bool) -> StreamExecutorResult<()> {
1845        match &mut self.recv_state {
1846            BarrierReceiverState::ReceivingBarrier => {
1847                let Some(barrier) = self.barrier_rx.recv().await else {
1848                    if pending_on_end {
1849                        return pending().await;
1850                    } else {
1851                        return Err(StreamExecutorError::channel_closed(
1852                            "barrier channel closed unexpectedly",
1853                        ));
1854                    }
1855                };
1856                if let Some(fut) = self.pre_apply_barrier(&barrier) {
1857                    self.recv_state = BarrierReceiverState::CreatingNewInput(barrier, fut);
1858                } else {
1859                    self.buffer.push_back((barrier, None));
1860                }
1861            }
1862            BarrierReceiverState::CreatingNewInput(barrier, fut) => {
1863                let new_inputs = fut.await?;
1864                self.buffer.push_back((barrier.clone(), Some(new_inputs)));
1865                self.recv_state = BarrierReceiverState::ReceivingBarrier;
1866            }
1867        }
1868        Ok(())
1869    }
1870
1871    fn pre_apply_barrier(&mut self, barrier: &Barrier) -> Option<BoxedNewInputsFuture> {
1872        if let Some(update) = barrier.as_update_merge(self.actor_id, self.curr_upstream_fragment_id)
1873            && !update.added_upstream_actors.is_empty()
1874        {
1875            // When update upstream fragment, added_actors will not be empty.
1876            let upstream_fragment_id =
1877                if let Some(new_upstream_fragment_id) = update.new_upstream_fragment_id {
1878                    self.curr_upstream_fragment_id = new_upstream_fragment_id;
1879                    new_upstream_fragment_id
1880                } else {
1881                    self.curr_upstream_fragment_id
1882                };
1883            let ctx = self.build_input_ctx.clone();
1884            let added_upstream_actors = update.added_upstream_actors.clone();
1885            let barrier = barrier.clone();
1886            let fut = async move {
1887                try_join_all(added_upstream_actors.iter().map(|upstream_actor| async {
1888                    let mut new_input = new_input(
1889                        &ctx.local_barrier_manager,
1890                        ctx.metrics.clone(),
1891                        ctx.actor_id,
1892                        ctx.fragment_id,
1893                        upstream_actor,
1894                        upstream_fragment_id,
1895                        ctx.actor_config.clone(),
1896                    )
1897                    .await?;
1898
1899                    // Poll the first barrier from the new upstreams. It must be the same as the one we polled from
1900                    // original upstreams.
1901                    let first_barrier = expect_first_barrier(&mut new_input).await?;
1902                    assert_equal_dispatcher_barrier(&barrier, &first_barrier);
1903
1904                    StreamExecutorResult::Ok(new_input)
1905                }))
1906                .await
1907            }
1908            .boxed();
1909
1910            Some(fut)
1911        } else {
1912            None
1913        }
1914    }
1915}