1use std::collections::VecDeque;
16use std::pin::Pin;
17use std::task::{Context, Poll};
18
19use risingwave_common::array::StreamChunkBuilder;
20use tokio::sync::mpsc;
21
22use super::exchange::input::BoxedActorInput;
23use super::*;
24use crate::executor::prelude::*;
25use crate::task::LocalBarrierManager;
26
27pub type SelectReceivers = DynamicReceivers<ActorId, ()>;
28
29pub type MergeUpstream = BufferChunks<SelectReceivers>;
30pub type SingletonUpstream = BoxedActorInput;
31
32pub(crate) enum MergeExecutorUpstream {
33 Singleton(SingletonUpstream),
34 Merge(MergeUpstream),
35}
36
37pub(crate) struct MergeExecutorInput {
38 upstream: MergeExecutorUpstream,
39 actor_context: ActorContextRef,
40 upstream_fragment_id: UpstreamFragmentId,
41 local_barrier_manager: LocalBarrierManager,
42 executor_stats: Arc<StreamingMetrics>,
43 pub(crate) info: ExecutorInfo,
44}
45
46impl MergeExecutorInput {
47 pub(crate) fn new(
48 upstream: MergeExecutorUpstream,
49 actor_context: ActorContextRef,
50 upstream_fragment_id: UpstreamFragmentId,
51 local_barrier_manager: LocalBarrierManager,
52 executor_stats: Arc<StreamingMetrics>,
53 info: ExecutorInfo,
54 ) -> Self {
55 Self {
56 upstream,
57 actor_context,
58 upstream_fragment_id,
59 local_barrier_manager,
60 executor_stats,
61 info,
62 }
63 }
64
65 pub(crate) fn into_executor(self, barrier_rx: mpsc::UnboundedReceiver<Barrier>) -> Executor {
66 let fragment_id = self.actor_context.fragment_id;
67 let executor = match self.upstream {
68 MergeExecutorUpstream::Singleton(input) => ReceiverExecutor::new(
69 self.actor_context,
70 fragment_id,
71 self.upstream_fragment_id,
72 input,
73 self.local_barrier_manager,
74 self.executor_stats,
75 barrier_rx,
76 )
77 .boxed(),
78 MergeExecutorUpstream::Merge(inputs) => MergeExecutor::new(
79 self.actor_context,
80 fragment_id,
81 self.upstream_fragment_id,
82 inputs,
83 self.local_barrier_manager,
84 self.executor_stats,
85 barrier_rx,
86 )
87 .boxed(),
88 };
89 (self.info, executor).into()
90 }
91}
92
93impl Stream for MergeExecutorInput {
94 type Item = DispatcherMessageStreamItem;
95
96 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
97 match &mut self.get_mut().upstream {
98 MergeExecutorUpstream::Singleton(input) => input.poll_next_unpin(cx),
99 MergeExecutorUpstream::Merge(inputs) => inputs.poll_next_unpin(cx),
100 }
101 }
102}
103
104mod upstream {
105 use super::*;
106
107 pub trait Upstream:
110 Stream<Item = StreamExecutorResult<DispatcherMessage>> + Unpin + Send + 'static
111 {
112 fn upstream_input_ids(&self) -> impl Iterator<Item = ActorId> + '_;
113 fn is_empty(&self) -> bool;
114 fn flush_buffered_watermarks(&mut self);
115 fn update(&mut self, to_add: Vec<BoxedActorInput>, to_remove: &HashSet<ActorId>);
116 }
117
118 impl Upstream for MergeUpstream {
119 fn upstream_input_ids(&self) -> impl Iterator<Item = ActorId> + '_ {
120 self.inner.upstream_input_ids()
121 }
122
123 fn is_empty(&self) -> bool {
124 self.inner.is_empty()
125 }
126
127 fn flush_buffered_watermarks(&mut self) {
128 self.inner.flush_buffered_watermarks();
129 }
130
131 fn update(&mut self, to_add: Vec<BoxedActorInput>, to_remove: &HashSet<ActorId>) {
132 if !to_add.is_empty() {
133 self.inner.add_upstreams_from(to_add);
134 }
135 if !to_remove.is_empty() {
136 self.inner.remove_upstreams(to_remove);
137 }
138 }
139 }
140
141 impl Upstream for SingletonUpstream {
142 fn upstream_input_ids(&self) -> impl Iterator<Item = ActorId> + '_ {
143 std::iter::once(self.id())
144 }
145
146 fn is_empty(&self) -> bool {
147 false
148 }
149
150 fn flush_buffered_watermarks(&mut self) {
151 }
153
154 fn update(&mut self, to_add: Vec<BoxedActorInput>, to_remove: &HashSet<ActorId>) {
155 assert_eq!(
156 to_remove,
157 &HashSet::from_iter([self.id()]),
158 "the removed upstream actor should be the same as the current input"
159 );
160
161 *self = Itertools::exactly_one(to_add.into_iter())
163 .expect("receiver should have exactly one new upstream");
164 }
165 }
166}
167use upstream::Upstream;
168
169pub struct MergeExecutorInner<U> {
171 actor_context: ActorContextRef,
173
174 upstream: U,
176
177 fragment_id: FragmentId,
179
180 upstream_fragment_id: FragmentId,
182
183 local_barrier_manager: LocalBarrierManager,
184
185 metrics: Arc<StreamingMetrics>,
187
188 barrier_rx: mpsc::UnboundedReceiver<Barrier>,
189}
190
191impl<U> MergeExecutorInner<U> {
192 pub fn new(
193 ctx: ActorContextRef,
194 fragment_id: FragmentId,
195 upstream_fragment_id: FragmentId,
196 upstream: U,
197 local_barrier_manager: LocalBarrierManager,
198 metrics: Arc<StreamingMetrics>,
199 barrier_rx: mpsc::UnboundedReceiver<Barrier>,
200 ) -> Self {
201 Self {
202 actor_context: ctx,
203 upstream,
204 fragment_id,
205 upstream_fragment_id,
206 local_barrier_manager,
207 metrics,
208 barrier_rx,
209 }
210 }
211}
212
213pub type MergeExecutor = MergeExecutorInner<MergeUpstream>;
215
216impl MergeExecutor {
217 #[cfg(test)]
218 pub fn for_test(
219 actor_id: impl Into<ActorId>,
220 inputs: Vec<super::exchange::permit::Receiver>,
221 local_barrier_manager: crate::task::LocalBarrierManager,
222 schema: Schema,
223 chunk_size: usize,
224 barrier_rx: Option<mpsc::UnboundedReceiver<Barrier>>,
225 ) -> Self {
226 let actor_id = actor_id.into();
227 use super::exchange::input::LocalInput;
228 use crate::executor::exchange::input::ActorInput;
229
230 let barrier_rx =
231 barrier_rx.unwrap_or_else(|| local_barrier_manager.subscribe_barrier(actor_id));
232
233 let metrics = StreamingMetrics::unused();
234 let actor_ctx = ActorContext::for_test(actor_id);
235 let upstream = Self::new_merge_upstream(
236 inputs
237 .into_iter()
238 .enumerate()
239 .map(|(idx, input)| LocalInput::new(input, ActorId::new(idx as u32)).boxed_input())
240 .collect(),
241 &metrics,
242 &actor_ctx,
243 chunk_size,
244 schema,
245 );
246
247 Self::new(
248 actor_ctx,
249 514.into(),
250 1919.into(),
251 upstream,
252 local_barrier_manager,
253 metrics.into(),
254 barrier_rx,
255 )
256 }
257
258 pub(crate) fn new_merge_upstream(
259 upstreams: Vec<BoxedActorInput>,
260 metrics: &StreamingMetrics,
261 actor_context: &ActorContext,
262 chunk_size: usize,
263 schema: Schema,
264 ) -> MergeUpstream {
265 let merge_barrier_align_duration = Some(
266 metrics
267 .merge_barrier_align_duration
268 .with_guarded_label_values(&[
269 &actor_context.id.to_string(),
270 &actor_context.fragment_id.to_string(),
271 ]),
272 );
273
274 BufferChunks::new(
275 SelectReceivers::new(upstreams, None, merge_barrier_align_duration),
277 chunk_size,
278 schema,
279 )
280 }
281}
282
283impl<U> MergeExecutorInner<U>
284where
285 U: Upstream,
286{
287 #[try_stream(ok = Message, error = StreamExecutorError)]
288 pub(crate) async fn execute_inner(mut self: Box<Self>) {
289 let mut upstream = self.upstream;
290 let actor_id = self.actor_context.id;
291
292 let mut metrics = self.metrics.new_actor_input_metrics(
293 actor_id,
294 self.fragment_id,
295 self.upstream_fragment_id,
296 );
297
298 let mut barrier_buffer = DispatchBarrierBuffer::new(
299 self.barrier_rx,
300 actor_id,
301 self.upstream_fragment_id,
302 self.local_barrier_manager,
303 self.metrics.clone(),
304 self.fragment_id,
305 self.actor_context.config.clone(),
306 );
307
308 loop {
309 let upstream_is_empty = upstream.is_empty();
310 let msg = barrier_buffer
311 .await_next_message(&mut upstream, &metrics, upstream_is_empty)
312 .await?;
313 let msg = match msg {
314 DispatcherMessage::Watermark(watermark) => Message::Watermark(watermark),
315 DispatcherMessage::Chunk(chunk) => {
316 metrics.actor_in_record_cnt.inc_by(chunk.cardinality() as _);
317 Message::Chunk(chunk)
318 }
319 DispatcherMessage::Barrier(barrier) => {
320 let (barrier, new_inputs) =
321 barrier_buffer.pop_barrier_with_inputs(barrier).await?;
322
323 if let Some(Mutation::Update(UpdateMutation { dispatchers, .. })) =
324 barrier.mutation.as_deref()
325 && upstream
326 .upstream_input_ids()
327 .any(|actor_id| dispatchers.contains_key(&actor_id))
328 {
329 upstream.flush_buffered_watermarks();
331 }
332
333 if let Some(update) =
334 barrier.as_update_merge(self.actor_context.id, self.upstream_fragment_id)
335 {
336 let new_upstream_fragment_id = update
337 .new_upstream_fragment_id
338 .unwrap_or(self.upstream_fragment_id);
339 let removed_upstream_actor_id: HashSet<_> =
340 if update.new_upstream_fragment_id.is_some() {
341 upstream.upstream_input_ids().collect()
342 } else {
343 update.removed_upstream_actor_id.iter().copied().collect()
344 };
345
346 upstream.flush_buffered_watermarks();
348
349 upstream.update(new_inputs.unwrap_or_default(), &removed_upstream_actor_id);
351
352 self.upstream_fragment_id = new_upstream_fragment_id;
353 metrics = self.metrics.new_actor_input_metrics(
354 actor_id,
355 self.fragment_id,
356 self.upstream_fragment_id,
357 );
358 }
359
360 let is_stop = barrier.is_stop(actor_id);
361 let msg = Message::Barrier(barrier);
362 if is_stop {
363 yield msg;
364 break;
365 }
366
367 msg
368 }
369 };
370
371 yield msg;
372 }
373 }
374}
375
376impl<U> Execute for MergeExecutorInner<U>
377where
378 U: Upstream,
379{
380 fn execute(self: Box<Self>) -> BoxedMessageStream {
381 self.execute_inner().boxed()
382 }
383}
384
385pub struct BufferChunks<S: Stream> {
389 inner: S,
390 chunk_builder: StreamChunkBuilder,
391
392 pending_items: VecDeque<S::Item>,
394}
395
396impl<S: Stream> BufferChunks<S> {
397 pub(super) fn new(inner: S, chunk_size: usize, schema: Schema) -> Self {
398 assert!(chunk_size > 0);
399 let chunk_builder = StreamChunkBuilder::new(chunk_size, schema.data_types());
400 Self {
401 inner,
402 chunk_builder,
403 pending_items: VecDeque::new(),
404 }
405 }
406}
407
408impl<S: Stream> std::ops::Deref for BufferChunks<S> {
409 type Target = S;
410
411 fn deref(&self) -> &Self::Target {
412 &self.inner
413 }
414}
415
416impl<S: Stream> std::ops::DerefMut for BufferChunks<S> {
417 fn deref_mut(&mut self) -> &mut Self::Target {
418 &mut self.inner
419 }
420}
421
422impl<S: Stream> Stream for BufferChunks<S>
423where
424 S: Stream<Item = DispatcherMessageStreamItem> + Unpin,
425{
426 type Item = S::Item;
427
428 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
429 loop {
430 if let Some(item) = self.pending_items.pop_front() {
431 return Poll::Ready(Some(item));
432 }
433
434 match self.inner.poll_next_unpin(cx) {
435 Poll::Pending => {
436 return if let Some(chunk_out) = self.chunk_builder.take() {
437 Poll::Ready(Some(Ok(MessageInner::Chunk(chunk_out))))
438 } else {
439 Poll::Pending
440 };
441 }
442
443 Poll::Ready(Some(result)) => {
444 if let Ok(MessageInner::Chunk(chunk)) = result {
445 for row in chunk.records() {
446 if let Some(chunk_out) = self.chunk_builder.append_record(row) {
447 self.pending_items
448 .push_back(Ok(MessageInner::Chunk(chunk_out)));
449 }
450 }
451 } else {
452 return if let Some(chunk_out) = self.chunk_builder.take() {
453 self.pending_items.push_back(result);
454 Poll::Ready(Some(Ok(MessageInner::Chunk(chunk_out))))
455 } else {
456 Poll::Ready(Some(result))
457 };
458 }
459 }
460
461 Poll::Ready(None) => {
462 return if let Some(chunk_out) = self.chunk_builder.take() {
463 Poll::Ready(Some(Ok(MessageInner::Chunk(chunk_out))))
464 } else {
465 Poll::Pending
466 };
467 }
468 }
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use std::sync::atomic::{AtomicBool, Ordering};
476 use std::task::Poll;
477 use std::time::Duration;
478
479 use assert_matches::assert_matches;
480 use futures::future::try_join_all;
481 use futures::{FutureExt, poll};
482 use risingwave_common::array::Op;
483 use risingwave_common::util::epoch::test_epoch;
484 use risingwave_pb::task_service::stream_exchange_service_server::{
485 StreamExchangeService, StreamExchangeServiceServer,
486 };
487 use risingwave_pb::task_service::{GetStreamRequest, GetStreamResponse, PbPermits};
488 use tokio::time::sleep;
489 use tokio_stream::wrappers::ReceiverStream;
490 use tonic::{Request, Response, Status, Streaming};
491
492 use super::*;
493 use crate::executor::exchange::input::{ActorInput, LocalInput, RemoteInput};
494 use crate::executor::exchange::permit::channel_for_test;
495 use crate::executor::{BarrierInner as Barrier, MessageInner as Message};
496 use crate::task::barrier_test_utils::LocalBarrierTestEnv;
497 use crate::task::test_utils::helper_make_local_actor;
498 use crate::task::{NewOutputRequest, TEST_PARTIAL_GRAPH_ID};
499
500 fn build_test_chunk(size: u64) -> StreamChunk {
501 let ops = vec![Op::Insert; size as usize];
502 StreamChunk::new(ops, vec![])
503 }
504
505 #[tokio::test]
506 async fn test_buffer_chunks() {
507 let test_env = LocalBarrierTestEnv::for_test().await;
508
509 let (tx, rx) = channel_for_test();
510 let input = LocalInput::new(rx, 1.into()).boxed_input();
511 let mut buffer = BufferChunks::new(input, 100, Schema::new(vec![]));
512
513 tx.send(Message::Chunk(build_test_chunk(10)).into())
515 .await
516 .unwrap();
517 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
518 assert_eq!(chunk.ops().len() as u64, 10);
519 });
520
521 tx.send(Message::Chunk(build_test_chunk(10)).into())
523 .await
524 .unwrap();
525 tx.send(Message::Chunk(build_test_chunk(10)).into())
526 .await
527 .unwrap();
528 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
529 assert_eq!(chunk.ops().len() as u64, 20);
530 });
531
532 tx.send(
534 Message::Watermark(Watermark {
535 col_idx: 0,
536 data_type: DataType::Int64,
537 val: ScalarImpl::Int64(233),
538 })
539 .into(),
540 )
541 .await
542 .unwrap();
543 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Watermark(watermark) => {
544 assert_eq!(watermark.val, ScalarImpl::Int64(233));
545 });
546
547 tx.send(Message::Chunk(build_test_chunk(10)).into())
549 .await
550 .unwrap();
551 tx.send(Message::Chunk(build_test_chunk(10)).into())
552 .await
553 .unwrap();
554 tx.send(
555 Message::Watermark(Watermark {
556 col_idx: 0,
557 data_type: DataType::Int64,
558 val: ScalarImpl::Int64(233),
559 })
560 .into(),
561 )
562 .await
563 .unwrap();
564 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
565 assert_eq!(chunk.ops().len() as u64, 20);
566 });
567 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Watermark(watermark) => {
568 assert_eq!(watermark.val, ScalarImpl::Int64(233));
569 });
570
571 let barrier = Barrier::new_test_barrier(test_epoch(1));
573 test_env.inject_barrier(&barrier, [2.into()]);
574 tx.send(Message::Barrier(barrier.clone().into_dispatcher()).into())
575 .await
576 .unwrap();
577 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Barrier(Barrier { epoch: barrier_epoch, .. }) => {
578 assert_eq!(barrier_epoch.curr, test_epoch(1));
579 });
580
581 tx.send(Message::Chunk(build_test_chunk(10)).into())
583 .await
584 .unwrap();
585 tx.send(Message::Chunk(build_test_chunk(10)).into())
586 .await
587 .unwrap();
588 let barrier = Barrier::new_test_barrier(test_epoch(2));
589 test_env.inject_barrier(&barrier, [2.into()]);
590 tx.send(Message::Barrier(barrier.clone().into_dispatcher()).into())
591 .await
592 .unwrap();
593 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
594 assert_eq!(chunk.ops().len() as u64, 20);
595 });
596 assert_matches!(buffer.next().await.unwrap().unwrap(), Message::Barrier(Barrier { epoch: barrier_epoch, .. }) => {
597 assert_eq!(barrier_epoch.curr, test_epoch(2));
598 });
599 }
600
601 #[tokio::test]
602 async fn test_merger() {
603 const CHANNEL_NUMBER: usize = 10;
604 let mut txs = Vec::with_capacity(CHANNEL_NUMBER);
605 let mut rxs = Vec::with_capacity(CHANNEL_NUMBER);
606 for _i in 0..CHANNEL_NUMBER {
607 let (tx, rx) = channel_for_test();
608 txs.push(tx);
609 rxs.push(rx);
610 }
611 let barrier_test_env = LocalBarrierTestEnv::for_test().await;
612 let actor_id = 233.into();
613 let mut handles = Vec::with_capacity(CHANNEL_NUMBER);
614
615 let epochs = (10..1000u64)
616 .step_by(10)
617 .map(|idx| (idx, test_epoch(idx)))
618 .collect_vec();
619 let mut prev_epoch = 0;
620 let prev_epoch = &mut prev_epoch;
621 let barriers: HashMap<_, _> = epochs
622 .iter()
623 .map(|(_, epoch)| {
624 let barrier = Barrier::with_prev_epoch_for_test(*epoch, *prev_epoch);
625 *prev_epoch = *epoch;
626 barrier_test_env.inject_barrier(&barrier, [actor_id]);
627 (*epoch, barrier)
628 })
629 .collect();
630 let b2 = Barrier::with_prev_epoch_for_test(test_epoch(1000), *prev_epoch)
631 .with_mutation(Mutation::Stop(StopMutation::default()));
632 barrier_test_env.inject_barrier(&b2, [actor_id]);
633 barrier_test_env.flush_all_events().await;
634
635 for (tx_id, tx) in txs.into_iter().enumerate() {
636 let epochs = epochs.clone();
637 let barriers = barriers.clone();
638 let b2 = b2.clone();
639 let handle = tokio::spawn(async move {
640 for (idx, epoch) in epochs {
641 if idx % 20 == 0 {
642 tx.send(Message::Chunk(build_test_chunk(10)).into())
643 .await
644 .unwrap();
645 } else {
646 tx.send(
647 Message::Watermark(Watermark {
648 col_idx: (idx as usize / 20 + tx_id) % CHANNEL_NUMBER,
649 data_type: DataType::Int64,
650 val: ScalarImpl::Int64(idx as i64),
651 })
652 .into(),
653 )
654 .await
655 .unwrap();
656 }
657 tx.send(Message::Barrier(barriers[&epoch].clone().into_dispatcher()).into())
658 .await
659 .unwrap();
660 sleep(Duration::from_millis(1)).await;
661 }
662 tx.send(Message::Barrier(b2.clone().into_dispatcher()).into())
663 .await
664 .unwrap();
665 });
666 handles.push(handle);
667 }
668
669 let merger = MergeExecutor::for_test(
670 actor_id,
671 rxs,
672 barrier_test_env.local_barrier_manager.clone(),
673 Schema::new(vec![]),
674 100,
675 None,
676 );
677 let mut merger = merger.boxed().execute();
678 for (idx, epoch) in epochs {
679 if idx % 20 == 0 {
680 let mut count = 0usize;
682 while count < 100 {
683 assert_matches!(merger.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
684 count += chunk.ops().len();
685 });
686 }
687 assert_eq!(count, 100);
688 } else if idx as usize / 20 >= CHANNEL_NUMBER - 1 {
689 for _ in 0..CHANNEL_NUMBER {
691 assert_matches!(merger.next().await.unwrap().unwrap(), Message::Watermark(watermark) => {
692 assert_eq!(watermark.val, ScalarImpl::Int64((idx - 20 * (CHANNEL_NUMBER as u64 - 1)) as i64));
693 });
694 }
695 }
696 assert_matches!(merger.next().await.unwrap().unwrap(), Message::Barrier(Barrier{epoch:barrier_epoch,..}) => {
698 assert_eq!(barrier_epoch.curr, epoch);
699 });
700 }
701 assert_matches!(
702 merger.next().await.unwrap().unwrap(),
703 Message::Barrier(Barrier {
704 mutation,
705 ..
706 }) if mutation.as_deref().unwrap().is_stop_mutation()
707 );
708
709 for handle in handles {
710 handle.await.unwrap();
711 }
712 }
713
714 #[tokio::test]
715 async fn empty_dynamic_merge_progresses_across_attach_detach_and_reattach() {
716 let actor_id = 999.into();
717 let resolver_1 = 1000.into();
718 let resolver_2 = 1001.into();
719 let empty_fragment = 0.into();
720 let resolver_fragment_1 = 500.into();
721 let resolver_fragment_2 = 501.into();
722 let barrier_test_env = LocalBarrierTestEnv::for_test().await;
723 let e1 = Barrier::new_test_barrier(test_epoch(1));
724 barrier_test_env.inject_barrier(&e1, [actor_id]);
725 barrier_test_env.flush_all_events().await;
726
727 let metrics = Arc::new(StreamingMetrics::unused());
728 let actor_ctx = ActorContext::for_test(actor_id);
729 let barrier_rx = barrier_test_env
730 .local_barrier_manager
731 .subscribe_barrier(actor_id);
732 let upstream = MergeExecutor::new_merge_upstream(
733 vec![],
734 &metrics,
735 &actor_ctx,
736 100,
737 Schema::empty().clone(),
738 );
739 let merge = MergeExecutor::new(
740 actor_ctx,
741 514.into(),
742 empty_fragment,
743 upstream,
744 barrier_test_env.local_barrier_manager.clone(),
745 metrics,
746 barrier_rx,
747 );
748 let mut merge = merge.boxed().execute();
749
750 macro_rules! assert_recv_pending {
751 () => {
752 assert_matches!(
753 poll!(merge.as_mut().next()),
754 Poll::Pending,
755 "an empty dynamic merge must wait for a barrier rather than terminate or busy-loop"
756 );
757 };
758 }
759
760 macro_rules! recv_barrier {
761 ($epoch:expr) => {
762 assert_matches!(
763 merge.next().await.unwrap().unwrap(),
764 Message::Barrier(Barrier { epoch, .. }) if epoch.curr == test_epoch($epoch)
765 );
766 };
767 }
768
769 async fn take_upstream_tx(
770 barrier_test_env: &LocalBarrierTestEnv,
771 upstream_actor_id: ActorId,
772 downstream_actor_id: ActorId,
773 ) -> crate::executor::exchange::permit::Sender {
774 let mut requests = barrier_test_env
775 .take_pending_new_output_requests(upstream_actor_id)
776 .await;
777 assert_eq!(requests.len(), 1);
778 let (actor_id, request) = requests.pop().unwrap();
779 assert_eq!(actor_id, downstream_actor_id);
780 let NewOutputRequest::Local(tx) = request else {
781 unreachable!()
782 };
783 tx
784 }
785
786 assert_recv_pending!();
787
788 recv_barrier!(1);
789 assert_recv_pending!();
790
791 let b1 = Barrier::new_test_barrier(test_epoch(2)).with_mutation(Mutation::Update(
792 UpdateMutation {
793 merges: maplit::hashmap! {
794 (actor_id, empty_fragment) => MergeUpdate {
795 actor_id,
796 upstream_fragment_id: empty_fragment,
797 new_upstream_fragment_id: Some(resolver_fragment_1),
798 added_upstream_actors: vec![helper_make_local_actor(resolver_1)],
799 removed_upstream_actor_id: vec![],
800 },
801 },
802 ..Default::default()
803 },
804 ));
805 barrier_test_env.inject_barrier(&b1, [actor_id]);
806 barrier_test_env.flush_all_events().await;
807 assert_recv_pending!();
808 barrier_test_env.flush_all_events().await;
809 let resolver_1_tx = take_upstream_tx(&barrier_test_env, resolver_1, actor_id).await;
810 resolver_1_tx
811 .send(Message::Barrier(b1.clone().into_dispatcher()).into())
812 .await
813 .unwrap();
814 recv_barrier!(2);
815
816 resolver_1_tx
817 .send(Message::Chunk(build_test_chunk(1)).into())
818 .await
819 .unwrap();
820 assert_matches!(merge.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
821 assert_eq!(chunk.cardinality(), 1);
822 });
823
824 let b2 = Barrier::new_test_barrier(test_epoch(3)).with_mutation(Mutation::Update(
825 UpdateMutation {
826 merges: maplit::hashmap! {
827 (actor_id, resolver_fragment_1) => MergeUpdate {
828 actor_id,
829 upstream_fragment_id: resolver_fragment_1,
830 new_upstream_fragment_id: Some(empty_fragment),
831 added_upstream_actors: vec![],
832 removed_upstream_actor_id: vec![resolver_1],
833 },
834 },
835 ..Default::default()
836 },
837 ));
838 barrier_test_env.inject_barrier(&b2, [actor_id]);
839 resolver_1_tx
840 .send(Message::Barrier(b2.clone().into_dispatcher()).into())
841 .await
842 .unwrap();
843 recv_barrier!(3);
844 assert_recv_pending!();
845
846 let e3 = Barrier::new_test_barrier(test_epoch(4));
847 barrier_test_env.inject_barrier(&e3, [actor_id]);
848 recv_barrier!(4);
849 assert_recv_pending!();
850
851 let b4 = Barrier::new_test_barrier(test_epoch(5)).with_mutation(Mutation::Update(
852 UpdateMutation {
853 merges: maplit::hashmap! {
854 (actor_id, empty_fragment) => MergeUpdate {
855 actor_id,
856 upstream_fragment_id: empty_fragment,
857 new_upstream_fragment_id: Some(resolver_fragment_2),
858 added_upstream_actors: vec![helper_make_local_actor(resolver_2)],
859 removed_upstream_actor_id: vec![],
860 },
861 },
862 ..Default::default()
863 },
864 ));
865 barrier_test_env.inject_barrier(&b4, [actor_id]);
866 barrier_test_env.flush_all_events().await;
867 assert_recv_pending!();
868 barrier_test_env.flush_all_events().await;
869 let resolver_2_tx = take_upstream_tx(&barrier_test_env, resolver_2, actor_id).await;
870 resolver_2_tx
871 .send(Message::Barrier(b4.into_dispatcher()).into())
872 .await
873 .unwrap();
874 recv_barrier!(5);
875
876 resolver_2_tx
877 .send(Message::Chunk(build_test_chunk(1)).into())
878 .await
879 .unwrap();
880 assert_matches!(merge.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
881 assert_eq!(chunk.cardinality(), 1);
882 });
883 }
884
885 #[tokio::test]
886 async fn test_configuration_change() {
887 let actor_id = 233.into();
888 let (untouched, old, new) = (234.into(), 235.into(), 238.into()); let barrier_test_env = LocalBarrierTestEnv::for_test().await;
890 let metrics = Arc::new(StreamingMetrics::unused());
891
892 let (upstream_fragment_id, fragment_id) = (10.into(), 18.into());
897
898 let actor_ctx = ActorContext::for_test(actor_id);
899
900 let inputs: Vec<_> =
901 try_join_all([untouched, old].into_iter().map(async |upstream_actor_id| {
902 new_input(
903 &barrier_test_env.local_barrier_manager,
904 metrics.clone(),
905 actor_id,
906 fragment_id,
907 &helper_make_local_actor(upstream_actor_id),
908 upstream_fragment_id,
909 actor_ctx.config.clone(),
910 )
911 .await
912 }))
913 .await
914 .unwrap();
915
916 let merge_updates = maplit::hashmap! {
917 (actor_id, upstream_fragment_id) => MergeUpdate {
918 actor_id,
919 upstream_fragment_id,
920 new_upstream_fragment_id: None,
921 added_upstream_actors: vec![helper_make_local_actor(new)],
922 removed_upstream_actor_id: vec![old],
923 }
924 };
925
926 let b1 = Barrier::new_test_barrier(test_epoch(1)).with_mutation(Mutation::Update(
927 UpdateMutation {
928 merges: merge_updates,
929 ..Default::default()
930 },
931 ));
932 barrier_test_env.inject_barrier(&b1, [actor_id]);
933 barrier_test_env.flush_all_events().await;
934
935 let barrier_rx = barrier_test_env
936 .local_barrier_manager
937 .subscribe_barrier(actor_id);
938 let upstream = MergeExecutor::new_merge_upstream(
939 inputs,
940 &metrics,
941 &actor_ctx,
942 100,
943 Schema::empty().clone(),
944 );
945
946 let mut merge = MergeExecutor::new(
947 actor_ctx,
948 fragment_id,
949 upstream_fragment_id,
950 upstream,
951 barrier_test_env.local_barrier_manager.clone(),
952 metrics.clone(),
953 barrier_rx,
954 )
955 .boxed()
956 .execute();
957
958 let mut txs = HashMap::new();
959 macro_rules! send {
960 ($actors:expr, $msg:expr) => {
961 for actor in $actors {
962 txs.get(&actor).unwrap().send($msg).await.unwrap();
963 }
964 };
965 }
966
967 macro_rules! assert_recv_pending {
968 () => {
969 assert!(
970 merge
971 .next()
972 .now_or_never()
973 .flatten()
974 .transpose()
975 .unwrap()
976 .is_none()
977 );
978 };
979 }
980 macro_rules! recv {
981 () => {
982 merge.next().await.transpose().unwrap()
983 };
984 }
985
986 macro_rules! collect_upstream_tx {
987 ($actors:expr) => {
988 for upstream_id in $actors {
989 let mut output_requests = barrier_test_env
990 .take_pending_new_output_requests(upstream_id)
991 .await;
992 assert_eq!(output_requests.len(), 1);
993 let (downstream_actor_id, request) = output_requests.pop().unwrap();
994 assert_eq!(downstream_actor_id, actor_id);
995 let NewOutputRequest::Local(tx) = request else {
996 unreachable!()
997 };
998 txs.insert(upstream_id, tx);
999 }
1000 };
1001 }
1002
1003 assert_recv_pending!();
1004 barrier_test_env.flush_all_events().await;
1005
1006 collect_upstream_tx!([untouched, old]);
1008
1009 send!([untouched, old], Message::Chunk(build_test_chunk(1)).into());
1011 assert_eq!(2, recv!().unwrap().as_chunk().unwrap().cardinality()); assert_recv_pending!();
1013
1014 send!(
1015 [untouched, old],
1016 Message::Barrier(b1.clone().into_dispatcher()).into()
1017 );
1018 assert_recv_pending!(); collect_upstream_tx!([new]);
1021
1022 send!([new], Message::Barrier(b1.clone().into_dispatcher()).into());
1023 recv!().unwrap().as_barrier().unwrap(); send!([untouched, new], Message::Chunk(build_test_chunk(1)).into());
1027 assert_eq!(2, recv!().unwrap().as_chunk().unwrap().cardinality()); assert_recv_pending!();
1029 }
1030
1031 struct FakeExchangeService {
1032 rpc_called: Arc<AtomicBool>,
1033 }
1034
1035 fn exchange_client_test_barrier() -> crate::executor::Barrier {
1036 Barrier::new_test_barrier(test_epoch(1))
1037 }
1038
1039 #[async_trait::async_trait]
1040 impl StreamExchangeService for FakeExchangeService {
1041 type GetStreamStream = ReceiverStream<std::result::Result<GetStreamResponse, Status>>;
1042
1043 async fn get_stream(
1044 &self,
1045 _request: Request<Streaming<GetStreamRequest>>,
1046 ) -> std::result::Result<Response<Self::GetStreamStream>, Status> {
1047 let (tx, rx) = tokio::sync::mpsc::channel(10);
1048 self.rpc_called.store(true, Ordering::SeqCst);
1049 let stream_chunk = StreamChunk::default().to_protobuf();
1051 tx.send(Ok(GetStreamResponse {
1052 message: Some(PbStreamMessageBatch {
1053 stream_message_batch: Some(
1054 risingwave_pb::stream_plan::stream_message_batch::StreamMessageBatch::StreamChunk(
1055 stream_chunk,
1056 ),
1057 ),
1058 }),
1059 permits: Some(PbPermits::default()),
1060 }))
1061 .await
1062 .unwrap();
1063 let barrier = exchange_client_test_barrier();
1065 tx.send(Ok(GetStreamResponse {
1066 message: Some(PbStreamMessageBatch {
1067 stream_message_batch: Some(
1068 risingwave_pb::stream_plan::stream_message_batch::StreamMessageBatch::BarrierBatch(
1069 BarrierBatch {
1070 barriers: vec![barrier.to_protobuf()],
1071 },
1072 ),
1073 ),
1074 }),
1075 permits: Some(PbPermits::default()),
1076 }))
1077 .await
1078 .unwrap();
1079 Ok(Response::new(ReceiverStream::new(rx)))
1080 }
1081 }
1082
1083 #[tokio::test]
1084 async fn test_stream_exchange_client() {
1085 let rpc_called = Arc::new(AtomicBool::new(false));
1086 let server_run = Arc::new(AtomicBool::new(false));
1087 let addr = "127.0.0.1:12348".parse().unwrap();
1088
1089 let (shutdown_send, shutdown_recv) = tokio::sync::oneshot::channel();
1091 let exchange_svc = StreamExchangeServiceServer::new(FakeExchangeService {
1092 rpc_called: rpc_called.clone(),
1093 });
1094 let cp_server_run = server_run.clone();
1095 let join_handle = tokio::spawn(async move {
1096 cp_server_run.store(true, Ordering::SeqCst);
1097 tonic::transport::Server::builder()
1098 .add_service(exchange_svc)
1099 .serve_with_shutdown(addr, async move {
1100 shutdown_recv.await.unwrap();
1101 })
1102 .await
1103 .unwrap();
1104 });
1105
1106 sleep(Duration::from_secs(1)).await;
1107 assert!(server_run.load(Ordering::SeqCst));
1108
1109 let test_env = LocalBarrierTestEnv::for_test().await;
1110
1111 let remote_input = {
1112 RemoteInput::new(
1113 &test_env.local_barrier_manager,
1114 addr.into(),
1115 TEST_PARTIAL_GRAPH_ID,
1116 (0.into(), 0.into()),
1117 (0.into(), 0.into()),
1118 Arc::new(StreamingMetrics::unused()),
1119 Arc::new(StreamingConfig::default()),
1120 )
1121 .await
1122 .unwrap()
1123 };
1124
1125 test_env.inject_barrier(&exchange_client_test_barrier(), [remote_input.id()]);
1126
1127 pin_mut!(remote_input);
1128
1129 assert_matches!(remote_input.next().await.unwrap().unwrap(), Message::Chunk(chunk) => {
1130 let (ops, columns, visibility) = chunk.into_inner();
1131 assert!(ops.is_empty());
1132 assert!(columns.is_empty());
1133 assert!(visibility.is_empty());
1134 });
1135 assert_matches!(remote_input.next().await.unwrap().unwrap(), Message::Barrier(Barrier { epoch: barrier_epoch, .. }) => {
1136 assert_eq!(barrier_epoch.curr, test_epoch(1));
1137 });
1138 assert!(rpc_called.load(Ordering::SeqCst));
1139
1140 shutdown_send.send(()).unwrap();
1141 join_handle.await.unwrap();
1142 }
1143}