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