1use 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
59pub 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 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 match msg {
205 MessageBatch::BarrierBatch(barrier_batch) => {
206 if barrier_batch.is_empty() {
207 return Ok(());
208 }
209 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 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 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 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 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 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 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 && !dropped_actors.contains(&self.actor_id)
390 {
391 for dispatcher in &mut self.dispatchers {
392 dispatcher.remove_outputs(dropped_actors);
393 }
394 }
395
396 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 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
507async 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 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
575async 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 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 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 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 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 fn dispatch_data(&mut self, chunk: StreamChunk) -> impl DispatchFuture<'_>;
769 fn dispatch_barriers(&mut self, barrier: DispatcherBarriers) -> impl DispatchFuture<'_>;
771 fn dispatch_watermark(&mut self, watermark: Watermark) -> impl DispatchFuture<'_>;
773
774 fn add_outputs(&mut self, outputs: impl IntoIterator<Item = Output>);
776 fn remove_outputs(&mut self, actor_ids: &HashSet<ActorId>);
778
779 fn dispatcher_id(&self) -> DispatcherId;
785
786 fn is_empty(&self) -> bool;
789}
790
791async 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 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 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 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 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 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 let num_outputs = self.outputs.len();
954
955 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 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 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 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 let chunk = self.output_mapping.apply(chunk);
1016 let chunk_vis = chunk.visibility();
1018
1019 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 match (new_op, elim_op) {
1033 (Op::Delete, Op::UpdateDelete) => Op::Delete,
1035 (Op::Insert, Op::UpdateInsert) => Op::Insert,
1037 _ => elim_op,
1039 }
1040 })
1041 .collect();
1042
1043 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 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#[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 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 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#[derive(Debug)]
1177pub struct SimpleDispatcher {
1178 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 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 assert_eq!(VirtualNode::COUNT_FOR_TEST, 256);
1289
1290 let num_outputs = 2; 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()); let (old_simple, new_simple) = (114.into(), 514.into()); 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(), ..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(), ..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 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 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(); try_recv!(new).unwrap().as_barrier_batch().unwrap(); try_recv!(old_simple).unwrap().as_chunk().unwrap();
1468 try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); 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 try_recv!(untouched).unwrap().as_barrier_batch().unwrap();
1480 try_recv!(old).unwrap_err(); try_recv!(new).unwrap().as_barrier_batch().unwrap();
1482
1483 try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); try_recv!(new_simple).unwrap_err(); tx.send(Message::Chunk(StreamChunk::default()).into())
1488 .await
1489 .unwrap();
1490
1491 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 try_recv!(old_simple).unwrap().as_chunk().unwrap();
1515 try_recv!(old_simple).unwrap().as_barrier_batch().unwrap(); try_recv!(new_simple).unwrap().as_barrier_batch().unwrap(); 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 try_recv!(old_simple).unwrap_err(); try_recv!(new_simple).unwrap().as_barrier_batch().unwrap();
1530 }
1531
1532 #[tokio::test]
1533 async fn test_hash_dispatcher() {
1534 assert_eq!(VirtualNode::COUNT_FOR_TEST, 256);
1536
1537 let num_outputs = 5; 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 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 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}