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