Skip to main content

risingwave_stream/executor/
dispatch.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
15use std::collections::{HashMap, HashSet};
16use std::iter::repeat_with;
17use std::ops::{Deref, DerefMut};
18use std::time::Duration;
19
20use anyhow::anyhow;
21use futures::FutureExt;
22use itertools::Itertools;
23use risingwave_common::array::Op;
24use risingwave_common::bitmap::BitmapBuilder;
25use risingwave_common::config::StreamingConfig;
26use risingwave_common::hash::{ActorMapping, ExpandedActorMapping, VirtualNode};
27use risingwave_common::metrics::LabelGuardedIntCounter;
28use risingwave_common::row::RowExt;
29use risingwave_common::util::iter_util::ZipEqFast;
30use risingwave_pb::stream_plan::update_mutation::PbDispatcherUpdate;
31use risingwave_pb::stream_plan::{self, PbDispatcher};
32use smallvec::{SmallVec, smallvec};
33use thiserror_ext::AsReport;
34use tokio::sync::mpsc::UnboundedReceiver;
35use tokio::task::consume_budget;
36use tokio::time::Instant;
37use tokio_stream::StreamExt;
38use tokio_stream::adapters::Peekable;
39use tracing::{Instrument, event};
40
41use super::exchange::output::Output;
42use super::{
43    AddMutation, DispatcherBarriers, DispatcherMessageBatch, MessageBatch, TroublemakerExecutor,
44    UpdateMutation,
45};
46use crate::executor::StreamConsumer;
47use crate::executor::prelude::*;
48use crate::task::{DispatcherId, NewOutputRequest};
49
50mod dispatch_sync_log_store;
51mod output_mapping;
52
53pub use dispatch_sync_log_store::SyncLogStoreDispatchExecutor;
54pub use output_mapping::DispatchOutputMapping;
55use risingwave_common::id::FragmentId;
56
57use crate::error::StreamError;
58
59/// [`DispatchExecutor`] consumes messages and send them into downstream actors. Usually,
60/// data chunks will be dispatched with some specified policy, while control message
61/// such as barriers will be distributed to all receivers.
62pub struct DispatchExecutor {
63    input: Executor,
64    inner: DispatchExecutorInner,
65}
66
67struct DispatcherWithMetrics {
68    dispatcher: DispatcherImpl,
69    pub actor_output_buffer_blocking_duration_ns: LabelGuardedIntCounter,
70}
71
72impl Debug for DispatcherWithMetrics {
73    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
74        self.dispatcher.fmt(f)
75    }
76}
77
78impl Deref for DispatcherWithMetrics {
79    type Target = DispatcherImpl;
80
81    fn deref(&self) -> &Self::Target {
82        &self.dispatcher
83    }
84}
85
86impl DerefMut for DispatcherWithMetrics {
87    fn deref_mut(&mut self) -> &mut Self::Target {
88        &mut self.dispatcher
89    }
90}
91
92struct DispatchExecutorMetrics {
93    actor_id_str: String,
94    fragment_id_str: String,
95    metrics: Arc<StreamingMetrics>,
96    actor_out_record_cnt: LabelGuardedIntCounter,
97}
98
99impl DispatchExecutorMetrics {
100    fn monitor_dispatcher(&self, dispatcher: DispatcherImpl) -> DispatcherWithMetrics {
101        DispatcherWithMetrics {
102            actor_output_buffer_blocking_duration_ns: self
103                .metrics
104                .actor_output_buffer_blocking_duration_ns
105                .with_guarded_label_values(&[
106                    self.actor_id_str.as_str(),
107                    self.fragment_id_str.as_str(),
108                    dispatcher.dispatcher_id().to_string().as_str(),
109                ]),
110            dispatcher,
111        }
112    }
113}
114
115pub struct DispatchExecutorInner {
116    dispatchers: Vec<DispatcherWithMetrics>,
117    actor_id: ActorId,
118    actor_config: Arc<StreamingConfig>,
119    metrics: DispatchExecutorMetrics,
120    new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
121    pending_new_output_requests: HashMap<ActorId, NewOutputRequest>,
122}
123
124impl DispatchExecutorInner {
125    async fn collect_outputs(
126        &mut self,
127        downstream_actors: &[ActorId],
128    ) -> StreamResult<Vec<Output>> {
129        fn resolve_output(downstream_actor: ActorId, request: NewOutputRequest) -> Output {
130            let tx = match request {
131                NewOutputRequest::Local(tx) | NewOutputRequest::Remote(tx) => tx,
132            };
133            Output::new(downstream_actor, tx)
134        }
135        let mut outputs = Vec::with_capacity(downstream_actors.len());
136        for &downstream_actor in downstream_actors {
137            let output =
138                if let Some(request) = self.pending_new_output_requests.remove(&downstream_actor) {
139                    resolve_output(downstream_actor, request)
140                } else {
141                    loop {
142                        let (requested_actor, request) = self
143                            .new_output_request_rx
144                            .recv()
145                            .await
146                            .ok_or_else(|| anyhow!("end of new output request"))?;
147                        if requested_actor == downstream_actor {
148                            break resolve_output(requested_actor, request);
149                        } else {
150                            assert!(
151                                self.pending_new_output_requests
152                                    .insert(requested_actor, request)
153                                    .is_none(),
154                                "duplicated inflight new output requests from actor {}",
155                                requested_actor
156                            );
157                        }
158                    }
159                };
160            outputs.push(output);
161        }
162        Ok(outputs)
163    }
164
165    async fn dispatch(&mut self, msg: MessageBatch) -> StreamResult<()> {
166        if self.dispatchers.is_empty() {
167            // Our dispatcher internal implementation calls tokio method, which already involves cooperative scheduling
168            // with tokio runtime.
169            //
170            // When there is no dispatcher, we should do the cooperative scheduling together.
171            consume_budget().await;
172        }
173        async fn await_with_metrics(
174            future: impl Future<Output = ()>,
175            metrics: &LabelGuardedIntCounter,
176        ) {
177            let mut start_time = Instant::now();
178            let interval_duration = Duration::from_secs(15);
179            let mut interval =
180                tokio::time::interval_at(start_time + interval_duration, interval_duration);
181
182            let mut fut = pin!(future);
183
184            loop {
185                tokio::select! {
186                    biased;
187                    _res = &mut fut => {
188                        let ns = start_time.elapsed().as_nanos() as u64;
189                        metrics.inc_by(ns);
190                        break;
191                    }
192                    _ = interval.tick() => {
193                        start_time = Instant::now();
194                        metrics.inc_by(interval_duration.as_nanos() as u64);
195                    }
196                };
197            }
198        }
199
200        use futures::StreamExt;
201
202        let limit = self.actor_config.developer.exchange_concurrent_dispatchers;
203        // Only barrier can be batched for now.
204        match msg {
205            MessageBatch::BarrierBatch(barrier_batch) => {
206                if barrier_batch.is_empty() {
207                    return Ok(());
208                }
209                // Only the first barrier in a batch can be mutation.
210                let mutation = barrier_batch[0].mutation.clone();
211                self.pre_mutate_dispatchers(&mutation).await?;
212                futures::stream::iter(self.dispatchers.iter_mut())
213                    .for_each_concurrent(limit, |dispatcher| async {
214                        let metrics = &dispatcher.actor_output_buffer_blocking_duration_ns;
215                        let dispatcher_output = &mut dispatcher.dispatcher;
216                        let fut = dispatcher_output.dispatch_barriers(
217                            barrier_batch
218                                .iter()
219                                .cloned()
220                                .map(|b| b.into_dispatcher())
221                                .collect(),
222                        );
223                        await_with_metrics(fut, metrics).await;
224                    })
225                    .await;
226                self.post_mutate_dispatchers(&mutation)?;
227            }
228            MessageBatch::Watermark(watermark) => {
229                futures::stream::iter(self.dispatchers.iter_mut())
230                    .for_each_concurrent(limit, |dispatcher| async {
231                        let metrics = &dispatcher.actor_output_buffer_blocking_duration_ns;
232                        let dispatcher_output = &mut dispatcher.dispatcher;
233                        let fut = dispatcher_output.dispatch_watermark(watermark.clone());
234                        await_with_metrics(fut, metrics).await;
235                    })
236                    .await;
237            }
238            MessageBatch::Chunk(chunk) => {
239                futures::stream::iter(self.dispatchers.iter_mut())
240                    .for_each_concurrent(limit, |dispatcher| async {
241                        let metrics = &dispatcher.actor_output_buffer_blocking_duration_ns;
242                        let dispatcher_output = &mut dispatcher.dispatcher;
243                        let fut = dispatcher_output.dispatch_data(chunk.clone());
244                        await_with_metrics(fut, metrics).await;
245                    })
246                    .await;
247
248                self.metrics
249                    .actor_out_record_cnt
250                    .inc_by(chunk.cardinality() as _);
251            }
252        }
253        self.dispatchers.retain(|dispatcher| match &**dispatcher {
254            DispatcherImpl::Failed(dispatcher_id, e) => {
255                warn!(%dispatcher_id, err = %e.as_report(), actor_id = %self.actor_id, "dispatch fail");
256                false
257            }
258            _ => true,
259        });
260        Ok(())
261    }
262
263    /// Add new dispatchers to the executor. Will check whether their ids are unique.
264    async fn add_dispatchers<'a>(
265        &mut self,
266        new_dispatchers: impl IntoIterator<Item = &'a PbDispatcher>,
267    ) -> StreamResult<()> {
268        for dispatcher in new_dispatchers {
269            let outputs = self
270                .collect_outputs(&dispatcher.downstream_actor_id)
271                .await?;
272            let dispatcher = DispatcherImpl::new(outputs, dispatcher)?;
273            let dispatcher = self.metrics.monitor_dispatcher(dispatcher);
274            self.dispatchers.push(dispatcher);
275        }
276
277        assert!(
278            self.dispatchers
279                .iter()
280                .map(|d| d.dispatcher_id())
281                .all_unique(),
282            "dispatcher ids must be unique: {:?}",
283            self.dispatchers
284        );
285
286        Ok(())
287    }
288
289    fn find_dispatcher(&mut self, dispatcher_id: DispatcherId) -> Option<&mut DispatcherImpl> {
290        self.dispatchers
291            .iter_mut()
292            .find(|d| d.dispatcher_id() == dispatcher_id)
293            .map(DerefMut::deref_mut)
294    }
295
296    /// Update the dispatcher BEFORE we actually dispatch this barrier. We'll only add the new
297    /// outputs.
298    async fn pre_update_dispatcher(&mut self, update: &PbDispatcherUpdate) -> StreamResult<()> {
299        let outputs = self
300            .collect_outputs(&update.added_downstream_actor_id)
301            .await?;
302
303        let Some(dispatcher) = self.find_dispatcher(update.dispatcher_id) else {
304            warn!(dispatcher_id = %update.dispatcher_id, added = ?update.added_downstream_actor_id, removed = ?update.removed_downstream_actor_id, actor_id = %self.actor_id, "ignore dispatcher update");
305            return Ok(());
306        };
307        dispatcher.add_outputs(outputs);
308
309        Ok(())
310    }
311
312    /// Update the dispatcher AFTER we dispatch this barrier. We'll remove some outputs and finally
313    /// update the hash mapping.
314    fn post_update_dispatcher(&mut self, update: &PbDispatcherUpdate) -> StreamResult<()> {
315        let ids = update.removed_downstream_actor_id.iter().copied().collect();
316
317        let Some(dispatcher) = self.find_dispatcher(update.dispatcher_id) else {
318            warn!(dispatcher_id = %update.dispatcher_id, added = ?update.added_downstream_actor_id, removed = ?update.removed_downstream_actor_id, actor_id = %self.actor_id, "ignore dispatcher update");
319            return Ok(());
320        };
321        dispatcher.remove_outputs(&ids);
322
323        // The hash mapping is only used by the hash dispatcher.
324        //
325        // We specify a single upstream hash mapping for scaling the downstream fragment. However,
326        // it's possible that there're multiple upstreams with different exchange types, for
327        // example, the `Broadcast` inner side of the dynamic filter. There're too many combinations
328        // to handle here, so we just ignore the `hash_mapping` field for any other exchange types.
329        if let DispatcherImpl::Hash(dispatcher) = dispatcher {
330            dispatcher.hash_mapping =
331                ActorMapping::from_protobuf(update.get_hash_mapping()?).to_expanded();
332        }
333
334        Ok(())
335    }
336
337    /// For `Add` and `Update`, update the dispatchers before we dispatch the barrier.
338    async fn pre_mutate_dispatchers(
339        &mut self,
340        mutation: &Option<Arc<Mutation>>,
341    ) -> StreamResult<()> {
342        let Some(mutation) = mutation.as_deref() else {
343            return Ok(());
344        };
345
346        match mutation {
347            Mutation::Add(AddMutation { adds, .. }) => {
348                if let Some(new_dispatchers) = adds.get(&self.actor_id) {
349                    self.add_dispatchers(new_dispatchers).await?;
350                }
351            }
352            Mutation::Update(UpdateMutation {
353                dispatchers,
354                actor_new_dispatchers: actor_dispatchers,
355                ..
356            }) => {
357                if let Some(new_dispatchers) = actor_dispatchers.get(&self.actor_id) {
358                    self.add_dispatchers(new_dispatchers).await?;
359                }
360
361                if let Some(updates) = dispatchers.get(&self.actor_id) {
362                    for update in updates {
363                        self.pre_update_dispatcher(update).await?;
364                    }
365                }
366            }
367            _ => {}
368        }
369
370        Ok(())
371    }
372
373    /// For `Stop` and `Update`, update the dispatchers after we dispatch the barrier.
374    fn post_mutate_dispatchers(&mut self, mutation: &Option<Arc<Mutation>>) -> StreamResult<()> {
375        let Some(mutation) = mutation.as_deref() else {
376            return Ok(());
377        };
378
379        if let Mutation::Update(UpdateMutation { dispatchers, .. }) = mutation
380            && let Some(updates) = dispatchers.get(&self.actor_id)
381        {
382            for update in updates {
383                self.post_update_dispatcher(update)?;
384            }
385        };
386
387        if let Some(dropped_actors) = mutation.all_stop_actors()
388            // Remove outputs only if this actor itself is not to be stopped.
389            && !dropped_actors.contains(&self.actor_id)
390        {
391            for dispatcher in &mut self.dispatchers {
392                dispatcher.remove_outputs(dropped_actors);
393            }
394        }
395
396        // After stopping the downstream mview, the outputs of some dispatcher might be empty and we
397        // should clean up them.
398        self.dispatchers.retain(|d| !d.is_empty());
399
400        Ok(())
401    }
402}
403
404impl DispatchExecutor {
405    pub(crate) async fn new(
406        input: Executor,
407        new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
408        dispatchers: Vec<stream_plan::Dispatcher>,
409        actor_context: &ActorContextRef,
410    ) -> StreamResult<Self> {
411        let mut executor = Self::new_inner(
412            input,
413            new_output_request_rx,
414            vec![],
415            actor_context.id,
416            actor_context.fragment_id,
417            actor_context.config.clone(),
418            actor_context.streaming_metrics.clone(),
419        );
420        let inner = &mut executor.inner;
421        for dispatcher in dispatchers {
422            let outputs = inner
423                .collect_outputs(&dispatcher.downstream_actor_id)
424                .await?;
425            let dispatcher = DispatcherImpl::new(outputs, &dispatcher)?;
426            let dispatcher = inner.metrics.monitor_dispatcher(dispatcher);
427            inner.dispatchers.push(dispatcher);
428        }
429        Ok(executor)
430    }
431
432    #[cfg(test)]
433    pub(crate) fn for_test(
434        input: Executor,
435        dispatchers: Vec<DispatcherImpl>,
436        actor_id: ActorId,
437        fragment_id: FragmentId,
438        actor_config: Arc<StreamingConfig>,
439        metrics: Arc<StreamingMetrics>,
440    ) -> (
441        Self,
442        tokio::sync::mpsc::UnboundedSender<(ActorId, NewOutputRequest)>,
443    ) {
444        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
445
446        (
447            Self::new_inner(
448                input,
449                rx,
450                dispatchers,
451                actor_id,
452                fragment_id,
453                actor_config,
454                metrics,
455            ),
456            tx,
457        )
458    }
459
460    fn new_inner(
461        mut input: Executor,
462        new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
463        dispatchers: Vec<DispatcherImpl>,
464        actor_id: ActorId,
465        fragment_id: FragmentId,
466        actor_config: Arc<StreamingConfig>,
467        metrics: Arc<StreamingMetrics>,
468    ) -> Self {
469        if crate::consistency::insane() {
470            // make some trouble before dispatching to avoid generating invalid dist key.
471            let mut info = input.info().clone();
472            info.identity = format!("{} (embedded trouble)", info.identity);
473            let troublemaker = TroublemakerExecutor::new(input, actor_config.developer.chunk_size);
474            input = (info, troublemaker).into();
475        }
476
477        let actor_id_str = actor_id.to_string();
478        let fragment_id_str = fragment_id.to_string();
479        let actor_out_record_cnt = metrics
480            .actor_out_record_cnt
481            .with_guarded_label_values(&[&actor_id_str, &fragment_id_str]);
482        let metrics = DispatchExecutorMetrics {
483            actor_id_str,
484            fragment_id_str,
485            metrics,
486            actor_out_record_cnt,
487        };
488        let dispatchers = dispatchers
489            .into_iter()
490            .map(|dispatcher| metrics.monitor_dispatcher(dispatcher))
491            .collect();
492
493        Self {
494            input,
495            inner: DispatchExecutorInner {
496                dispatchers,
497                actor_id,
498                actor_config,
499                metrics,
500                new_output_request_rx,
501                pending_new_output_requests: Default::default(),
502            },
503        }
504    }
505}
506
507/// Dispatches a message batch downstream and returns the barriers that should be yielded by the
508/// barrier stream.
509async fn dispatch_message_batch(
510    inner: &mut DispatchExecutorInner,
511    batch: MessageBatch,
512) -> StreamResult<Option<Vec<Barrier>>> {
513    match batch {
514        MessageBatch::Chunk(chunk) => {
515            inner
516                .dispatch(MessageBatch::Chunk(chunk))
517                .instrument(tracing::info_span!("dispatch_chunk"))
518                .instrument_await("dispatch_chunk")
519                .await?;
520            Ok(None)
521        }
522        MessageBatch::BarrierBatch(barrier_batch) => {
523            assert!(!barrier_batch.is_empty());
524            let yielded_barriers = barrier_batch.clone();
525            inner
526                .dispatch(MessageBatch::BarrierBatch(barrier_batch))
527                .instrument(tracing::info_span!("dispatch_barrier_batch"))
528                .instrument_await("dispatch_barrier_batch")
529                .await?;
530            inner
531                .metrics
532                .metrics
533                .barrier_batch_size
534                .observe(yielded_barriers.len() as f64);
535            Ok(Some(yielded_barriers))
536        }
537        MessageBatch::Watermark(watermark) => {
538            inner
539                .dispatch(MessageBatch::Watermark(watermark))
540                .instrument(tracing::info_span!("dispatch_watermark"))
541                .instrument_await("dispatch_watermark")
542                .await?;
543            Ok(None)
544        }
545    }
546}
547
548impl StreamConsumer for DispatchExecutor {
549    type BarrierStream = impl Stream<Item = StreamResult<Barrier>> + Send;
550
551    fn execute(mut self: Box<Self>) -> Self::BarrierStream {
552        let max_barrier_count_per_batch = self.inner.actor_config.developer.max_barrier_batch_size;
553        #[try_stream]
554        async move {
555            let mut input = self.input.execute().peekable();
556            loop {
557                let Some(message) =
558                    try_batch_barriers(max_barrier_count_per_batch, &mut input).await?
559                else {
560                    // end_of_stream
561                    break;
562                };
563                if let Some(barrier_batch) =
564                    dispatch_message_batch(&mut self.inner, message).await?
565                {
566                    for barrier in barrier_batch {
567                        yield barrier;
568                    }
569                }
570            }
571        }
572    }
573}
574
575/// Tries to batch up to `max_barrier_count_per_batch` consecutive barriers within a single message batch.
576///
577/// Returns the message batch.
578///
579/// Returns None if end of stream.
580async fn try_batch_barriers(
581    max_barrier_count_per_batch: u32,
582    input: &mut Peekable<BoxedMessageStream>,
583) -> StreamResult<Option<MessageBatch>> {
584    let Some(msg) = input.next().await else {
585        // end_of_stream
586        return Ok(None);
587    };
588    let mut barrier_batch = vec![];
589    let msg: Message = msg?;
590    let max_peek_attempts = match msg {
591        Message::Chunk(c) => {
592            return Ok(Some(MessageBatch::Chunk(c)));
593        }
594        Message::Watermark(w) => {
595            return Ok(Some(MessageBatch::Watermark(w)));
596        }
597        Message::Barrier(b) => {
598            let peek_more_barrier = b.mutation.is_none();
599            barrier_batch.push(b);
600            if peek_more_barrier {
601                max_barrier_count_per_batch.saturating_sub(1)
602            } else {
603                0
604            }
605        }
606    };
607    // Try to peek more consecutive non-mutation barriers.
608    for _ in 0..max_peek_attempts {
609        let peek = input.peek().now_or_never();
610        let Some(peek) = peek else {
611            break;
612        };
613        let Some(msg) = peek else {
614            // end_of_stream
615            break;
616        };
617        let Ok(Message::Barrier(barrier)) = msg else {
618            break;
619        };
620        if barrier.mutation.is_some() {
621            break;
622        }
623        let msg: Message = input.next().now_or_never().unwrap().unwrap()?;
624        let Message::Barrier(ref barrier) = msg else {
625            unreachable!("must be a barrier");
626        };
627        barrier_batch.push(barrier.clone());
628    }
629    Ok(Some(MessageBatch::BarrierBatch(barrier_batch)))
630}
631
632#[derive(Debug)]
633pub(crate) enum DispatcherImpl {
634    Hash(HashDataDispatcher),
635    Broadcast(BroadcastDispatcher),
636    Simple(SimpleDispatcher),
637    #[cfg_attr(not(test), expect(dead_code))]
638    RoundRobin(RoundRobinDataDispatcher),
639    /// The dispatcher has failed to dispatch to downstream and gets pending
640    /// Should be clean up later.
641    Failed(DispatcherId, StreamError),
642}
643
644impl DispatcherImpl {
645    pub fn new(outputs: Vec<Output>, dispatcher: &PbDispatcher) -> StreamResult<Self> {
646        let output_mapping =
647            DispatchOutputMapping::from_protobuf(dispatcher.output_mapping.clone().unwrap());
648
649        use risingwave_pb::stream_plan::DispatcherType::*;
650        let dispatcher_impl = match dispatcher.get_type()? {
651            Hash => {
652                assert!(!outputs.is_empty());
653                let dist_key_indices = dispatcher
654                    .dist_key_indices
655                    .iter()
656                    .map(|i| *i as usize)
657                    .collect();
658
659                let hash_mapping =
660                    ActorMapping::from_protobuf(dispatcher.get_hash_mapping()?).to_expanded();
661
662                DispatcherImpl::Hash(HashDataDispatcher::new(
663                    outputs,
664                    dist_key_indices,
665                    output_mapping,
666                    hash_mapping,
667                    dispatcher.dispatcher_id,
668                ))
669            }
670            Broadcast => DispatcherImpl::Broadcast(BroadcastDispatcher::new(
671                outputs,
672                output_mapping,
673                dispatcher.dispatcher_id,
674            )),
675            Simple | NoShuffle => {
676                let [output]: [_; 1] = outputs.try_into().unwrap();
677                DispatcherImpl::Simple(SimpleDispatcher::new(
678                    output,
679                    output_mapping,
680                    dispatcher.dispatcher_id,
681                ))
682            }
683            Unspecified => unreachable!(),
684        };
685
686        Ok(dispatcher_impl)
687    }
688}
689
690macro_rules! impl_dispatcher {
691    ($( { $variant_name:ident } ),*) => {
692        impl DispatcherImpl {
693            pub async fn dispatch_data(&mut self, chunk: StreamChunk) {
694                if let Err(e) = match self {
695                    $( Self::$variant_name(inner) => inner.dispatch_data(chunk).await, )*
696                    Self::Failed(..) => unreachable!(),
697                } {
698                    *self = Self::Failed(self.dispatcher_id(), e);
699                }
700            }
701
702            pub async fn dispatch_barriers(&mut self, barriers: DispatcherBarriers) {
703                if let Err(e) = match self {
704                    $( Self::$variant_name(inner) => inner.dispatch_barriers(barriers).await, )*
705                    Self::Failed(..) => unreachable!(),
706                } {
707                    *self = Self::Failed(self.dispatcher_id(), e);
708                }
709            }
710
711            pub async fn dispatch_watermark(&mut self, watermark: Watermark) {
712                if let Err(e) = match self {
713                    $( Self::$variant_name(inner) => inner.dispatch_watermark(watermark).await, )*
714                    Self::Failed(..) => unreachable!(),
715                } {
716                    *self = Self::Failed(self.dispatcher_id(), e);
717                }
718            }
719
720            pub fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>) {
721                match self {
722                    $(Self::$variant_name(inner) => inner.add_outputs(outputs), )*
723                    Self::Failed(..) => {},
724                }
725            }
726
727            pub fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>) {
728                match self {
729                    $(Self::$variant_name(inner) => inner.remove_outputs(actor_ids), )*
730                    Self::Failed(..) => {},
731                }
732            }
733
734            pub fn dispatcher_id(&self) -> DispatcherId {
735                match self {
736                    $(Self::$variant_name(inner) => inner.dispatcher_id(), )*
737                    Self::Failed(dispatcher_id, ..) => *dispatcher_id,
738                }
739            }
740
741            pub fn is_empty(&self) -> bool {
742                match self {
743                    $(Self::$variant_name(inner) => inner.is_empty(), )*
744                    Self::Failed(..) => true,
745                }
746            }
747        }
748    }
749}
750
751macro_rules! for_all_dispatcher_variants {
752    ($macro:ident) => {
753        $macro! {
754            { Hash },
755            { Broadcast },
756            { Simple },
757            { RoundRobin }
758        }
759    };
760}
761
762for_all_dispatcher_variants! { impl_dispatcher }
763
764pub trait DispatchFuture<'a> = Future<Output = StreamResult<()>> + Send;
765
766trait Dispatcher: Debug + 'static {
767    /// Dispatch a data chunk to downstream actors.
768    fn dispatch_data(&mut self, chunk: StreamChunk) -> impl DispatchFuture<'_>;
769    /// Dispatch barriers to downstream actors, generally by broadcasting it.
770    fn dispatch_barriers(&mut self, barrier: DispatcherBarriers) -> impl DispatchFuture<'_>;
771    /// Dispatch a watermark to downstream actors, generally by broadcasting it.
772    fn dispatch_watermark(&mut self, watermark: Watermark) -> impl DispatchFuture<'_>;
773
774    /// Add new outputs to the dispatcher.
775    fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>);
776    /// Remove outputs to `actor_ids` from the dispatcher.
777    fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>);
778
779    /// The ID of the dispatcher. A [`DispatchExecutor`] may have multiple dispatchers with
780    /// different IDs.
781    ///
782    /// Note that the dispatcher id is always equal to the downstream fragment id.
783    /// See also `proto/stream_plan.proto`.
784    fn dispatcher_id(&self) -> DispatcherId;
785
786    /// Whether the dispatcher has no outputs. If so, it'll be cleaned up from the
787    /// [`DispatchExecutor`].
788    fn is_empty(&self) -> bool;
789}
790
791/// Concurrently broadcast a message to all outputs.
792///
793/// Note that this does not follow `concurrent_dispatchers` in the config and the concurrency is
794/// always unlimited.
795async fn broadcast_concurrent(
796    outputs: impl IntoIterator<Item = &'_ mut Output>,
797    message: DispatcherMessageBatch,
798) -> StreamResult<()> {
799    futures::future::try_join_all(
800        outputs
801            .into_iter()
802            .map(|output| output.send(message.clone())),
803    )
804    .await?;
805    Ok(())
806}
807
808#[derive(Debug)]
809pub struct RoundRobinDataDispatcher {
810    outputs: Vec<Output>,
811    output_mapping: DispatchOutputMapping,
812    cur: usize,
813    dispatcher_id: DispatcherId,
814}
815
816impl RoundRobinDataDispatcher {
817    #[cfg_attr(not(test), expect(dead_code))]
818    pub fn new(
819        outputs: Vec<Output>,
820        output_mapping: DispatchOutputMapping,
821        dispatcher_id: DispatcherId,
822    ) -> Self {
823        Self {
824            outputs,
825            output_mapping,
826            cur: 0,
827            dispatcher_id,
828        }
829    }
830}
831
832impl Dispatcher for RoundRobinDataDispatcher {
833    async fn dispatch_data(&mut self, chunk: StreamChunk) -> StreamResult<()> {
834        let chunk = self.output_mapping.apply(chunk);
835
836        self.outputs[self.cur]
837            .send(DispatcherMessageBatch::Chunk(chunk))
838            .await?;
839        self.cur += 1;
840        self.cur %= self.outputs.len();
841        Ok(())
842    }
843
844    async fn dispatch_barriers(&mut self, barriers: DispatcherBarriers) -> StreamResult<()> {
845        // always broadcast barrier
846        broadcast_concurrent(
847            &mut self.outputs,
848            DispatcherMessageBatch::BarrierBatch(barriers),
849        )
850        .await
851    }
852
853    async fn dispatch_watermark(&mut self, watermark: Watermark) -> StreamResult<()> {
854        if let Some(watermark) = self.output_mapping.apply_watermark(watermark) {
855            // always broadcast watermark
856            broadcast_concurrent(
857                &mut self.outputs,
858                DispatcherMessageBatch::Watermark(watermark),
859            )
860            .await?;
861        }
862        Ok(())
863    }
864
865    fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>) {
866        self.outputs.extend(outputs);
867    }
868
869    fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>) {
870        self.outputs
871            .extract_if(.., |output| actor_ids.contains(&output.actor_id()))
872            .count();
873        self.cur = self.cur.min(self.outputs.len() - 1);
874    }
875
876    fn dispatcher_id(&self) -> DispatcherId {
877        self.dispatcher_id
878    }
879
880    fn is_empty(&self) -> bool {
881        self.outputs.is_empty()
882    }
883}
884
885pub struct HashDataDispatcher {
886    outputs: Vec<Output>,
887    keys: Vec<usize>,
888    output_mapping: DispatchOutputMapping,
889    /// Mapping from virtual node to actor id, used for hash data dispatcher to dispatch tasks to
890    /// different downstream actors.
891    hash_mapping: ExpandedActorMapping,
892    dispatcher_id: DispatcherId,
893}
894
895impl Debug for HashDataDispatcher {
896    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
897        f.debug_struct("HashDataDispatcher")
898            .field("outputs", &self.outputs)
899            .field("keys", &self.keys)
900            .field("dispatcher_id", &self.dispatcher_id)
901            .finish_non_exhaustive()
902    }
903}
904
905impl HashDataDispatcher {
906    pub fn new(
907        outputs: Vec<Output>,
908        keys: Vec<usize>,
909        output_mapping: DispatchOutputMapping,
910        hash_mapping: ExpandedActorMapping,
911        dispatcher_id: DispatcherId,
912    ) -> Self {
913        Self {
914            outputs,
915            keys,
916            output_mapping,
917            hash_mapping,
918            dispatcher_id,
919        }
920    }
921}
922
923impl Dispatcher for HashDataDispatcher {
924    fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>) {
925        self.outputs.extend(outputs);
926    }
927
928    async fn dispatch_barriers(&mut self, barriers: DispatcherBarriers) -> StreamResult<()> {
929        // always broadcast barrier
930        broadcast_concurrent(
931            &mut self.outputs,
932            DispatcherMessageBatch::BarrierBatch(barriers),
933        )
934        .await
935    }
936
937    async fn dispatch_watermark(&mut self, watermark: Watermark) -> StreamResult<()> {
938        if let Some(watermark) = self.output_mapping.apply_watermark(watermark) {
939            // always broadcast watermark
940            broadcast_concurrent(
941                &mut self.outputs,
942                DispatcherMessageBatch::Watermark(watermark),
943            )
944            .await?;
945        }
946        Ok(())
947    }
948
949    async fn dispatch_data(&mut self, chunk: StreamChunk) -> StreamResult<()> {
950        // A chunk can be shuffled into multiple output chunks that to be sent to downstreams.
951        // In these output chunks, the only difference are visibility map, which is calculated
952        // by the hash value of each line in the input chunk.
953        let num_outputs = self.outputs.len();
954
955        // get hash value of every line by its key
956        let vnode_count = self.hash_mapping.len();
957        let vnodes = VirtualNode::compute_chunk(chunk.data_chunk(), &self.keys, vnode_count);
958
959        tracing::debug!(target: "events::stream::dispatch::hash", "\n{}\n keys {:?} => {:?}", chunk.to_pretty(), self.keys, vnodes);
960
961        let mut vis_maps = repeat_with(|| BitmapBuilder::with_capacity(chunk.capacity()))
962            .take(num_outputs)
963            .collect_vec();
964        let mut last_update_delete_row_idx = None;
965        let mut new_ops: Vec<Op> = Vec::with_capacity(chunk.capacity());
966
967        for (row_idx, ((vnode, &op), visible)) in vnodes
968            .iter()
969            .copied()
970            .zip_eq_fast(chunk.ops())
971            .zip_eq_fast(chunk.visibility().iter())
972            .enumerate()
973        {
974            // Build visibility map for every output chunk.
975            for (output, vis_map) in self.outputs.iter().zip_eq_fast(vis_maps.iter_mut()) {
976                vis_map.append(visible && self.hash_mapping[vnode.to_index()] == output.actor_id());
977            }
978
979            if !visible {
980                new_ops.push(op);
981                continue;
982            }
983
984            // The `Update` message, noted by an `UpdateDelete` and a successive `UpdateInsert`,
985            // need to be rewritten to common `Delete` and `Insert` if the distribution key
986            // columns are changed, since the distribution key will eventually be part of the
987            // stream key of the downstream executor, and there's an invariant that stream key
988            // must be the same for rows within an `Update` pair.
989            if op == Op::UpdateDelete {
990                last_update_delete_row_idx = Some(row_idx);
991            } else if op == Op::UpdateInsert {
992                let delete_row_idx = last_update_delete_row_idx
993                    .take()
994                    .expect("missing U- before U+");
995
996                // Check if any distribution key column value changed
997                let dist_key_changed = chunk.row_at(delete_row_idx).1.project(&self.keys)
998                    != chunk.row_at(row_idx).1.project(&self.keys);
999
1000                if dist_key_changed {
1001                    new_ops.push(Op::Delete);
1002                    new_ops.push(Op::Insert);
1003                } else {
1004                    new_ops.push(Op::UpdateDelete);
1005                    new_ops.push(Op::UpdateInsert);
1006                }
1007            } else {
1008                new_ops.push(op);
1009            }
1010        }
1011        assert!(last_update_delete_row_idx.is_none(), "missing U+ after U-");
1012
1013        // Apply output mapping after calculating the vnode and new visibility maps.
1014        // The output mapping may project columns and eliminate noop updates.
1015        let chunk = self.output_mapping.apply(chunk);
1016        // Get the visibility after noop update elimination to incorporate into the final visibility.
1017        let chunk_vis = chunk.visibility();
1018
1019        // The noop elimination in `output_mapping.apply()` may normalize partially-visible
1020        // update pairs (e.g., U- → Delete, U+ → Insert). We must adopt these normalized ops,
1021        // otherwise the downstream will see an orphan U+ and panic.
1022        // Meanwhile, `new_ops` contains dist-key-changed rewrites (U-/U+ → Delete/Insert)
1023        // that are not reflected in `chunk.ops()`. Merge both: prefer `new_ops` when it already
1024        // did a rewrite, otherwise take the (possibly normalized) op from the chunk.
1025        let chunk_ops = chunk.ops();
1026        let ops: Vec<Op> = new_ops
1027            .iter()
1028            .zip_eq_fast(chunk_ops.iter())
1029            .map(|(&new_op, &elim_op)| {
1030                // If new_ops already rewrote U-/U+ to Delete/Insert (dist_key_changed), keep it.
1031                // Otherwise, use the post-elimination op which may have been normalized.
1032                match (new_op, elim_op) {
1033                    // dist_key_changed rewrite: new_ops turned U- into Delete
1034                    (Op::Delete, Op::UpdateDelete) => Op::Delete,
1035                    // dist_key_changed rewrite: new_ops turned U+ into Insert
1036                    (Op::Insert, Op::UpdateInsert) => Op::Insert,
1037                    // For all other cases, use the post-elimination op (which includes normalize).
1038                    _ => elim_op,
1039                }
1040            })
1041            .collect();
1042
1043        // individually output StreamChunk integrated with vis_map
1044        futures::future::try_join_all(
1045            vis_maps
1046                .into_iter()
1047                .zip_eq_fast(self.outputs.iter_mut())
1048                .map(|(vis_map, output)| async {
1049                    let vis_map = vis_map.finish();
1050                    // Combine hash routing visibility with noop update elimination visibility.
1051                    // A row is visible only if it passes BOTH:
1052                    // 1. Hash routing (the row should go to this output)
1053                    // 2. Noop update elimination (the row was not eliminated as a noop)
1054                    let combined_vis = &vis_map & chunk_vis;
1055                    let new_stream_chunk = StreamChunk::with_visibility(
1056                        ops.clone(),
1057                        chunk.columns().into(),
1058                        combined_vis,
1059                    );
1060                    if new_stream_chunk.cardinality() > 0 {
1061                        event!(
1062                            tracing::Level::TRACE,
1063                            msg = "chunk",
1064                            downstream = %output.actor_id(),
1065                            "send = \n{:#?}",
1066                            new_stream_chunk
1067                        );
1068                        output
1069                            .send(DispatcherMessageBatch::Chunk(new_stream_chunk))
1070                            .await?;
1071                    }
1072                    StreamResult::Ok(())
1073                }),
1074        )
1075        .await?;
1076
1077        Ok(())
1078    }
1079
1080    fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>) {
1081        self.outputs
1082            .extract_if(.., |output| actor_ids.contains(&output.actor_id()))
1083            .count();
1084    }
1085
1086    fn dispatcher_id(&self) -> DispatcherId {
1087        self.dispatcher_id
1088    }
1089
1090    fn is_empty(&self) -> bool {
1091        self.outputs.is_empty()
1092    }
1093}
1094
1095/// `BroadcastDispatcher` dispatches message to all outputs.
1096#[derive(Debug)]
1097pub struct BroadcastDispatcher {
1098    outputs: HashMap<ActorId, Output>,
1099    output_mapping: DispatchOutputMapping,
1100    dispatcher_id: DispatcherId,
1101}
1102
1103impl BroadcastDispatcher {
1104    pub fn new(
1105        outputs: impl IntoIterator<Item = Output>,
1106        output_mapping: DispatchOutputMapping,
1107        dispatcher_id: DispatcherId,
1108    ) -> Self {
1109        Self {
1110            outputs: Self::into_pairs(outputs).collect(),
1111            output_mapping,
1112            dispatcher_id,
1113        }
1114    }
1115
1116    fn into_pairs(
1117        outputs: impl IntoIterator<Item = Output>,
1118    ) -> impl Iterator<Item = (ActorId, Output)> {
1119        outputs
1120            .into_iter()
1121            .map(|output| (output.actor_id(), output))
1122    }
1123}
1124
1125impl Dispatcher for BroadcastDispatcher {
1126    async fn dispatch_data(&mut self, chunk: StreamChunk) -> StreamResult<()> {
1127        let chunk = self.output_mapping.apply(chunk);
1128        broadcast_concurrent(
1129            self.outputs.values_mut(),
1130            DispatcherMessageBatch::Chunk(chunk),
1131        )
1132        .await
1133    }
1134
1135    async fn dispatch_barriers(&mut self, barriers: DispatcherBarriers) -> StreamResult<()> {
1136        // always broadcast barrier
1137        broadcast_concurrent(
1138            self.outputs.values_mut(),
1139            DispatcherMessageBatch::BarrierBatch(barriers),
1140        )
1141        .await
1142    }
1143
1144    async fn dispatch_watermark(&mut self, watermark: Watermark) -> StreamResult<()> {
1145        if let Some(watermark) = self.output_mapping.apply_watermark(watermark) {
1146            // always broadcast watermark
1147            broadcast_concurrent(
1148                self.outputs.values_mut(),
1149                DispatcherMessageBatch::Watermark(watermark),
1150            )
1151            .await?;
1152        }
1153        Ok(())
1154    }
1155
1156    fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>) {
1157        self.outputs.extend(Self::into_pairs(outputs));
1158    }
1159
1160    fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>) {
1161        self.outputs
1162            .extract_if(|actor_id, _| actor_ids.contains(actor_id))
1163            .count();
1164    }
1165
1166    fn dispatcher_id(&self) -> DispatcherId {
1167        self.dispatcher_id
1168    }
1169
1170    fn is_empty(&self) -> bool {
1171        self.outputs.is_empty()
1172    }
1173}
1174
1175/// `SimpleDispatcher` dispatches message to a single output.
1176#[derive(Debug)]
1177pub struct SimpleDispatcher {
1178    /// In most cases, there is exactly one output. However, in some cases of configuration change,
1179    /// the field needs to be temporarily set to 0 or 2 outputs.
1180    ///
1181    /// - When dropping a materialized view, the output will be removed and this field becomes
1182    ///   empty. The [`DispatchExecutor`] will immediately clean-up this empty dispatcher before
1183    ///   finishing processing the current mutation.
1184    /// - When migrating a singleton fragment, the new output will be temporarily added in `pre`
1185    ///   stage and this field becomes multiple, which is for broadcasting this configuration
1186    ///   change barrier to both old and new downstream actors. In `post` stage, the old output
1187    ///   will be removed and this field becomes single again.
1188    ///
1189    /// Therefore, when dispatching data, we assert that there's exactly one output by
1190    /// `Self::output`.
1191    output: SmallVec<[Output; 2]>,
1192    output_mapping: DispatchOutputMapping,
1193    dispatcher_id: DispatcherId,
1194}
1195
1196impl SimpleDispatcher {
1197    pub fn new(
1198        output: Output,
1199        output_mapping: DispatchOutputMapping,
1200        dispatcher_id: DispatcherId,
1201    ) -> Self {
1202        Self {
1203            output: smallvec![output],
1204            output_mapping,
1205            dispatcher_id,
1206        }
1207    }
1208}
1209
1210impl Dispatcher for SimpleDispatcher {
1211    fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>) {
1212        self.output.extend(outputs);
1213        assert!(self.output.len() <= 2);
1214    }
1215
1216    async fn dispatch_barriers(&mut self, barriers: DispatcherBarriers) -> StreamResult<()> {
1217        // Only barrier is allowed to be dispatched to multiple outputs during migration.
1218        for output in &mut self.output {
1219            output
1220                .send(DispatcherMessageBatch::BarrierBatch(barriers.clone()))
1221                .await?;
1222        }
1223        Ok(())
1224    }
1225
1226    async fn dispatch_data(&mut self, chunk: StreamChunk) -> StreamResult<()> {
1227        let output =
1228            Itertools::exactly_one(self.output.iter_mut()).expect("expect exactly one output");
1229
1230        let chunk = self.output_mapping.apply(chunk);
1231        output.send(DispatcherMessageBatch::Chunk(chunk)).await
1232    }
1233
1234    async fn dispatch_watermark(&mut self, watermark: Watermark) -> StreamResult<()> {
1235        let output =
1236            Itertools::exactly_one(self.output.iter_mut()).expect("expect exactly one output");
1237
1238        if let Some(watermark) = self.output_mapping.apply_watermark(watermark) {
1239            output
1240                .send(DispatcherMessageBatch::Watermark(watermark))
1241                .await?;
1242        }
1243        Ok(())
1244    }
1245
1246    fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>) {
1247        self.output
1248            .retain(|output| !actor_ids.contains(&output.actor_id()));
1249    }
1250
1251    fn dispatcher_id(&self) -> DispatcherId {
1252        self.dispatcher_id
1253    }
1254
1255    fn is_empty(&self) -> bool {
1256        self.output.is_empty()
1257    }
1258}
1259
1260#[cfg(test)]
1261mod tests {
1262    use std::hash::{BuildHasher, Hasher};
1263
1264    use futures::pin_mut;
1265    use multimap::MultiMap;
1266    use risingwave_common::array::stream_chunk::StreamChunkTestExt;
1267    use risingwave_common::array::{Array, ArrayBuilder, I32ArrayBuilder};
1268    use risingwave_common::catalog::{Field, Schema};
1269    use risingwave_common::types::DataType;
1270    use risingwave_common::util::epoch::test_epoch;
1271    use risingwave_common::util::hash_util::Crc32FastBuilder;
1272    use risingwave_expr::expr::{InputRefExpression, NonStrictExpression};
1273    use risingwave_pb::stream_plan::{DispatcherType, PbDispatchOutputMapping};
1274    use tokio::sync::mpsc::unbounded_channel;
1275
1276    use super::*;
1277    use crate::executor::exchange::output::Output;
1278    use crate::executor::exchange::permit::channel_for_test;
1279    use crate::executor::project::ProjectExecutor;
1280    use crate::executor::receiver::ReceiverExecutor;
1281    use crate::executor::test_utils::{MockSource, StreamExecutorTestExt};
1282    use crate::executor::{ActorContext, BarrierInner as Barrier, MessageInner as Message};
1283    use crate::task::barrier_test_utils::LocalBarrierTestEnv;
1284
1285    #[tokio::test]
1286    async fn test_hash_dispatcher_complex() {
1287        // This test only works when vnode count is 256.
1288        assert_eq!(VirtualNode::COUNT_FOR_TEST, 256);
1289
1290        let num_outputs = 2; // actor id ranges from 1 to 2
1291        let key_indices = &[0, 2];
1292        let (output_tx_vecs, mut output_rx_vecs): (Vec<_>, Vec<_>) =
1293            (0..num_outputs).map(|_| channel_for_test()).collect();
1294        let outputs = output_tx_vecs
1295            .into_iter()
1296            .enumerate()
1297            .map(|(actor_id, tx)| Output::new(ActorId::new(actor_id as u32 + 1), tx))
1298            .collect::<Vec<_>>();
1299        let mut hash_mapping = (1..num_outputs + 1)
1300            .flat_map(|id| vec![ActorId::new(id as u32); VirtualNode::COUNT_FOR_TEST / num_outputs])
1301            .collect_vec();
1302        hash_mapping.resize(
1303            VirtualNode::COUNT_FOR_TEST,
1304            ActorId::new(num_outputs as u32),
1305        );
1306        let mut hash_dispatcher = HashDataDispatcher::new(
1307            outputs,
1308            key_indices.to_vec(),
1309            DispatchOutputMapping::Simple(vec![0, 1, 2]),
1310            hash_mapping,
1311            0.into(),
1312        );
1313
1314        let chunk = StreamChunk::from_pretty(
1315            "  I I I
1316            +  4 6 8
1317            +  5 7 9
1318            +  0 0 0
1319            -  1 1 1 D
1320            U- 2 0 2
1321            U+ 2 0 2
1322            U- 3 3 2
1323            U+ 3 3 4",
1324        );
1325        hash_dispatcher.dispatch_data(chunk).await.unwrap();
1326
1327        assert_eq!(
1328            *output_rx_vecs[0].recv().await.unwrap().as_chunk().unwrap(),
1329            StreamChunk::from_pretty(
1330                "  I I I
1331                +  4 6 8
1332                +  5 7 9
1333                +  0 0 0
1334                -  1 1 1 D
1335                U- 2 0 2
1336                U+ 2 0 2
1337                -  3 3 2 D  // Should rewrite UpdateDelete to Delete
1338                +  3 3 4    // Should rewrite UpdateInsert to Insert",
1339            )
1340        );
1341        assert_eq!(
1342            *output_rx_vecs[1].recv().await.unwrap().as_chunk().unwrap(),
1343            StreamChunk::from_pretty(
1344                "  I I I
1345                +  4 6 8 D
1346                +  5 7 9 D
1347                +  0 0 0 D
1348                -  1 1 1 D  // Should keep original invisible mark
1349                U- 2 0 2 D  // Should keep UpdateDelete
1350                U+ 2 0 2 D  // Should keep UpdateInsert
1351                -  3 3 2    // Should rewrite UpdateDelete to Delete
1352                +  3 3 4 D  // Should rewrite UpdateInsert to Insert",
1353            )
1354        );
1355    }
1356
1357    #[tokio::test]
1358    async fn test_configuration_change() {
1359        let _schema = Schema { fields: vec![] };
1360        let (tx, rx) = channel_for_test();
1361        let actor_id = 233.into();
1362        let barrier_test_env = LocalBarrierTestEnv::for_test().await;
1363
1364        let (untouched, old, new) = (234.into(), 235.into(), 238.into()); // broadcast downstream actors
1365        let (old_simple, new_simple) = (114.into(), 514.into()); // simple downstream actors
1366
1367        // actor_id -> untouched, old, new, old_simple, new_simple
1368
1369        let broadcast_dispatcher_id = 666.into();
1370        let broadcast_dispatcher = PbDispatcher {
1371            r#type: DispatcherType::Broadcast as _,
1372            dispatcher_id: broadcast_dispatcher_id,
1373            downstream_actor_id: vec![untouched, old],
1374            output_mapping: PbDispatchOutputMapping::identical(0).into(), /* dummy length as it's not used */
1375            ..Default::default()
1376        };
1377
1378        let simple_dispatcher_id = 888.into();
1379        let simple_dispatcher = PbDispatcher {
1380            r#type: DispatcherType::Simple as _,
1381            dispatcher_id: simple_dispatcher_id,
1382            downstream_actor_id: vec![old_simple],
1383            output_mapping: PbDispatchOutputMapping::identical(0).into(), /* dummy length as it's not used */
1384            ..Default::default()
1385        };
1386
1387        let dispatcher_updates = maplit::hashmap! {
1388            actor_id => vec![PbDispatcherUpdate {
1389                actor_id,
1390                dispatcher_id: broadcast_dispatcher_id,
1391                added_downstream_actor_id: vec![new],
1392                removed_downstream_actor_id: vec![old],
1393                hash_mapping: Default::default(),
1394            }]
1395        };
1396        let b1 = Barrier::new_test_barrier(test_epoch(1)).with_mutation(Mutation::Update(
1397            UpdateMutation {
1398                dispatchers: dispatcher_updates,
1399                ..Default::default()
1400            },
1401        ));
1402        barrier_test_env.inject_barrier(&b1, [actor_id]);
1403        barrier_test_env.flush_all_events().await;
1404
1405        let input = Executor::new(
1406            Default::default(),
1407            ReceiverExecutor::for_test(
1408                actor_id,
1409                rx,
1410                barrier_test_env.local_barrier_manager.clone(),
1411            )
1412            .boxed(),
1413        );
1414
1415        let (new_output_request_tx, new_output_request_rx) = unbounded_channel();
1416        let mut rxs = [untouched, old, new, old_simple, new_simple]
1417            .into_iter()
1418            .map(|id| {
1419                (id, {
1420                    let (tx, rx) = channel_for_test();
1421                    new_output_request_tx
1422                        .send((id, NewOutputRequest::Local(tx)))
1423                        .unwrap();
1424                    rx
1425                })
1426            })
1427            .collect::<HashMap<_, _>>();
1428        let executor = Box::new(
1429            DispatchExecutor::new(
1430                input,
1431                new_output_request_rx,
1432                vec![broadcast_dispatcher, simple_dispatcher],
1433                &ActorContext::for_test(actor_id),
1434            )
1435            .await
1436            .unwrap(),
1437        )
1438        .execute();
1439
1440        pin_mut!(executor);
1441
1442        macro_rules! try_recv {
1443            ($down_id:expr) => {
1444                rxs.get_mut(&$down_id).unwrap().try_recv()
1445            };
1446        }
1447
1448        // 3. Send a chunk.
1449        tx.send(Message::Chunk(StreamChunk::default()).into())
1450            .await
1451            .unwrap();
1452
1453        tx.send(Message::Barrier(b1.clone().into_dispatcher()).into())
1454            .await
1455            .unwrap();
1456        executor.next().await.unwrap().unwrap();
1457
1458        // 5. Check downstream.
1459        try_recv!(untouched).unwrap().as_chunk().unwrap();
1460        try_recv!(untouched).unwrap().as_barrier_batch().unwrap();
1461
1462        try_recv!(old).unwrap().as_chunk().unwrap();
1463        try_recv!(old).unwrap().as_barrier_batch().unwrap(); // It should still receive the barrier even if it's to be removed.
1464
1465        try_recv!(new).unwrap().as_barrier_batch().unwrap(); // Since it's just added, it won't receive the chunk.
1466
1467        try_recv!(old_simple).unwrap().as_chunk().unwrap();
1468        try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); // Untouched.
1469
1470        // 6. Send another barrier.
1471        let b2 = Barrier::new_test_barrier(test_epoch(2));
1472        barrier_test_env.inject_barrier(&b2, [actor_id]);
1473        tx.send(Message::Barrier(b2.into_dispatcher()).into())
1474            .await
1475            .unwrap();
1476        executor.next().await.unwrap().unwrap();
1477
1478        // 7. Check downstream.
1479        try_recv!(untouched).unwrap().as_barrier_batch().unwrap();
1480        try_recv!(old).unwrap_err(); // Since it's stopped, we can't receive the new messages.
1481        try_recv!(new).unwrap().as_barrier_batch().unwrap();
1482
1483        try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); // Untouched.
1484        try_recv!(new_simple).unwrap_err(); // Untouched.
1485
1486        // 8. Send another chunk.
1487        tx.send(Message::Chunk(StreamChunk::default()).into())
1488            .await
1489            .unwrap();
1490
1491        // 9. Send a configuration change barrier for simple dispatcher.
1492        let dispatcher_updates = maplit::hashmap! {
1493            actor_id => vec![PbDispatcherUpdate {
1494                actor_id,
1495                dispatcher_id: simple_dispatcher_id,
1496                added_downstream_actor_id: vec![new_simple],
1497                removed_downstream_actor_id: vec![old_simple],
1498                hash_mapping: Default::default(),
1499            }]
1500        };
1501        let b3 = Barrier::new_test_barrier(test_epoch(3)).with_mutation(Mutation::Update(
1502            UpdateMutation {
1503                dispatchers: dispatcher_updates,
1504                ..Default::default()
1505            },
1506        ));
1507        barrier_test_env.inject_barrier(&b3, [actor_id]);
1508        tx.send(Message::Barrier(b3.into_dispatcher()).into())
1509            .await
1510            .unwrap();
1511        executor.next().await.unwrap().unwrap();
1512
1513        // 10. Check downstream.
1514        try_recv!(old_simple).unwrap().as_chunk().unwrap();
1515        try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); // It should still receive the barrier even if it's to be removed.
1516
1517        try_recv!(new_simple).unwrap().as_barrier_batch().unwrap(); // Since it's just added, it won't receive the chunk.
1518
1519        // 11. Send another barrier.
1520        let b4 = Barrier::new_test_barrier(test_epoch(4));
1521        barrier_test_env.inject_barrier(&b4, [actor_id]);
1522        tx.send(Message::Barrier(b4.into_dispatcher()).into())
1523            .await
1524            .unwrap();
1525        executor.next().await.unwrap().unwrap();
1526
1527        // 12. Check downstream.
1528        try_recv!(old_simple).unwrap_err(); // Since it's stopped, we can't receive the new messages.
1529        try_recv!(new_simple).unwrap().as_barrier_batch().unwrap();
1530    }
1531
1532    #[tokio::test]
1533    async fn test_hash_dispatcher() {
1534        // This test only works when vnode count is 256.
1535        assert_eq!(VirtualNode::COUNT_FOR_TEST, 256);
1536
1537        let num_outputs = 5; // actor id ranges from 1 to 5
1538        let cardinality = 10;
1539        let dimension = 4;
1540        let key_indices = &[0, 2];
1541        let (output_tx_vecs, output_rx_vecs): (Vec<_>, Vec<_>) =
1542            (0..num_outputs).map(|_| channel_for_test()).collect();
1543        let outputs = output_tx_vecs
1544            .into_iter()
1545            .enumerate()
1546            .map(|(actor_id, tx)| Output::new(ActorId::new(1 + actor_id as u32), tx))
1547            .collect::<Vec<_>>();
1548        let mut hash_mapping = (1..num_outputs + 1)
1549            .flat_map(|id| vec![ActorId::new(id as _); VirtualNode::COUNT_FOR_TEST / num_outputs])
1550            .collect_vec();
1551        hash_mapping.resize(
1552            VirtualNode::COUNT_FOR_TEST,
1553            ActorId::new(num_outputs as u32),
1554        );
1555        let mut hash_dispatcher = HashDataDispatcher::new(
1556            outputs,
1557            key_indices.to_vec(),
1558            DispatchOutputMapping::Simple((0..dimension).collect()),
1559            hash_mapping.clone(),
1560            0.into(),
1561        );
1562
1563        let mut ops = Vec::new();
1564        for idx in 0..cardinality {
1565            if idx % 2 == 0 {
1566                ops.push(Op::Insert);
1567            } else {
1568                ops.push(Op::Delete);
1569            }
1570        }
1571
1572        let mut start = 19260817i32..;
1573        let mut builders = (0..dimension)
1574            .map(|_| I32ArrayBuilder::new(cardinality))
1575            .collect_vec();
1576        let mut output_cols = vec![vec![vec![]; dimension]; num_outputs];
1577        let mut output_ops = vec![vec![]; num_outputs];
1578        for op in &ops {
1579            let hash_builder = Crc32FastBuilder;
1580            let mut hasher = hash_builder.build_hasher();
1581            let one_row = (0..dimension).map(|_| start.next().unwrap()).collect_vec();
1582            for key_idx in key_indices {
1583                let val = one_row[*key_idx];
1584                let bytes = val.to_le_bytes();
1585                hasher.update(&bytes);
1586            }
1587            let output_idx = hash_mapping[hasher.finish() as usize % VirtualNode::COUNT_FOR_TEST]
1588                .as_raw_id() as usize
1589                - 1;
1590            for (builder, val) in builders.iter_mut().zip_eq_fast(one_row.iter()) {
1591                builder.append(Some(*val));
1592            }
1593            output_cols[output_idx]
1594                .iter_mut()
1595                .zip_eq_fast(one_row.iter())
1596                .for_each(|(each_column, val)| each_column.push(*val));
1597            output_ops[output_idx].push(op);
1598        }
1599
1600        let columns = builders
1601            .into_iter()
1602            .map(|builder| {
1603                let array = builder.finish();
1604                array.into_ref()
1605            })
1606            .collect();
1607
1608        let chunk = StreamChunk::new(ops, columns);
1609        hash_dispatcher.dispatch_data(chunk).await.unwrap();
1610
1611        for (output_idx, mut rx) in output_rx_vecs.into_iter().enumerate() {
1612            let mut output = vec![];
1613            while let Some(Some(msg)) = rx.recv().now_or_never() {
1614                output.push(msg);
1615            }
1616            // It is possible that there is no chunks, as a key doesn't belong to any hash bucket.
1617            assert!(output.len() <= 1);
1618            if output.is_empty() {
1619                assert!(output_cols[output_idx].iter().all(|x| { x.is_empty() }));
1620            } else {
1621                let message = output.first().unwrap();
1622                let real_chunk = match message {
1623                    DispatcherMessageBatch::Chunk(chunk) => chunk,
1624                    _ => panic!(),
1625                };
1626                real_chunk
1627                    .columns()
1628                    .iter()
1629                    .zip_eq_fast(output_cols[output_idx].iter())
1630                    .for_each(|(real_col, expect_col)| {
1631                        let real_vals = real_chunk
1632                            .visibility()
1633                            .iter_ones()
1634                            .map(|row_idx| real_col.as_int32().value_at(row_idx).unwrap())
1635                            .collect::<Vec<_>>();
1636                        assert_eq!(real_vals.len(), expect_col.len());
1637                        assert_eq!(real_vals, *expect_col);
1638                    });
1639            }
1640        }
1641    }
1642
1643    #[tokio::test]
1644    async fn test_hash_dispatcher_non_adjacent_update_after_project() {
1645        // This test only works when vnode count is 256.
1646        assert_eq!(VirtualNode::COUNT_FOR_TEST, 256);
1647
1648        let schema = Schema {
1649            fields: vec![
1650                Field::unnamed(DataType::Int64),
1651                Field::unnamed(DataType::Int64),
1652            ],
1653        };
1654        let (mut tx, source) = MockSource::channel();
1655        let source = source.into_executor(schema, vec![0]);
1656
1657        let proj = ProjectExecutor::new(
1658            ActorContext::for_test(123),
1659            source,
1660            vec![
1661                NonStrictExpression::for_test(InputRefExpression::new(DataType::Int64, 0)),
1662                NonStrictExpression::for_test(InputRefExpression::new(DataType::Int64, 1)),
1663            ],
1664            MultiMap::new(),
1665            vec![],
1666            true,
1667        );
1668        let mut proj = proj.boxed().execute();
1669
1670        tx.push_barrier(test_epoch(1), false);
1671        proj.expect_barrier().await;
1672
1673        tx.push_chunk(StreamChunk::from_pretty(
1674            "  I I
1675            U- 1 1
1676            U+ 1 2
1677            U- 1 2
1678            U+ 1 3",
1679        ));
1680
1681        let projected = proj.expect_chunk().await;
1682        assert_eq!(
1683            projected,
1684            StreamChunk::from_pretty(
1685                "  I I
1686                - 1 1
1687                U+ 1 2 D
1688                U- 1 2 D
1689                + 1 3",
1690            )
1691        );
1692
1693        let (output_tx, _output_rx) = channel_for_test();
1694        let outputs = vec![Output::new(ActorId::new(1), output_tx)];
1695        let hash_mapping = vec![ActorId::new(1); VirtualNode::COUNT_FOR_TEST];
1696        let mut hash_dispatcher = HashDataDispatcher::new(
1697            outputs,
1698            vec![0],
1699            DispatchOutputMapping::Simple(vec![0]),
1700            hash_mapping,
1701            0.into(),
1702        );
1703
1704        hash_dispatcher.dispatch_data(projected).await.unwrap();
1705    }
1706
1707    #[tokio::test]
1708    async fn test_hash_dispatcher_missing_update_delete_after_project() {
1709        let schema = Schema {
1710            fields: vec![
1711                Field::unnamed(DataType::Int64),
1712                Field::unnamed(DataType::Int64),
1713            ],
1714        };
1715        let (mut tx, source) = MockSource::channel();
1716        let source = source.into_executor(schema, vec![0]);
1717
1718        let proj = ProjectExecutor::new(
1719            ActorContext::for_test(123),
1720            source,
1721            vec![NonStrictExpression::for_test(InputRefExpression::new(
1722                DataType::Int64,
1723                0,
1724            ))],
1725            MultiMap::new(),
1726            vec![],
1727            true,
1728        );
1729        let mut proj = proj.boxed().execute();
1730
1731        tx.push_barrier(test_epoch(1), false);
1732        proj.expect_barrier().await;
1733
1734        tx.push_chunk(StreamChunk::from_pretty(
1735            "  I I
1736            + 1 10
1737            U- 1 10
1738            U+ 1 30",
1739        ));
1740
1741        let projected = proj.expect_chunk().await;
1742        assert_eq!(
1743            projected,
1744            StreamChunk::from_pretty(
1745                "  I
1746                + 1 D
1747                U- 1 D
1748                + 1",
1749            )
1750        );
1751
1752        let (output_tx, _output_rx) = channel_for_test();
1753        let outputs = vec![Output::new(ActorId::new(1), output_tx)];
1754        let hash_mapping = vec![ActorId::new(1); VirtualNode::COUNT_FOR_TEST];
1755        let mut hash_dispatcher = HashDataDispatcher::new(
1756            outputs,
1757            vec![0],
1758            DispatchOutputMapping::Simple(vec![0]),
1759            hash_mapping,
1760            0.into(),
1761        );
1762
1763        hash_dispatcher.dispatch_data(projected).await.unwrap();
1764    }
1765
1766    #[tokio::test]
1767    async fn test_hash_dispatcher_internal_projection_normalize_single_u_plus() {
1768        let (output_tx, mut output_rx) = channel_for_test();
1769        let outputs = vec![Output::new(ActorId::new(1), output_tx)];
1770        let hash_mapping = vec![ActorId::new(1); VirtualNode::COUNT_FOR_TEST];
1771        let mut hash_dispatcher = HashDataDispatcher::new(
1772            outputs,
1773            vec![0],
1774            DispatchOutputMapping::Simple(vec![0]),
1775            hash_mapping,
1776            0.into(),
1777        );
1778
1779        let input = StreamChunk::from_pretty(
1780            "  I I
1781            + 1 10
1782            U- 1 10
1783            U+ 1 30",
1784        );
1785
1786        hash_dispatcher.dispatch_data(input).await.unwrap();
1787
1788        let output = output_rx.recv().await.unwrap();
1789        assert_eq!(
1790            *output.as_chunk().unwrap(),
1791            StreamChunk::from_pretty(
1792                "  I
1793                + 1 D
1794                U- 1 D
1795                + 1",
1796            )
1797        );
1798    }
1799}