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