11use crate :: channel_resolver_ext:: get_distributed_channel_resolver;
22use crate :: distributed_physical_optimizer_rule:: NetworkBoundaryExt ;
33use crate :: execution_plans:: common:: require_one_child;
4- use crate :: protobuf:: DistributedCodec ;
4+ use crate :: protobuf:: { DistributedCodec , StageKey } ;
5+ use crate :: stage:: DisplayCtx ;
56use crate :: { ExecutionTask , Stage } ;
7+ use bytes:: Bytes ;
68use datafusion:: common:: exec_err;
79use datafusion:: common:: tree_node:: { Transformed , TreeNode } ;
10+ use datafusion:: error:: DataFusionError ;
811use datafusion:: execution:: { SendableRecordBatchStream , TaskContext } ;
912use datafusion:: physical_plan:: { DisplayAs , DisplayFormatType , ExecutionPlan , PlanProperties } ;
1013use datafusion_proto:: physical_plan:: PhysicalExtensionCodec ;
1114use rand:: Rng ;
1215use std:: any:: Any ;
13- use std:: fmt:: Formatter ;
16+ use std:: f32:: consts:: E ;
17+ use std:: fmt:: { format, Formatter } ;
1418use std:: sync:: Arc ;
19+ use std:: sync:: Mutex ;
1520use url:: Url ;
21+ use std:: sync:: OnceLock ;
1622
17- /// [ExecutionPlan ] that executes the inner plan in distributed mode.
23+ /// [ExecutionPan ] that executes the inner plan in distributed mode.
1824/// Before executing it, two modifications are lazily performed on the plan:
1925/// 1. Assigns worker URLs to all the stages. A random set of URLs are sampled from the
2026/// channel resolver and assigned to each task in each stage.
@@ -23,11 +29,34 @@ use url::Url;
2329#[ derive( Debug , Clone ) ]
2430pub struct DistributedExec {
2531 pub plan : Arc < dyn ExecutionPlan > ,
32+ pub prepared_plan : Arc < Mutex < Option < Arc < dyn ExecutionPlan > > > > ,
33+ pub display_ctx : Option < DisplayCtx > ,
2634}
2735
2836impl DistributedExec {
2937 pub fn new ( plan : Arc < dyn ExecutionPlan > ) -> Self {
30- Self { plan }
38+ Self { plan, prepared_plan : Arc :: new ( Mutex :: new ( None ) ) , display_ctx : None }
39+ }
40+
41+ /// Returns a special stage key used to identify the root "stage" of the distributed plan.
42+ /// TODO: reconcile this with display_plan_graphviz
43+ pub ( crate ) fn to_stage_key ( & self ) -> StageKey {
44+ StageKey {
45+ query_id : Bytes :: new ( ) ,
46+ stage_id : 0 as u64 ,
47+ task_number : 0 ,
48+ }
49+ }
50+
51+ pub ( crate ) fn with_display_ctx ( & self , display_ctx : DisplayCtx ) -> Self {
52+ Self { display_ctx : Some ( display_ctx) , ..self . clone ( ) }
53+ }
54+
55+ pub ( crate ) fn pepared_plan ( & self ) -> Result < Arc < dyn ExecutionPlan > , DataFusionError > {
56+ self . prepared_plan . lock ( )
57+ . map_err ( |e| DataFusionError :: Internal ( format ! ( "Failed to lock prepared plan: {}" , e) ) ) ?
58+ . clone ( )
59+ . ok_or ( DataFusionError :: Internal ( "No prepared plan found. Was .execute() called?" . to_string ( ) ) )
3160 }
3261
3362 fn prepare_plan (
@@ -99,6 +128,8 @@ impl ExecutionPlan for DistributedExec {
99128 ) -> datafusion:: common:: Result < Arc < dyn ExecutionPlan > > {
100129 Ok ( Arc :: new ( DistributedExec {
101130 plan : require_one_child ( & children) ?,
131+ prepared_plan : self . prepared_plan . clone ( ) ,
132+ display_ctx : self . display_ctx . clone ( ) ,
102133 } ) )
103134 }
104135
@@ -120,7 +151,17 @@ impl ExecutionPlan for DistributedExec {
120151 let channel_resolver = get_distributed_channel_resolver ( context. session_config ( ) ) ?;
121152 let codec = DistributedCodec :: new_combined_with_user ( context. session_config ( ) ) ;
122153
123- let plan = self . prepare_plan ( & channel_resolver. get_urls ( ) ?, & codec) ?;
154+ let plan = {
155+ let mut guard = self . prepared_plan . lock ( ) . map_err ( |e| DataFusionError :: Internal ( format ! ( "Failed to lock prepared plan: {}" , e) ) ) ?;
156+ match guard. clone ( ) {
157+ Some ( plan) => plan,
158+ None => {
159+ let prepared = self . prepare_plan ( & channel_resolver. get_urls ( ) ?, & codec) ?;
160+ * guard = Some ( prepared. clone ( ) ) ;
161+ prepared
162+ }
163+ }
164+ } ;
124165 plan. execute ( partition, context)
125166 }
126167}
0 commit comments