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