1use std::collections::{HashMap, HashSet};
16use std::marker::PhantomData;
17use std::mem::take;
18
19use anyhow::anyhow;
20use risingwave_common::bitmap::Bitmap;
21use risingwave_meta_model::WorkerId;
22use risingwave_meta_model::fragment::DistributionType;
23use risingwave_pb::common::{ActorInfo, HostAddress};
24use risingwave_pb::id::{PartialGraphId, SubscriberId};
25use risingwave_pb::stream_plan::update_mutation::{DispatcherUpdate, MergeUpdate};
26use risingwave_pb::stream_plan::{AddMutation, PbDispatcher, StreamNode, UpdateMutation};
27use tracing::warn;
28
29use crate::MetaResult;
30use crate::barrier::rpc::ControlStreamManager;
31use crate::controller::fragment::InflightFragmentInfo;
32use crate::controller::utils::compose_dispatchers;
33use crate::model::{
34 ActorId, ActorNewNoShuffle, ActorUpstreams, DownstreamFragmentRelation, Fragment,
35 FragmentActorDispatchers, FragmentDownstreamRelation, FragmentId, StreamActor,
36 StreamJobActorsToCreate,
37};
38
39type ComposedEdge = (
40 HashMap<ActorId, PbDispatcher>,
41 HashMap<ActorId, ActorUpstreams>,
42 Option<HashMap<ActorId, ActorId>>,
43);
44
45#[derive(Debug)]
49struct EdgeBuilderFragmentInfo {
50 distribution_type: DistributionType,
51 actors: HashMap<ActorId, Option<Bitmap>>,
52 actor_location: HashMap<ActorId, HostAddress>,
53 partial_graph_id: PartialGraphId,
54}
55
56#[derive(Debug)]
57enum FragmentStatus {
58 Existing(EdgeBuilderFragmentInfo),
59 New(EdgeBuilderFragmentInfo),
61 Stopping(EdgeBuilderFragmentInfo),
64 Changed {
65 before: EdgeBuilderFragmentInfo,
66 after: EdgeBuilderFragmentInfo,
67 },
68}
69
70impl EdgeBuilderFragmentInfo {
71 fn from_inflight(
73 info: &InflightFragmentInfo,
74 partial_graph_id: PartialGraphId,
75 control_stream_manager: &ControlStreamManager,
76 ) -> Self {
77 let (actors, actor_location) = info
78 .actors
79 .iter()
80 .map(|(&actor_id, actor)| {
81 (
82 (actor_id, actor.vnode_bitmap.clone()),
83 (actor_id, control_stream_manager.host_addr(actor.worker_id)),
84 )
85 })
86 .unzip();
87 Self {
88 distribution_type: info.distribution_type,
89 actors,
90 actor_location,
91 partial_graph_id,
92 }
93 }
94
95 fn from_fragment(
97 fragment: &Fragment,
98 stream_actors: &HashMap<FragmentId, Vec<StreamActor>>,
99 actor_worker: &HashMap<ActorId, WorkerId>,
100 partial_graph_id: PartialGraphId,
101 control_stream_manager: &ControlStreamManager,
102 ) -> Self {
103 let (actors, actor_location) = stream_actors
104 .get(&fragment.fragment_id)
105 .into_iter()
106 .flatten()
107 .map(|actor| {
108 (
109 (actor.actor_id, actor.vnode_bitmap.clone()),
110 (
111 actor.actor_id,
112 control_stream_manager.host_addr(actor_worker[&actor.actor_id]),
113 ),
114 )
115 })
116 .unzip();
117 Self {
118 distribution_type: fragment.distribution_type.into(),
119 actors,
120 actor_location,
121 partial_graph_id,
122 }
123 }
124}
125
126#[derive(Debug)]
127pub(crate) struct FragmentEdgeBuildResult {
128 upstreams: HashMap<FragmentId, HashMap<ActorId, ActorUpstreams>>,
129 dispatchers: FragmentActorDispatchers,
130 merge_updates: HashMap<FragmentId, Vec<MergeUpdate>>,
131 dispatcher_updates: Vec<DispatcherUpdate>,
132}
133
134impl FragmentEdgeBuildResult {
135 fn validate_terminal_consumption(&self) {
136 let remaining_upstreams = self.upstreams.values().map(HashMap::len).sum::<usize>();
137 let remaining_dispatchers = self.dispatchers.values().map(HashMap::len).sum::<usize>();
138 let unapplied_dispatcher_updates = self.dispatcher_updates.len();
139 let unapplied_merge_updates = self.merge_updates.values().map(Vec::len).sum::<usize>();
140 let has_unconsumed_fields = remaining_upstreams != 0
141 || remaining_dispatchers != 0
142 || unapplied_dispatcher_updates != 0
143 || unapplied_merge_updates != 0;
144
145 debug_assert!(
146 !has_unconsumed_fields,
147 "edge result has unconsumed fields: remaining_upstreams={remaining_upstreams}, remaining_dispatchers={remaining_dispatchers}, unapplied_dispatcher_updates={unapplied_dispatcher_updates}, unapplied_merge_updates={unapplied_merge_updates}"
148 );
149 #[cfg(not(debug_assertions))]
150 if has_unconsumed_fields {
151 warn!(
152 remaining_upstreams,
153 remaining_dispatchers,
154 unapplied_dispatcher_updates,
155 unapplied_merge_updates,
156 "edge result has unconsumed fields"
157 );
158 }
159 }
160
161 pub(crate) fn apply_to_add_mutation(mut self, mutation: &mut AddMutation) {
163 let dispatchers = take(&mut self.dispatchers);
164 self.validate_terminal_consumption();
165 for (actor_id, dispatchers) in dispatchers.into_values().flatten() {
166 mutation
167 .actor_dispatchers
168 .entry(actor_id)
169 .or_default()
170 .dispatchers
171 .extend(dispatchers);
172 }
173 }
174
175 pub(crate) fn apply_to_update_mutation(mut self, mutation: &mut UpdateMutation) {
180 let upstreams = take(&mut self.upstreams);
181 let dispatchers = take(&mut self.dispatchers);
182 let dispatcher_updates = take(&mut self.dispatcher_updates);
183 let merge_updates = take(&mut self.merge_updates);
184 self.validate_terminal_consumption();
185 mutation.dispatcher_update.extend(dispatcher_updates);
186 mutation
187 .merge_update
188 .extend(merge_updates.into_values().flatten());
189 for actor_upstreams in upstreams.into_values().flat_map(HashMap::into_iter) {
190 let (actor_id, actor_upstreams) = actor_upstreams;
191 for (upstream_fragment_id, added_upstream_actors) in actor_upstreams {
192 mutation.merge_update.push(MergeUpdate {
193 actor_id,
194 upstream_fragment_id,
195 new_upstream_fragment_id: None,
196 added_upstream_actors: added_upstream_actors.into_values().collect(),
197 removed_upstream_actor_id: vec![],
198 });
199 }
200 }
201 for (actor_id, dispatchers) in dispatchers.into_values().flatten() {
202 mutation
203 .actor_new_dispatchers
204 .entry(actor_id)
205 .or_default()
206 .dispatchers
207 .extend(dispatchers);
208 }
209 }
210
211 pub(crate) fn collect_actors_to_create(
212 &mut self,
213 actors: impl Iterator<
214 Item = (
215 FragmentId,
216 &StreamNode,
217 impl Iterator<Item = (&StreamActor, WorkerId)>,
218 impl IntoIterator<Item = SubscriberId>,
219 ),
220 >,
221 ) -> StreamJobActorsToCreate {
222 let mut actors_to_create = StreamJobActorsToCreate::default();
223 for (fragment_id, node, actors, subscriber_ids) in actors {
224 let subscriber_ids: HashSet<_> = subscriber_ids.into_iter().collect();
225 for (actor, worker_id) in actors {
226 let upstreams = self
227 .upstreams
228 .get_mut(&fragment_id)
229 .and_then(|upstreams| upstreams.remove(&actor.actor_id))
230 .unwrap_or_default();
231 let dispatchers = self
232 .dispatchers
233 .get_mut(&fragment_id)
234 .and_then(|upstreams| upstreams.remove(&actor.actor_id))
235 .unwrap_or_default();
236 actors_to_create
237 .entry(worker_id)
238 .or_default()
239 .entry(fragment_id)
240 .or_insert_with(|| (node.clone(), vec![], subscriber_ids.clone()))
241 .1
242 .push((actor.clone(), upstreams, dispatchers))
243 }
244 }
245 actors_to_create
246 }
247
248 pub(crate) fn take_common_upstream_actors(
254 &mut self,
255 upstream_fragment_id: FragmentId,
256 downstream_fragment_id: FragmentId,
257 ) -> MetaResult<Vec<ActorInfo>> {
258 let downstream_actor_upstreams = self
259 .upstreams
260 .get_mut(&downstream_fragment_id)
261 .ok_or_else(|| {
262 anyhow!("cannot find upstreams for downstream fragment {downstream_fragment_id}")
263 })?;
264 let mut common_upstream_actors = None;
265 for (&downstream_actor_id, actor_upstreams) in downstream_actor_upstreams.iter_mut() {
266 let upstream_actors = actor_upstreams
267 .remove(&upstream_fragment_id)
268 .ok_or_else(|| {
269 anyhow!(
270 "cannot find upstream fragment {upstream_fragment_id} for downstream actor {downstream_actor_id}"
271 )
272 })?;
273 if let Some(common) = &common_upstream_actors {
274 if common != &upstream_actors {
278 return Err(anyhow!(
279 "upstream actors from fragment {upstream_fragment_id} are not common across downstream fragment {downstream_fragment_id}"
280 )
281 .into());
282 }
283 } else {
284 common_upstream_actors = Some(upstream_actors);
285 }
286 }
287 let common_upstream_actors = common_upstream_actors
288 .ok_or_else(|| anyhow!("downstream fragment {downstream_fragment_id} has no actors"))?;
289
290 downstream_actor_upstreams.retain(|_, actor_upstreams| !actor_upstreams.is_empty());
291 if downstream_actor_upstreams.is_empty() {
292 self.upstreams.remove(&downstream_fragment_id);
293 }
294
295 let mut common_upstream_actors: Vec<_> = common_upstream_actors.into_values().collect();
296 common_upstream_actors.sort_unstable_by_key(|actor| actor.actor_id);
297 Ok(common_upstream_actors)
298 }
299}
300
301pub(crate) struct RegisteringFragments;
302pub(crate) struct AddingRelations;
303
304pub(crate) struct FragmentEdgeBuilder<State> {
305 fragments: HashMap<FragmentId, FragmentStatus>,
306 result: FragmentEdgeBuildResult,
307 actor_new_no_shuffle: ActorNewNoShuffle,
308 _state: PhantomData<State>,
309}
310
311impl FragmentEdgeBuilder<RegisteringFragments> {
312 pub(crate) fn new() -> Self {
313 Self {
314 fragments: Default::default(),
315 result: FragmentEdgeBuildResult {
316 upstreams: Default::default(),
317 dispatchers: Default::default(),
318 merge_updates: Default::default(),
319 dispatcher_updates: Default::default(),
320 },
321 actor_new_no_shuffle: Default::default(),
322 _state: PhantomData,
323 }
324 }
325
326 fn add_existing_fragment_infos(
327 &mut self,
328 fragment_infos: impl IntoIterator<Item = (FragmentId, EdgeBuilderFragmentInfo)>,
329 ) {
330 for (fragment_id, info) in fragment_infos {
331 self.fragments
332 .try_insert(fragment_id, FragmentStatus::Existing(info))
333 .expect("non-duplicate");
334 }
335 }
336
337 pub(crate) fn add_existing_fragments<'a>(
338 mut self,
339 fragments: impl IntoIterator<Item = &'a InflightFragmentInfo>,
340 partial_graph_id: PartialGraphId,
341 control_stream_manager: &ControlStreamManager,
342 ) -> Self {
343 self.add_existing_fragment_infos(fragments.into_iter().map(|fragment| {
344 (
345 fragment.fragment_id,
346 EdgeBuilderFragmentInfo::from_inflight(
347 fragment,
348 partial_graph_id,
349 control_stream_manager,
350 ),
351 )
352 }));
353 self
354 }
355
356 fn add_new_fragment_infos(
357 &mut self,
358 fragment_infos: impl IntoIterator<Item = (FragmentId, EdgeBuilderFragmentInfo)>,
359 ) {
360 for (fragment_id, info) in fragment_infos {
361 self.fragments
362 .try_insert(fragment_id, FragmentStatus::New(info))
363 .expect("new fragment must not already be registered");
364 }
365 }
366
367 pub(crate) fn add_new_fragments<'a>(
368 mut self,
369 fragments: impl IntoIterator<Item = &'a InflightFragmentInfo>,
370 partial_graph_id: PartialGraphId,
371 control_stream_manager: &ControlStreamManager,
372 ) -> Self {
373 self.add_new_fragment_infos(fragments.into_iter().map(|fragment| {
374 (
375 fragment.fragment_id,
376 EdgeBuilderFragmentInfo::from_inflight(
377 fragment,
378 partial_graph_id,
379 control_stream_manager,
380 ),
381 )
382 }));
383 self
384 }
385
386 pub(crate) fn stop_existing_fragments(
389 mut self,
390 fragment_ids: impl IntoIterator<Item = FragmentId>,
391 ) -> Self {
392 for fragment_id in fragment_ids {
393 let status = self
394 .fragments
395 .remove(&fragment_id)
396 .expect("stopped fragment must already be registered");
397 let FragmentStatus::Existing(info) = status else {
398 panic!("fragment {fragment_id} must be existing before it is stopped");
399 };
400 self.fragments
401 .insert(fragment_id, FragmentStatus::Stopping(info));
402 }
403 self
404 }
405
406 pub(crate) fn add_new_logical_fragments<'a>(
407 mut self,
408 fragments: impl IntoIterator<Item = (PartialGraphId, &'a Fragment)>,
409 stream_actors: &HashMap<FragmentId, Vec<StreamActor>>,
410 actor_worker: &HashMap<ActorId, WorkerId>,
411 control_stream_manager: &ControlStreamManager,
412 ) -> Self {
413 self.add_new_fragment_infos(fragments.into_iter().map(|(partial_graph_id, fragment)| {
414 (
415 fragment.fragment_id,
416 EdgeBuilderFragmentInfo::from_fragment(
417 fragment,
418 stream_actors,
419 actor_worker,
420 partial_graph_id,
421 control_stream_manager,
422 ),
423 )
424 }));
425 self
426 }
427
428 fn replace_existing_fragment_actor_infos(
429 &mut self,
430 fragment_infos: impl IntoIterator<Item = (FragmentId, EdgeBuilderFragmentInfo)>,
431 ) {
432 for (fragment_id, after) in fragment_infos {
433 let status = self
434 .fragments
435 .remove(&fragment_id)
436 .expect("changed fragment must already be registered");
437 let FragmentStatus::Existing(before) = status else {
438 panic!("fragment {fragment_id} can only be changed once");
439 };
440 assert_eq!(
441 before.distribution_type, after.distribution_type,
442 "fragment distribution type cannot change"
443 );
444 for actor_id in before.actors.keys() {
445 assert!(
446 !after.actors.contains_key(actor_id),
447 "changed fragment {fragment_id} must use entirely fresh actor ids: retained {actor_id}"
448 );
449 }
450 self.fragments
451 .insert(fragment_id, FragmentStatus::Changed { before, after });
452 }
453 }
454
455 pub(crate) fn replace_existing_fragment_actors<'a>(
456 mut self,
457 fragments: impl IntoIterator<Item = &'a InflightFragmentInfo>,
458 partial_graph_id: PartialGraphId,
459 control_stream_manager: &ControlStreamManager,
460 ) -> Self {
461 self.replace_existing_fragment_actor_infos(fragments.into_iter().map(|fragment| {
462 (
463 fragment.fragment_id,
464 EdgeBuilderFragmentInfo::from_inflight(
465 fragment,
466 partial_graph_id,
467 control_stream_manager,
468 ),
469 )
470 }));
471 self
472 }
473
474 pub(crate) fn finish_fragments(self) -> FragmentEdgeBuilder<AddingRelations> {
475 FragmentEdgeBuilder {
476 fragments: self.fragments,
477 result: self.result,
478 actor_new_no_shuffle: self.actor_new_no_shuffle,
479 _state: PhantomData,
480 }
481 }
482}
483
484impl FragmentEdgeBuilder<AddingRelations> {
485 pub(crate) fn add_relations(
486 mut self,
487 relations: &FragmentDownstreamRelation,
488 ) -> MetaResult<Self> {
489 for fragment_id in self.fragments.keys().copied().collect::<Vec<_>>() {
490 let Some(relations) = relations.get(&fragment_id) else {
491 continue;
492 };
493 for relation in relations {
494 if self
495 .fragments
496 .contains_key(&relation.downstream_fragment_id)
497 {
498 self.add_edge_inner(fragment_id, relation)?;
499 }
500 }
501 }
502 Ok(self)
503 }
504
505 pub(crate) fn add_edge(
507 mut self,
508 fragment_id: FragmentId,
509 downstream: &DownstreamFragmentRelation,
510 ) -> MetaResult<Self> {
511 let installs_existing_relation = matches!(
512 (
513 self.fragments.get(&fragment_id),
514 self.fragments.get(&downstream.downstream_fragment_id),
515 ),
516 (
517 Some(FragmentStatus::Existing(_)),
518 Some(FragmentStatus::Existing(_)),
519 )
520 );
521 if installs_existing_relation {
522 self.add_existing_relation(fragment_id, downstream);
523 } else {
524 self.add_edge_inner(fragment_id, downstream)?;
525 }
526 Ok(self)
527 }
528
529 fn add_existing_relation(
530 &mut self,
531 fragment_id: FragmentId,
532 downstream: &DownstreamFragmentRelation,
533 ) {
534 let FragmentStatus::Existing(fragment) = &self.fragments[&fragment_id] else {
535 unreachable!("checked existing source fragment")
536 };
537 let FragmentStatus::Existing(downstream_fragment) =
538 &self.fragments[&downstream.downstream_fragment_id]
539 else {
540 unreachable!("checked existing downstream fragment")
541 };
542 let (dispatchers, upstreams, no_shuffle_map) =
543 Self::compose_edge(fragment_id, fragment, downstream, downstream_fragment);
544 Self::add_no_shuffle_mapping(
545 &mut self.actor_new_no_shuffle,
546 fragment_id,
547 downstream.downstream_fragment_id,
548 no_shuffle_map,
549 );
550 Self::add_dispatchers(&mut self.result.dispatchers, fragment_id, dispatchers);
551 Self::add_merge_updates(
552 &mut self.result.merge_updates,
553 fragment_id,
554 downstream.downstream_fragment_id,
555 downstream_fragment,
556 upstreams,
557 [],
558 );
559 }
560
561 fn add_edge_inner(
562 &mut self,
563 fragment_id: FragmentId,
564 downstream: &DownstreamFragmentRelation,
565 ) -> MetaResult<()> {
566 let Some(fragment_status) = self.fragments.get(&fragment_id) else {
567 if self
568 .fragments
569 .contains_key(&downstream.downstream_fragment_id)
570 {
571 return Err(anyhow!(
572 "cannot find fragment {} with downstream {:?}",
573 fragment_id,
574 downstream
575 )
576 .into());
577 } else {
578 return Ok(());
580 }
581 };
582 let Some(downstream_status) = self.fragments.get(&downstream.downstream_fragment_id) else {
583 return Ok(());
586 };
587 match (fragment_status, downstream_status) {
588 (FragmentStatus::Existing(_), FragmentStatus::Existing(_)) => {}
593 (
594 FragmentStatus::Existing(fragment),
595 FragmentStatus::Changed {
596 before,
597 after: downstream_fragment,
598 },
599 ) => {
600 let (dispatchers, upstreams, no_shuffle_map) =
601 Self::compose_edge(fragment_id, fragment, downstream, downstream_fragment);
602 Self::add_no_shuffle_mapping(
603 &mut self.actor_new_no_shuffle,
604 fragment_id,
605 downstream.downstream_fragment_id,
606 no_shuffle_map,
607 );
608 Self::add_upstreams(
609 &mut self.result.upstreams,
610 downstream.downstream_fragment_id,
611 upstreams,
612 );
613 Self::add_dispatcher_updates(
614 &mut self.result.dispatcher_updates,
615 dispatchers,
616 before.actors.keys().copied(),
617 );
618 }
619 (
620 FragmentStatus::Changed {
621 before,
622 after: fragment,
623 },
624 FragmentStatus::Existing(downstream_fragment),
625 ) => {
626 let (dispatchers, upstreams, no_shuffle_map) =
627 Self::compose_edge(fragment_id, fragment, downstream, downstream_fragment);
628 Self::add_no_shuffle_mapping(
629 &mut self.actor_new_no_shuffle,
630 fragment_id,
631 downstream.downstream_fragment_id,
632 no_shuffle_map,
633 );
634 Self::add_dispatchers(&mut self.result.dispatchers, fragment_id, dispatchers);
635 Self::add_merge_updates(
636 &mut self.result.merge_updates,
637 fragment_id,
638 downstream.downstream_fragment_id,
639 downstream_fragment,
640 upstreams,
641 before.actors.keys().copied(),
642 );
643 }
644 (FragmentStatus::Stopping(fragment), FragmentStatus::Existing(downstream_fragment)) => {
645 Self::add_merge_updates(
646 &mut self.result.merge_updates,
647 fragment_id,
648 downstream.downstream_fragment_id,
649 downstream_fragment,
650 HashMap::new(),
651 fragment.actors.keys().copied(),
652 );
653 }
654 (FragmentStatus::Stopping(_), FragmentStatus::Stopping(_)) => {}
655 (FragmentStatus::New(_), FragmentStatus::Changed { .. })
656 | (FragmentStatus::Changed { .. }, FragmentStatus::New(_))
657 | (FragmentStatus::Existing(_), FragmentStatus::Stopping(_))
658 | (FragmentStatus::New(_), FragmentStatus::Stopping(_))
659 | (FragmentStatus::Changed { .. }, FragmentStatus::Stopping(_))
660 | (
661 FragmentStatus::Stopping(_),
662 FragmentStatus::New(_) | FragmentStatus::Changed { .. },
663 ) => {
664 return Err(anyhow!(
665 "unsupported fragment lifecycle edge: {} -> {}",
666 fragment_id,
667 downstream.downstream_fragment_id,
668 )
669 .into());
670 }
671 (FragmentStatus::Existing(fragment), FragmentStatus::New(downstream_fragment))
672 | (FragmentStatus::New(fragment), FragmentStatus::Existing(downstream_fragment))
673 | (FragmentStatus::New(fragment), FragmentStatus::New(downstream_fragment))
674 | (
675 FragmentStatus::Changed {
676 after: fragment, ..
677 },
678 FragmentStatus::Changed {
679 after: downstream_fragment,
680 ..
681 },
682 ) => {
683 let (dispatchers, upstreams, no_shuffle_map) =
684 Self::compose_edge(fragment_id, fragment, downstream, downstream_fragment);
685 Self::add_no_shuffle_mapping(
686 &mut self.actor_new_no_shuffle,
687 fragment_id,
688 downstream.downstream_fragment_id,
689 no_shuffle_map,
690 );
691 Self::add_dispatchers(&mut self.result.dispatchers, fragment_id, dispatchers);
692 Self::add_upstreams(
693 &mut self.result.upstreams,
694 downstream.downstream_fragment_id,
695 upstreams,
696 );
697 }
698 }
699 Ok(())
700 }
701
702 fn add_no_shuffle_mapping(
703 actor_new_no_shuffle: &mut ActorNewNoShuffle,
704 fragment_id: FragmentId,
705 downstream_fragment_id: FragmentId,
706 no_shuffle_map: Option<HashMap<ActorId, ActorId>>,
707 ) {
708 if let Some(no_shuffle_map) = no_shuffle_map {
709 actor_new_no_shuffle
710 .entry(fragment_id)
711 .or_default()
712 .insert(downstream_fragment_id, no_shuffle_map);
713 }
714 }
715
716 fn add_dispatchers(
717 fragment_dispatchers: &mut FragmentActorDispatchers,
718 fragment_id: FragmentId,
719 dispatchers: HashMap<ActorId, PbDispatcher>,
720 ) {
721 for (actor_id, dispatcher) in dispatchers {
722 fragment_dispatchers
723 .entry(fragment_id)
724 .or_default()
725 .entry(actor_id)
726 .or_default()
727 .push(dispatcher);
728 }
729 }
730
731 fn add_upstreams(
732 fragment_upstreams: &mut HashMap<FragmentId, HashMap<ActorId, ActorUpstreams>>,
733 fragment_id: FragmentId,
734 upstreams: HashMap<ActorId, ActorUpstreams>,
735 ) {
736 let target = fragment_upstreams.entry(fragment_id).or_default();
737 for (actor_id, actor_upstreams) in upstreams {
738 target.entry(actor_id).or_default().extend(actor_upstreams);
739 }
740 }
741
742 fn add_dispatcher_updates(
743 dispatcher_updates: &mut Vec<DispatcherUpdate>,
744 dispatchers: HashMap<ActorId, PbDispatcher>,
745 removed_downstream_actor_id: impl IntoIterator<Item = ActorId>,
746 ) {
747 let removed_downstream_actor_id: Vec<_> = removed_downstream_actor_id.into_iter().collect();
748 for (actor_id, dispatcher) in dispatchers {
749 dispatcher_updates.push(DispatcherUpdate {
750 actor_id,
751 dispatcher_id: dispatcher.dispatcher_id,
752 hash_mapping: dispatcher.hash_mapping,
753 added_downstream_actor_id: dispatcher.downstream_actor_id,
754 removed_downstream_actor_id: removed_downstream_actor_id.clone(),
755 });
756 }
757 }
758
759 fn add_merge_updates(
760 merge_updates: &mut HashMap<FragmentId, Vec<MergeUpdate>>,
761 fragment_id: FragmentId,
762 downstream_fragment_id: FragmentId,
763 downstream_fragment: &EdgeBuilderFragmentInfo,
764 upstreams: HashMap<ActorId, ActorUpstreams>,
765 removed_upstream_actor_id: impl IntoIterator<Item = ActorId>,
766 ) {
767 let removed_upstream_actor_id: Vec<_> = removed_upstream_actor_id.into_iter().collect();
768 let fragment_merge_updates = merge_updates.entry(downstream_fragment_id).or_default();
769 for actor_id in downstream_fragment.actors.keys() {
770 let added_upstream_actors = upstreams
771 .get(actor_id)
772 .and_then(|upstreams| upstreams.get(&fragment_id))
773 .into_iter()
774 .flat_map(|upstreams| upstreams.values().cloned())
775 .collect();
776 fragment_merge_updates.push(MergeUpdate {
777 actor_id: *actor_id,
778 upstream_fragment_id: fragment_id,
779 new_upstream_fragment_id: None,
780 added_upstream_actors,
781 removed_upstream_actor_id: removed_upstream_actor_id.clone(),
782 });
783 }
784 }
785
786 fn compose_edge_dispatchers(
787 fragment: &EdgeBuilderFragmentInfo,
788 downstream: &DownstreamFragmentRelation,
789 downstream_fragment: &EdgeBuilderFragmentInfo,
790 ) -> (
791 HashMap<ActorId, PbDispatcher>,
792 Option<HashMap<ActorId, ActorId>>,
793 ) {
794 compose_dispatchers(
795 fragment.distribution_type,
796 &fragment.actors,
797 downstream.downstream_fragment_id,
798 downstream_fragment.distribution_type,
799 &downstream_fragment.actors,
800 downstream.dispatcher_type,
801 downstream.dist_key_indices.clone(),
802 downstream.output_mapping.clone(),
803 )
804 }
805
806 fn compose_edge(
807 fragment_id: FragmentId,
808 fragment: &EdgeBuilderFragmentInfo,
809 downstream: &DownstreamFragmentRelation,
810 downstream_fragment: &EdgeBuilderFragmentInfo,
811 ) -> ComposedEdge {
812 let (dispatchers, no_shuffle_map) =
813 Self::compose_edge_dispatchers(fragment, downstream, downstream_fragment);
814 let mut upstreams: HashMap<ActorId, ActorUpstreams> = HashMap::new();
815 for (&actor_id, dispatcher) in &dispatchers {
816 let actor_location = &fragment.actor_location[&actor_id];
817 for &downstream_actor in &dispatcher.downstream_actor_id {
818 upstreams
819 .entry(downstream_actor)
820 .or_default()
821 .entry(fragment_id)
822 .or_default()
823 .insert(
824 actor_id,
825 ActorInfo {
826 actor_id,
827 host: Some(actor_location.clone()),
828 partial_graph_id: fragment.partial_graph_id,
829 },
830 );
831 }
832 }
833 (dispatchers, upstreams, no_shuffle_map)
834 }
835
836 pub(crate) fn replace_upstream(
837 mut self,
838 fragment_id: FragmentId,
839 original_upstream_fragment_id: FragmentId,
840 new_upstream_fragment_id: FragmentId,
841 ) -> Self {
842 let fragment_merge_updates = self.result.merge_updates.entry(fragment_id).or_default();
843 if let Some(fragment_upstreams) = self.result.upstreams.get_mut(&fragment_id) {
844 fragment_upstreams.retain(|&actor_id, actor_upstreams| {
845 if let Some(new_upstreams) = actor_upstreams.remove(&new_upstream_fragment_id) {
846 fragment_merge_updates.push(MergeUpdate {
847 actor_id,
848 upstream_fragment_id: original_upstream_fragment_id,
849 new_upstream_fragment_id: Some(new_upstream_fragment_id),
850 added_upstream_actors: new_upstreams.into_values().collect(),
851 removed_upstream_actor_id: vec![],
852 })
853 } else if cfg!(debug_assertions) {
854 panic!("cannot find new upstreams for actor {} in fragment {} to new_upstream {}. Current upstreams {:?}", actor_id, fragment_id, new_upstream_fragment_id, actor_upstreams);
855 } else {
856 warn!(%actor_id, %fragment_id, %new_upstream_fragment_id, ?actor_upstreams, "cannot find new upstreams for actor");
857 }
858 !actor_upstreams.is_empty()
859 })
860 } else if cfg!(debug_assertions) {
861 panic!(
862 "cannot find new upstreams for fragment {} to new_upstream {} to replace {}. Current upstreams: {:?}",
863 fragment_id,
864 new_upstream_fragment_id,
865 original_upstream_fragment_id,
866 self.result.upstreams
867 );
868 } else {
869 warn!(%fragment_id, %new_upstream_fragment_id, %original_upstream_fragment_id, upstreams = ?self.result.upstreams, "cannot find new upstreams to replace");
870 }
871 self
872 }
873
874 pub(crate) fn build(self) -> (FragmentEdgeBuildResult, ActorNewNoShuffle) {
876 (self.result, self.actor_new_no_shuffle)
877 }
878}
879
880#[cfg(test)]
881mod tests {
882 use risingwave_meta_model::DispatcherType;
883 use risingwave_pb::stream_plan::PbDispatchOutputMapping;
884
885 use super::*;
886
887 #[derive(Clone, Copy, Debug)]
888 enum TestStatus {
889 Existing,
890 New,
891 Stopping,
892 Changed,
893 }
894
895 fn actor(id: u32) -> ActorId {
896 ActorId::new(id)
897 }
898
899 fn fragment(id: u32) -> FragmentId {
900 FragmentId::new(id)
901 }
902
903 fn edge_info(
904 distribution_type: DistributionType,
905 actors: impl IntoIterator<Item = (u32, Option<Bitmap>)>,
906 ) -> EdgeBuilderFragmentInfo {
907 let actors: HashMap<_, _> = actors
908 .into_iter()
909 .map(|(id, bitmap)| (actor(id), bitmap))
910 .collect();
911 let actor_location = actors
912 .keys()
913 .map(|actor_id| {
914 (
915 *actor_id,
916 HostAddress {
917 host: format!("actor-{actor_id}"),
918 port: 1234,
919 },
920 )
921 })
922 .collect();
923 EdgeBuilderFragmentInfo {
924 distribution_type,
925 actors,
926 actor_location,
927 partial_graph_id: PartialGraphId::new(1),
928 }
929 }
930
931 fn single_info(actor_id: u32) -> EdgeBuilderFragmentInfo {
932 edge_info(DistributionType::Single, [(actor_id, None)])
933 }
934
935 fn relation(target: FragmentId, dispatcher_type: DispatcherType) -> DownstreamFragmentRelation {
936 DownstreamFragmentRelation {
937 downstream_fragment_id: target,
938 dispatcher_type,
939 dist_key_indices: vec![],
940 output_mapping: PbDispatchOutputMapping::default(),
941 }
942 }
943
944 fn build_status_pair(
945 source: TestStatus,
946 target: TestStatus,
947 ) -> MetaResult<FragmentEdgeBuildResult> {
948 let source_fragment = fragment(1);
949 let target_fragment = fragment(2);
950 let existing = [(source_fragment, source, 1), (target_fragment, target, 11)]
951 .into_iter()
952 .filter(|(_, status, _)| !matches!(status, TestStatus::New))
953 .map(|(fragment_id, _, actor_id)| (fragment_id, single_info(actor_id)));
954 let mut builder = FragmentEdgeBuilder::new();
955 builder.add_existing_fragment_infos(existing);
956 for (fragment_id, status, actor_id) in
957 [(source_fragment, source, 2), (target_fragment, target, 12)]
958 {
959 match status {
960 TestStatus::Existing => {}
961 TestStatus::New => {
962 builder.add_new_fragment_infos([(fragment_id, single_info(actor_id))]);
963 }
964 TestStatus::Stopping => {
965 builder = builder.stop_existing_fragments([fragment_id]);
966 }
967 TestStatus::Changed => {
968 builder.replace_existing_fragment_actor_infos([(
969 fragment_id,
970 single_info(actor_id),
971 )]);
972 }
973 }
974 }
975 let (result, _) = builder
976 .finish_fragments()
977 .add_relations(&HashMap::from([(
978 source_fragment,
979 vec![relation(target_fragment, DispatcherType::Broadcast)],
980 )]))?
981 .build();
982 Ok(result)
983 }
984
985 #[test]
986 fn test_endpoint_status_matrix() {
987 for source in [
988 TestStatus::Existing,
989 TestStatus::New,
990 TestStatus::Stopping,
991 TestStatus::Changed,
992 ] {
993 for target in [
994 TestStatus::Existing,
995 TestStatus::New,
996 TestStatus::Stopping,
997 TestStatus::Changed,
998 ] {
999 let result = build_status_pair(source, target);
1000 if matches!(
1001 (source, target),
1002 (
1003 TestStatus::Existing | TestStatus::New | TestStatus::Changed,
1004 TestStatus::Stopping
1005 ) | (TestStatus::Stopping, TestStatus::New | TestStatus::Changed)
1006 | (TestStatus::New, TestStatus::Changed)
1007 | (TestStatus::Changed, TestStatus::New)
1008 ) {
1009 assert!(result.is_err(), "source={source:?}, target={target:?}");
1010 continue;
1011 }
1012 let result = result.unwrap();
1013 assert_eq!(
1014 result.dispatchers.contains_key(&fragment(1)),
1015 matches!(source, TestStatus::New | TestStatus::Changed)
1016 || matches!((source, target), (TestStatus::Existing, TestStatus::New)),
1017 "source={source:?}, target={target:?}"
1018 );
1019 assert_eq!(
1020 result.upstreams.contains_key(&fragment(2)),
1021 matches!(target, TestStatus::New | TestStatus::Changed)
1022 || matches!((source, target), (TestStatus::New, TestStatus::Existing)),
1023 "source={source:?}, target={target:?}"
1024 );
1025 assert_eq!(
1026 !result.dispatcher_updates.is_empty(),
1027 matches!(
1028 (source, target),
1029 (TestStatus::Existing, TestStatus::Changed)
1030 ),
1031 "source={source:?}, target={target:?}"
1032 );
1033 assert_eq!(
1034 result
1035 .merge_updates
1036 .values()
1037 .any(|updates| !updates.is_empty()),
1038 matches!(
1039 (source, target),
1040 (
1041 TestStatus::Stopping | TestStatus::Changed,
1042 TestStatus::Existing
1043 )
1044 ),
1045 "source={source:?}, target={target:?}"
1046 );
1047 }
1048 }
1049 }
1050
1051 #[test]
1052 fn test_hash_and_broadcast_dispatcher_updates() {
1053 for dispatcher_type in [DispatcherType::Hash, DispatcherType::Broadcast] {
1054 let source = fragment(1);
1055 let target = fragment(2);
1056 let bitmap = || Some(Bitmap::from_iter([true, true]));
1057 let mut builder = FragmentEdgeBuilder::new();
1058 builder.add_existing_fragment_infos([
1059 (source, edge_info(DistributionType::Single, [(1, None)])),
1060 (target, edge_info(DistributionType::Hash, [(11, bitmap())])),
1061 ]);
1062 builder.replace_existing_fragment_actor_infos([(
1063 target,
1064 edge_info(DistributionType::Hash, [(12, bitmap())]),
1065 )]);
1066 let (result, _) = builder
1067 .finish_fragments()
1068 .add_edge(source, &relation(target, dispatcher_type))
1069 .unwrap()
1070 .build();
1071
1072 let update = &result.dispatcher_updates[0];
1073 assert_eq!(update.added_downstream_actor_id, vec![actor(12)]);
1074 assert_eq!(update.removed_downstream_actor_id, vec![actor(11)]);
1075 assert_eq!(
1076 update.hash_mapping.is_some(),
1077 dispatcher_type == DispatcherType::Hash
1078 );
1079 }
1080 }
1081
1082 #[test]
1083 fn test_no_shuffle_changed_fragments() {
1084 let source = fragment(1);
1085 let target = fragment(2);
1086 let mut builder = FragmentEdgeBuilder::new();
1087 builder.add_existing_fragment_infos([(source, single_info(1)), (target, single_info(11))]);
1088 builder.replace_existing_fragment_actor_infos([
1089 (source, single_info(2)),
1090 (target, single_info(12)),
1091 ]);
1092 let (result, actor_new_no_shuffle) = builder
1093 .finish_fragments()
1094 .add_edge(source, &relation(target, DispatcherType::NoShuffle))
1095 .unwrap()
1096 .build();
1097
1098 assert!(result.dispatcher_updates.is_empty());
1099 assert!(result.merge_updates.is_empty());
1100 assert_eq!(actor_new_no_shuffle[&source][&target][&actor(2)], actor(12));
1101 }
1102
1103 #[test]
1104 fn test_no_shuffle_updates_remove_before_actor_superset() {
1105 let source = fragment(1);
1106 let target = fragment(2);
1107 let actors = |left, right| {
1108 [
1109 (left, Some(Bitmap::from_iter([true, false]))),
1110 (right, Some(Bitmap::from_iter([false, true]))),
1111 ]
1112 };
1113 let relations =
1114 HashMap::from([(source, vec![relation(target, DispatcherType::NoShuffle)])]);
1115
1116 let mut builder = FragmentEdgeBuilder::new();
1117 builder.add_existing_fragment_infos([
1118 (source, edge_info(DistributionType::Hash, actors(1, 2))),
1119 (target, edge_info(DistributionType::Hash, actors(11, 12))),
1120 ]);
1121 builder.replace_existing_fragment_actor_infos([(
1122 target,
1123 edge_info(DistributionType::Hash, actors(13, 14)),
1124 )]);
1125 let (result, _) = builder
1126 .finish_fragments()
1127 .add_relations(&relations)
1128 .unwrap()
1129 .build();
1130
1131 assert_eq!(result.dispatcher_updates.len(), 2);
1132 for update in result.dispatcher_updates {
1133 assert_eq!(update.added_downstream_actor_id.len(), 1);
1134 let mut removed = update.removed_downstream_actor_id;
1135 removed.sort_unstable();
1136 assert_eq!(removed, vec![actor(11), actor(12)]);
1137 }
1138
1139 let mut builder = FragmentEdgeBuilder::new();
1140 builder.add_existing_fragment_infos([
1141 (source, edge_info(DistributionType::Hash, actors(1, 2))),
1142 (target, edge_info(DistributionType::Hash, actors(11, 12))),
1143 ]);
1144 builder.replace_existing_fragment_actor_infos([(
1145 source,
1146 edge_info(DistributionType::Hash, actors(3, 4)),
1147 )]);
1148 let (result, _) = builder
1149 .finish_fragments()
1150 .add_relations(&relations)
1151 .unwrap()
1152 .build();
1153
1154 let merge_updates = &result.merge_updates[&target];
1155 assert_eq!(merge_updates.len(), 2);
1156 for update in merge_updates {
1157 assert_eq!(update.added_upstream_actors.len(), 1);
1158 let mut removed = update.removed_upstream_actor_id.clone();
1159 removed.sort_unstable();
1160 assert_eq!(removed, vec![actor(1), actor(2)]);
1161 }
1162 }
1163
1164 #[test]
1165 fn test_replace_upstream() {
1166 let old_source = fragment(1);
1167 let new_source = fragment(2);
1168 let target = fragment(3);
1169 let mut builder = FragmentEdgeBuilder::new();
1170 builder.add_existing_fragment_infos([(target, single_info(11))]);
1171 builder.add_new_fragment_infos([(new_source, single_info(2))]);
1172 let (result, _) = builder
1173 .finish_fragments()
1174 .add_edge(new_source, &relation(target, DispatcherType::Broadcast))
1175 .unwrap()
1176 .replace_upstream(target, old_source, new_source)
1177 .build();
1178
1179 let update = &result.merge_updates[&target][0];
1180 assert_eq!(update.upstream_fragment_id, old_source);
1181 assert_eq!(update.new_upstream_fragment_id, Some(new_source));
1182 assert_eq!(update.added_upstream_actors[0].actor_id, actor(2));
1183 }
1184
1185 #[test]
1186 fn test_new_to_existing_upstreams_are_consumed_for_add_mutation() {
1187 let source = fragment(1);
1188 let target = fragment(2);
1189 let mut builder = FragmentEdgeBuilder::new();
1190 builder.add_existing_fragment_infos([(target, single_info(11))]);
1191 builder.add_new_fragment_infos([(source, single_info(1))]);
1192 let (mut result, _) = builder
1193 .finish_fragments()
1194 .add_edge(source, &relation(target, DispatcherType::Broadcast))
1195 .unwrap()
1196 .build();
1197 let source_actor = StreamActor {
1198 actor_id: actor(1),
1199 fragment_id: source,
1200 vnode_bitmap: None,
1201 mview_definition: Default::default(),
1202 expr_context: None,
1203 config_override: Default::default(),
1204 };
1205 let node = StreamNode::default();
1206 let worker_id: WorkerId = 1.into();
1207 let actors_to_create = result.collect_actors_to_create(std::iter::once((
1208 source,
1209 &node,
1210 std::iter::once((&source_actor, worker_id)),
1211 [],
1212 )));
1213
1214 assert_eq!(actors_to_create[&worker_id][&source].1[0].2.len(), 1);
1215 let upstream_actors = result.take_common_upstream_actors(source, target).unwrap();
1216 assert_eq!(upstream_actors.len(), 1);
1217 assert_eq!(upstream_actors[0].actor_id, actor(1));
1218 result.apply_to_add_mutation(&mut AddMutation::default());
1219 }
1220
1221 #[test]
1222 fn test_take_common_upstream_actors_from_hash_edge() {
1223 let source = fragment(1);
1224 let target = fragment(2);
1225 let actors = |left, right| {
1226 [
1227 (left, Some(Bitmap::from_iter([true, false]))),
1228 (right, Some(Bitmap::from_iter([false, true]))),
1229 ]
1230 };
1231 let mut builder = FragmentEdgeBuilder::new();
1232 builder.add_existing_fragment_infos([(
1233 target,
1234 edge_info(DistributionType::Hash, actors(11, 12)),
1235 )]);
1236 builder.add_new_fragment_infos([(source, edge_info(DistributionType::Hash, actors(1, 2)))]);
1237 let (mut result, _) = builder
1238 .finish_fragments()
1239 .add_edge(source, &relation(target, DispatcherType::Hash))
1240 .unwrap()
1241 .build();
1242
1243 let upstream_actors = result.take_common_upstream_actors(source, target).unwrap();
1244 assert_eq!(
1245 upstream_actors
1246 .into_iter()
1247 .map(|actor| actor.actor_id)
1248 .collect::<Vec<_>>(),
1249 vec![actor(1), actor(2)]
1250 );
1251 assert!(!result.upstreams.contains_key(&target));
1252 }
1253
1254 #[test]
1255 #[should_panic(expected = "remaining_upstreams=1")]
1256 fn test_add_mutation_rejects_uncollected_actor_upstreams() {
1257 let source = fragment(1);
1258 let target = fragment(2);
1259 let mut builder = FragmentEdgeBuilder::new();
1260 builder.add_existing_fragment_infos([(source, single_info(1))]);
1261 builder.add_new_fragment_infos([(target, single_info(11))]);
1262 let (result, _) = builder
1263 .finish_fragments()
1264 .add_edge(source, &relation(target, DispatcherType::Broadcast))
1265 .unwrap()
1266 .build();
1267
1268 result.apply_to_add_mutation(&mut AddMutation::default());
1269 }
1270
1271 #[test]
1272 fn test_update_mutation_consumes_edge_updates_before_validation() {
1273 let source = fragment(1);
1274 let mut result = build_status_pair(TestStatus::Changed, TestStatus::Existing).unwrap();
1275 let source_actor = StreamActor {
1276 actor_id: actor(2),
1277 fragment_id: source,
1278 vnode_bitmap: None,
1279 mview_definition: Default::default(),
1280 expr_context: None,
1281 config_override: Default::default(),
1282 };
1283 let node = StreamNode::default();
1284 result.collect_actors_to_create(std::iter::once((
1285 source,
1286 &node,
1287 std::iter::once((&source_actor, 1.into())),
1288 [],
1289 )));
1290 let mut mutation = UpdateMutation::default();
1291
1292 result.apply_to_update_mutation(&mut mutation);
1293
1294 assert_eq!(mutation.merge_update.len(), 1);
1295 }
1296
1297 #[test]
1298 fn test_update_mutation_does_not_infer_dropped_actors() {
1299 let caller_owned_drop = actor(99);
1300
1301 let mut changed = build_status_pair(TestStatus::Changed, TestStatus::Existing).unwrap();
1302 let changed_actor = StreamActor {
1303 actor_id: actor(2),
1304 fragment_id: fragment(1),
1305 vnode_bitmap: None,
1306 mview_definition: Default::default(),
1307 expr_context: None,
1308 config_override: Default::default(),
1309 };
1310 changed.collect_actors_to_create(std::iter::once((
1311 fragment(1),
1312 &StreamNode::default(),
1313 std::iter::once((&changed_actor, 1.into())),
1314 [],
1315 )));
1316 let mut changed_mutation = UpdateMutation {
1317 dropped_actors: vec![caller_owned_drop],
1318 ..Default::default()
1319 };
1320 changed.apply_to_update_mutation(&mut changed_mutation);
1321 assert_eq!(changed_mutation.dropped_actors, vec![caller_owned_drop]);
1322
1323 let stopping = build_status_pair(TestStatus::Stopping, TestStatus::Existing).unwrap();
1324 let mut stopping_mutation = UpdateMutation {
1325 dropped_actors: vec![caller_owned_drop],
1326 ..Default::default()
1327 };
1328 stopping.apply_to_update_mutation(&mut stopping_mutation);
1329 assert_eq!(stopping_mutation.dropped_actors, vec![caller_owned_drop]);
1330 }
1331
1332 #[test]
1333 fn test_update_mutation_lowers_new_to_existing_upstreams() {
1334 let source = fragment(1);
1335 let mut result = build_status_pair(TestStatus::New, TestStatus::Existing).unwrap();
1336 let source_actor = StreamActor {
1337 actor_id: actor(2),
1338 fragment_id: source,
1339 vnode_bitmap: None,
1340 mview_definition: Default::default(),
1341 expr_context: None,
1342 config_override: Default::default(),
1343 };
1344 let node = StreamNode::default();
1345 result.collect_actors_to_create(std::iter::once((
1346 source,
1347 &node,
1348 std::iter::once((&source_actor, 1.into())),
1349 [],
1350 )));
1351 let mut mutation = UpdateMutation::default();
1352
1353 result.apply_to_update_mutation(&mut mutation);
1354
1355 assert_eq!(mutation.merge_update.len(), 1);
1356 let update = &mutation.merge_update[0];
1357 assert_eq!(update.actor_id, actor(11));
1358 assert_eq!(update.upstream_fragment_id, source);
1359 assert_eq!(
1360 update
1361 .added_upstream_actors
1362 .iter()
1363 .map(|actor| actor.actor_id)
1364 .collect::<Vec<_>>(),
1365 vec![actor(2)]
1366 );
1367 assert!(update.removed_upstream_actor_id.is_empty());
1368 assert_eq!(update.new_upstream_fragment_id, None);
1369 }
1370
1371 #[test]
1372 fn test_attach_new_relation_dispatcher_to_add_mutation() {
1373 let source = fragment(1);
1374 let target = fragment(2);
1375 let mut builder = FragmentEdgeBuilder::new();
1376 builder.add_existing_fragment_infos([(source, single_info(1))]);
1377 builder.add_new_fragment_infos([(target, single_info(11))]);
1378 let (mut result, _) = builder
1379 .finish_fragments()
1380 .add_edge(source, &relation(target, DispatcherType::Broadcast))
1381 .unwrap()
1382 .build();
1383 let target_actor = StreamActor {
1384 actor_id: actor(11),
1385 fragment_id: target,
1386 vnode_bitmap: None,
1387 mview_definition: Default::default(),
1388 expr_context: None,
1389 config_override: Default::default(),
1390 };
1391 let node = StreamNode::default();
1392 result.collect_actors_to_create(std::iter::once((
1393 target,
1394 &node,
1395 std::iter::once((&target_actor, 1.into())),
1396 [],
1397 )));
1398 let mut mutation = AddMutation::default();
1399
1400 result.apply_to_add_mutation(&mut mutation);
1401
1402 assert_eq!(mutation.actor_dispatchers[&actor(1)].dispatchers.len(), 1);
1403 assert_eq!(
1404 mutation.actor_dispatchers[&actor(1)].dispatchers[0].downstream_actor_id,
1405 vec![actor(11)]
1406 );
1407 }
1408
1409 #[test]
1410 fn test_add_relations_only_uses_registered_fragments() {
1411 let external_source = fragment(1);
1412 let source = fragment(2);
1413 let target = fragment(3);
1414 let mut builder = FragmentEdgeBuilder::new();
1415 builder.add_new_fragment_infos([(source, single_info(2)), (target, single_info(3))]);
1416 let (result, _) = builder
1417 .finish_fragments()
1418 .add_relations(&HashMap::from([
1419 (
1420 external_source,
1421 vec![relation(source, DispatcherType::Broadcast)],
1422 ),
1423 (source, vec![relation(target, DispatcherType::Broadcast)]),
1424 ]))
1425 .unwrap()
1426 .build();
1427
1428 assert!(!result.dispatchers.contains_key(&external_source));
1429 assert!(!result.upstreams.contains_key(&source));
1430 assert!(result.dispatchers.contains_key(&source));
1431 assert!(result.upstreams.contains_key(&target));
1432 }
1433
1434 #[test]
1435 fn test_add_edge_rejects_unregistered_upstream() {
1436 let source = fragment(1);
1437 let target = fragment(2);
1438 let mut builder = FragmentEdgeBuilder::new();
1439 builder.add_existing_fragment_infos([(target, single_info(2))]);
1440
1441 let result = builder
1442 .finish_fragments()
1443 .add_edge(source, &relation(target, DispatcherType::Broadcast));
1444
1445 assert!(result.is_err());
1446 }
1447
1448 #[test]
1449 #[should_panic(expected = "must use entirely fresh actor ids")]
1450 fn test_changed_fragment_rejects_retained_actor_ids() {
1451 let fragment = fragment(1);
1452 let mut builder = FragmentEdgeBuilder::new();
1453 builder.add_existing_fragment_infos([(fragment, single_info(1))]);
1454 builder.replace_existing_fragment_actor_infos([(fragment, single_info(1))]);
1455 }
1456 #[test]
1457 fn test_attach_new_relation_between_existing_fragments() {
1458 let source = fragment(1);
1459 let target = fragment(2);
1460 let mut builder = FragmentEdgeBuilder::new();
1461 builder.add_existing_fragment_infos([(source, single_info(1)), (target, single_info(11))]);
1462 let mut mutation = UpdateMutation::default();
1463
1464 let (edges, _) = builder
1465 .finish_fragments()
1466 .add_edge(source, &relation(target, DispatcherType::Broadcast))
1467 .unwrap()
1468 .build();
1469 edges.apply_to_update_mutation(&mut mutation);
1470
1471 assert!(mutation.dispatcher_update.is_empty());
1472 assert_eq!(
1473 mutation.actor_new_dispatchers[&actor(1)].dispatchers[0].downstream_actor_id,
1474 vec![actor(11)]
1475 );
1476 assert_eq!(mutation.merge_update.len(), 1);
1477 let merge_update = &mutation.merge_update[0];
1478 assert_eq!(merge_update.actor_id, actor(11));
1479 assert_eq!(merge_update.upstream_fragment_id, source);
1480 assert_eq!(merge_update.added_upstream_actors[0].actor_id, actor(1));
1481 assert!(merge_update.removed_upstream_actor_id.is_empty());
1482 }
1483}