1- use crate :: { NetworkBroadcastExec , NetworkCoalesceExec , NetworkShuffleExec , Stage } ;
1+ use crate :: { BroadcastExec , NetworkBroadcastExec , NetworkCoalesceExec , NetworkShuffleExec , Stage } ;
22use datafusion:: common:: Result ;
3- use datafusion:: physical_plan:: ExecutionPlan ;
3+ use datafusion:: physical_expr:: Partitioning ;
4+ use datafusion:: physical_plan:: repartition:: RepartitionExec ;
5+ use datafusion:: physical_plan:: { ExecutionPlan , ExecutionPlanProperties } ;
46use std:: sync:: Arc ;
57
68/// This trait represents a node that introduces the necessity of a network boundary in the plan.
@@ -15,6 +17,22 @@ pub trait NetworkBoundary: ExecutionPlan {
1517
1618 /// Returns the assigned input [Stage], if any.
1719 fn input_stage ( & self ) -> & Stage ;
20+
21+ /// Defines what head node should the producer stage feeding this [NetworkBoundary]
22+ /// implementation have. This information is used during planning an executing for ensuring
23+ /// the head of a stage has the appropriate shape for consumption.
24+ fn producer_head ( & self , consumer_tasks : usize ) -> ProducerHead ;
25+ }
26+
27+ /// Defines what shape should the head node of a stage have upon getting executed. Depending
28+ /// on the [NetworkBoundary] implementation, the stage below should have different head nodes.
29+ pub enum ProducerHead {
30+ /// No specific head node is necessary.
31+ None ,
32+ /// The head node should be a [BroadcastExec].
33+ BroadcastExec { output_partitions : usize } ,
34+ /// The head node should be a [RepartitionExec].
35+ RepartitionExec { partitioning : Partitioning } ,
1836}
1937
2038/// Extension trait for downcasting dynamic types to [NetworkBoundary].
@@ -41,38 +59,28 @@ impl NetworkBoundaryExt for dyn ExecutionPlan {
4159 }
4260}
4361
44- /// Scales up the head node of the input stage of a network boundary. Different network boundaries
45- /// have different needs for scaling up their input, like for example, scaling up a RepartitionExec
46- /// during shuffles.
47- pub ( crate ) fn network_boundary_scale_input (
62+ /// Ensures the head of the provided plan complies with the passed [ProducerHead] definition. This
63+ /// can be called both during planning and lazily at runtime.
64+ pub ( crate ) fn insert_producer_head (
4865 input : Arc < dyn ExecutionPlan > ,
49- consumer_partitions : usize ,
50- consumer_task_count : usize ,
66+ head : ProducerHead ,
5167) -> Result < Arc < dyn ExecutionPlan > > {
52- let transformed = NetworkShuffleExec :: scale_input (
53- Arc :: clone ( & input) ,
54- consumer_partitions,
55- consumer_task_count,
56- ) ?;
57- if transformed. transformed {
58- return Ok ( transformed. data ) ;
59- }
60- let transformed = NetworkBroadcastExec :: scale_input (
61- Arc :: clone ( & input) ,
62- consumer_partitions,
63- consumer_task_count,
64- ) ?;
65- if transformed. transformed {
66- return Ok ( transformed. data ) ;
67- }
68- let transformed = NetworkCoalesceExec :: scale_input (
69- Arc :: clone ( & input) ,
70- consumer_partitions,
71- consumer_task_count,
72- ) ?;
73- if transformed. transformed {
74- return Ok ( transformed. data ) ;
75- }
76-
77- Ok ( input)
68+ let input = if let Some ( r_exec) = input. downcast_ref :: < RepartitionExec > ( ) {
69+ Arc :: clone ( r_exec. input ( ) )
70+ } else if let Some ( b_exec) = input. downcast_ref :: < BroadcastExec > ( ) {
71+ Arc :: clone ( b_exec. input ( ) )
72+ } else {
73+ input
74+ } ;
75+ let plan = match head {
76+ ProducerHead :: None => input,
77+ ProducerHead :: BroadcastExec { output_partitions } => {
78+ let partitions = input. output_partitioning ( ) . partition_count ( ) ;
79+ Arc :: new ( BroadcastExec :: new ( input, output_partitions / partitions) )
80+ }
81+ ProducerHead :: RepartitionExec { partitioning } => {
82+ Arc :: new ( RepartitionExec :: try_new ( input, partitioning) ?)
83+ }
84+ } ;
85+ Ok ( plan)
7886}
0 commit comments