1use std::collections::{BTreeMap, HashMap};
16
17use assert_matches::assert_matches;
18use risingwave_common::bail;
19use risingwave_common::hash::{IsSingleton, VnodeCount, VnodeCountCompat};
20use risingwave_common::util::iter_util::ZipEqFast;
21use risingwave_common::util::stream_graph_visitor::visit_tables;
22use risingwave_pb::stream_plan::stream_node::NodeBody;
23use risingwave_pb::stream_plan::{
24 DispatchStrategy, DispatcherType, MergeNode, StreamNode, StreamScanType,
25};
26
27use crate::MetaResult;
28use crate::model::{Fragment, FragmentDownstreamRelation, FragmentId, FragmentReplaceUpstream};
29use crate::stream::stream_graph::fragment::{
30 CompleteStreamFragmentGraph, DownstreamExternalEdgeId, EdgeId, EitherFragment,
31 StreamFragmentEdge,
32};
33use crate::stream::stream_graph::id::GlobalFragmentId;
34use crate::stream::stream_graph::schedule;
35use crate::stream::stream_graph::schedule::Distribution;
36
37impl FragmentActorBuilder {
38 fn rewrite(&self) -> MetaResult<StreamNode> {
44 self.rewrite_inner(&self.node, 0)
45 }
46
47 fn rewrite_inner(&self, stream_node: &StreamNode, depth: usize) -> MetaResult<StreamNode> {
48 match stream_node.get_node_body()? {
49 NodeBody::Exchange(exchange) => {
51 if depth == 0 {
54 bail!(
55 "there should be no ExchangeNode on the top of the plan node: {:#?}",
56 stream_node
57 )
58 }
59 assert!(!stream_node.get_fields().is_empty());
60 assert!(stream_node.input.is_empty());
61
62 let (upstream_fragment_id, _) = &self.upstreams[&EdgeId::Internal {
64 link_id: stream_node.get_operator_id().as_raw_id(),
65 }];
66
67 let upstream_fragment_id = upstream_fragment_id.as_global_id();
68
69 Ok(StreamNode {
70 node_body: Some(NodeBody::Merge(Box::new({
71 MergeNode {
72 upstream_fragment_id,
73 upstream_dispatcher_type: exchange.get_strategy()?.r#type,
74 ..Default::default()
75 }
76 }))),
77 identity: "MergeExecutor".to_owned(),
78 ..stream_node.clone()
79 })
80 }
81
82 NodeBody::StreamScan(stream_scan) => {
84 let input = stream_node.get_input();
85 if stream_scan.stream_scan_type() == StreamScanType::CrossDbSnapshotBackfill {
86 assert!(input.is_empty());
89 return Ok(stream_node.clone());
90 }
91 assert_eq!(input.len(), 2);
92
93 let merge_node = &input[0];
94 assert_matches!(merge_node.node_body, Some(NodeBody::Merge(_)));
95 let batch_plan_node = &input[1];
96 assert_matches!(batch_plan_node.node_body, Some(NodeBody::BatchPlan(_)));
97
98 let (upstream_fragment_id, upstream_no_shuffle_actor) = &self.upstreams
100 [&EdgeId::UpstreamExternal {
101 upstream_job_id: stream_scan.table_id.as_job_id(),
102 downstream_fragment_id: self.fragment_id,
103 }];
104
105 let is_shuffled_backfill = stream_scan.stream_scan_type
106 == StreamScanType::ArrangementBackfill as i32
107 || stream_scan.stream_scan_type == StreamScanType::SnapshotBackfill as i32;
108 if !is_shuffled_backfill {
109 assert!(*upstream_no_shuffle_actor);
110 }
111
112 let upstream_dispatcher_type = if is_shuffled_backfill {
113 DispatcherType::Hash as _
116 } else {
117 DispatcherType::NoShuffle as _
118 };
119
120 let upstream_fragment_id = upstream_fragment_id.as_global_id();
121
122 let input = vec![
123 StreamNode {
125 node_body: Some(NodeBody::Merge(Box::new({
126 MergeNode {
127 upstream_fragment_id,
128 upstream_dispatcher_type,
129 ..Default::default()
130 }
131 }))),
132 ..merge_node.clone()
133 },
134 batch_plan_node.clone(),
135 ];
136
137 Ok(StreamNode {
138 input,
139 ..stream_node.clone()
140 })
141 }
142
143 NodeBody::CdcFilter(_) | NodeBody::SourceBackfill(_) => {
147 let input = stream_node.get_input();
148 assert_eq!(input.len(), 1);
149
150 let merge_node = &input[0];
151 assert_matches!(merge_node.node_body, Some(NodeBody::Merge(_)));
152
153 let upstream_source_id = match stream_node.get_node_body()? {
154 NodeBody::CdcFilter(node) => node.upstream_source_id,
155 NodeBody::SourceBackfill(node) => node.upstream_source_id,
156 _ => unreachable!(),
157 };
158
159 let (upstream_fragment_id, upstream_is_no_shuffle) = &self.upstreams
161 [&EdgeId::UpstreamExternal {
162 upstream_job_id: upstream_source_id.as_share_source_job_id(),
163 downstream_fragment_id: self.fragment_id,
164 }];
165
166 assert!(
167 *upstream_is_no_shuffle,
168 "Upstream Cdc Source should be singleton. \
169 SourceBackfill is NoShuffle 1-1 correspondence. \
170 So they both should have only one upstream actor."
171 );
172
173 let upstream_fragment_id = upstream_fragment_id.as_global_id();
174
175 let input = vec![
177 StreamNode {
179 node_body: Some(NodeBody::Merge(Box::new({
180 MergeNode {
181 upstream_fragment_id,
182 upstream_dispatcher_type: DispatcherType::NoShuffle as _,
183 ..Default::default()
184 }
185 }))),
186 ..merge_node.clone()
187 },
188 ];
189 Ok(StreamNode {
190 input,
191 ..stream_node.clone()
192 })
193 }
194
195 NodeBody::IcebergWithPkIndexWriter(_) => {
196 let mut new_stream_node = stream_node.clone();
197 for (input, new_input) in stream_node
200 .input
201 .iter()
202 .zip_eq_fast(&mut new_stream_node.input)
203 {
204 *new_input = self.rewrite_inner(input, depth + 1)?;
205 let Some(NodeBody::Merge(merge)) = new_input.node_body.as_mut() else {
206 bail!("iceberg pk-index writer input must be a merge after actor rewrite");
207 };
208 merge.allow_empty_upstream = true;
209 }
210 Ok(new_stream_node)
211 }
212
213 _ => {
215 let mut new_stream_node = stream_node.clone();
216 for (input, new_input) in stream_node
217 .input
218 .iter()
219 .zip_eq_fast(&mut new_stream_node.input)
220 {
221 *new_input = self.rewrite_inner(input, depth + 1)?;
222 }
223 Ok(new_stream_node)
224 }
225 }
226 }
227}
228
229#[derive(Default)]
234struct UpstreamFragmentChange {
235 new_downstreams: HashMap<GlobalFragmentId, DispatchStrategy>,
237}
238
239#[derive(Default)]
240struct DownstreamFragmentChange {
241 new_upstreams: HashMap<DownstreamExternalEdgeId, GlobalFragmentId>,
244}
245
246impl UpstreamFragmentChange {
247 fn add_dispatcher(
249 &mut self,
250 downstream_fragment_id: GlobalFragmentId,
251 dispatch: DispatchStrategy,
252 ) {
253 self.new_downstreams
254 .try_insert(downstream_fragment_id, dispatch)
255 .unwrap();
256 }
257}
258
259impl DownstreamFragmentChange {
260 fn add_upstream(
262 &mut self,
263 edge_id: DownstreamExternalEdgeId,
264 new_upstream_fragment_id: GlobalFragmentId,
265 ) {
266 self.new_upstreams
267 .try_insert(edge_id, new_upstream_fragment_id)
268 .unwrap();
269 }
270}
271
272#[derive(Debug)]
273struct FragmentActorBuilder {
274 fragment_id: GlobalFragmentId,
275 node: StreamNode,
276 downstreams: HashMap<GlobalFragmentId, DispatchStrategy>,
277 upstreams: HashMap<EdgeId, (GlobalFragmentId, bool)>,
279}
280
281impl FragmentActorBuilder {
282 fn new(fragment_id: GlobalFragmentId, node: StreamNode) -> Self {
283 Self {
284 fragment_id,
285 node,
286 downstreams: Default::default(),
287 upstreams: Default::default(),
288 }
289 }
290}
291
292#[derive(Default)]
299struct ActorGraphBuildStateInner {
300 fragment_actor_builders: BTreeMap<GlobalFragmentId, FragmentActorBuilder>,
302
303 downstream_fragment_changes: BTreeMap<GlobalFragmentId, DownstreamFragmentChange>,
306
307 upstream_fragment_changes: BTreeMap<GlobalFragmentId, UpstreamFragmentChange>,
310}
311
312struct FragmentLinkNode {
314 fragment_id: GlobalFragmentId,
315}
316
317impl ActorGraphBuildStateInner {
318 fn add_dispatcher(
323 &mut self,
324 fragment_id: GlobalFragmentId,
325 downstream_fragment_id: GlobalFragmentId,
326 dispatch: DispatchStrategy,
327 ) {
328 if let Some(builder) = self.fragment_actor_builders.get_mut(&fragment_id) {
329 builder
330 .downstreams
331 .try_insert(downstream_fragment_id, dispatch)
332 .unwrap();
333 } else {
334 self.upstream_fragment_changes
335 .entry(fragment_id)
336 .or_default()
337 .add_dispatcher(downstream_fragment_id, dispatch);
338 }
339 }
340
341 fn add_upstream(
346 &mut self,
347 fragment_id: GlobalFragmentId,
348 edge_id: EdgeId,
349 upstream_fragment_id: GlobalFragmentId,
350 is_no_shuffle: bool,
351 ) {
352 if let Some(builder) = self.fragment_actor_builders.get_mut(&fragment_id) {
353 builder
354 .upstreams
355 .try_insert(edge_id, (upstream_fragment_id, is_no_shuffle))
356 .unwrap();
357 } else {
358 let EdgeId::DownstreamExternal(edge_id) = edge_id else {
359 unreachable!("edge from internal to external must be `DownstreamExternal`")
360 };
361 self.downstream_fragment_changes
362 .entry(fragment_id)
363 .or_default()
364 .add_upstream(edge_id, upstream_fragment_id);
365 }
366 }
367
368 fn add_link(
377 &mut self,
378 upstream: FragmentLinkNode,
379 downstream: FragmentLinkNode,
380 edge: &StreamFragmentEdge,
381 ) {
382 let dt = edge.dispatch_strategy.r#type();
383
384 match dt {
385 DispatcherType::NoShuffle => {
387 self.add_dispatcher(
389 upstream.fragment_id,
390 downstream.fragment_id,
391 edge.dispatch_strategy.clone(),
392 );
393
394 self.add_upstream(downstream.fragment_id, edge.id, upstream.fragment_id, true);
396 }
397
398 DispatcherType::Hash | DispatcherType::Broadcast | DispatcherType::Simple => {
400 self.add_dispatcher(
401 upstream.fragment_id,
402 downstream.fragment_id,
403 edge.dispatch_strategy.clone(),
404 );
405 self.add_upstream(downstream.fragment_id, edge.id, upstream.fragment_id, false);
406 }
407
408 DispatcherType::Unspecified => unreachable!(),
409 }
410 }
411}
412
413struct ActorGraphBuildState {
415 inner: ActorGraphBuildStateInner,
417}
418
419impl ActorGraphBuildState {
420 fn new() -> Self {
422 Self {
423 inner: Default::default(),
424 }
425 }
426
427 fn finish(self) -> ActorGraphBuildStateInner {
429 self.inner
430 }
431}
432
433pub struct ActorGraphBuildResult {
436 pub graph: BTreeMap<FragmentId, Fragment>,
438 pub downstream_fragment_relations: FragmentDownstreamRelation,
441
442 pub upstream_fragment_downstreams: FragmentDownstreamRelation,
444
445 pub replace_upstream: FragmentReplaceUpstream,
448}
449
450#[derive(Debug)]
453pub struct ActorGraphBuilder {
454 distributions: HashMap<GlobalFragmentId, Distribution>,
456
457 fragment_graph: CompleteStreamFragmentGraph,
459}
460
461impl ActorGraphBuilder {
462 pub fn new(fragment_graph: CompleteStreamFragmentGraph) -> MetaResult<Self> {
465 let expected_vnode_count = fragment_graph.max_parallelism();
466 let scheduler = schedule::Scheduler::new(expected_vnode_count)?;
467
468 let distributions = scheduler.schedule(&fragment_graph)?;
469
470 let mut fragment_graph = fragment_graph;
472 for (id, fragment) in fragment_graph.building_fragments_mut() {
473 let mut error = None;
474 let fragment_vnode_count = distributions[id].vnode_count();
475 visit_tables(fragment, |table, _| {
476 if error.is_some() {
477 return;
478 }
479 let vnode_count = if table.is_singleton() {
482 if fragment_vnode_count > 1 {
483 tracing::info!(
484 table.name,
485 "found singleton table in hash-distributed fragment"
486 );
487 }
488 1
489 } else {
490 fragment_vnode_count
491 };
492 match table.vnode_count_inner().value_opt() {
493 Some(required_vnode_count) if required_vnode_count != vnode_count => {
500 error = Some(format!(
501 "failed to align vnode count for table {}({}): required {}, but got {}",
502 table.id, table.name, required_vnode_count, vnode_count
503 ));
504 }
505 _ => table.maybe_vnode_count = VnodeCount::set(vnode_count).to_protobuf(),
507 }
508 });
509 if let Some(error) = error {
510 bail!(error);
511 }
512 }
513
514 Ok(Self {
515 distributions,
516 fragment_graph,
517 })
518 }
519
520 pub fn generate_graph(self) -> MetaResult<ActorGraphBuildResult> {
523 let ActorGraphBuildStateInner {
525 fragment_actor_builders,
526 downstream_fragment_changes,
527 upstream_fragment_changes,
528 } = self.build_actor_graph()?;
529
530 let mut downstream_fragment_relations: FragmentDownstreamRelation = HashMap::new();
531 let graph = {
533 let mut fragment_nodes: HashMap<GlobalFragmentId, StreamNode> = HashMap::new();
536
537 for (fragment_id, builder) in fragment_actor_builders {
538 let global_fragment_id = fragment_id.as_global_id();
539 let node = builder.rewrite()?;
540 downstream_fragment_relations
541 .try_insert(
542 global_fragment_id,
543 builder
544 .downstreams
545 .into_iter()
546 .map(|(id, dispatch)| (id.as_global_id(), dispatch).into())
547 .collect(),
548 )
549 .expect("non-duplicate");
550 fragment_nodes
551 .try_insert(fragment_id, node)
552 .expect("non-duplicate");
553 }
554
555 let mut graph = BTreeMap::new();
556 for (fragment_id, stream_node) in fragment_nodes {
557 let distribution = self.distributions[&fragment_id].clone();
558 let fragment =
559 self.fragment_graph
560 .seal_fragment(fragment_id, distribution, stream_node);
561 let fragment_id = fragment_id.as_global_id();
562 graph.insert(fragment_id, fragment);
563 }
564 graph
565 };
566
567 let upstream_fragment_downstreams = upstream_fragment_changes
569 .into_iter()
570 .map(|(fragment_id, changes)| {
571 (
572 fragment_id.as_global_id(),
573 changes
574 .new_downstreams
575 .into_iter()
576 .map(|(downstream_fragment_id, new_dispatch)| {
577 (downstream_fragment_id.as_global_id(), new_dispatch).into()
578 })
579 .collect(),
580 )
581 })
582 .collect();
583
584 let replace_upstream = downstream_fragment_changes
586 .into_iter()
587 .map(|(fragment_id, changes)| {
588 let fragment_id = fragment_id.as_global_id();
589 (
590 fragment_id,
591 changes
592 .new_upstreams
593 .into_iter()
594 .map(move |(edge_id, upstream_fragment_id)| {
595 let upstream_fragment_id = upstream_fragment_id.as_global_id();
596 let DownstreamExternalEdgeId {
597 original_upstream_fragment_id,
598 ..
599 } = edge_id;
600 (
601 original_upstream_fragment_id.as_global_id(),
602 upstream_fragment_id,
603 )
604 })
605 .collect(),
606 )
607 })
608 .filter(|(_, fragment_changes): &(_, HashMap<_, _>)| !fragment_changes.is_empty())
609 .collect();
610
611 Ok(ActorGraphBuildResult {
612 graph,
613 downstream_fragment_relations,
614 upstream_fragment_downstreams,
615 replace_upstream,
616 })
617 }
618
619 fn build_actor_graph(&self) -> MetaResult<ActorGraphBuildStateInner> {
621 let mut state = ActorGraphBuildState::new();
622
623 for fragment_id in self.fragment_graph.topo_order()? {
626 self.build_actor_graph_fragment(fragment_id, &mut state)?;
627 }
628
629 Ok(state.finish())
630 }
631
632 fn build_actor_graph_fragment(
634 &self,
635 fragment_id: GlobalFragmentId,
636 state: &mut ActorGraphBuildState,
637 ) -> MetaResult<()> {
638 let current_fragment = self.fragment_graph.get_fragment(fragment_id);
639
640 match current_fragment {
642 EitherFragment::Building(current_fragment) => {
644 let node = current_fragment.node.clone().unwrap();
645 state
646 .inner
647 .fragment_actor_builders
648 .try_insert(fragment_id, FragmentActorBuilder::new(fragment_id, node))
649 .expect("non-duplicate");
650 }
651
652 EitherFragment::Existing => {}
654 };
655
656 for (downstream_fragment_id, edge) in self.fragment_graph.get_downstreams(fragment_id) {
658 state.inner.add_link(
659 FragmentLinkNode { fragment_id },
660 FragmentLinkNode {
661 fragment_id: downstream_fragment_id,
662 },
663 edge,
664 );
665 }
666
667 Ok(())
668 }
669}