@@ -5,7 +5,7 @@ use crate::execution_plans::ChildrenIsolatorUnionExec;
55use crate :: networking:: get_distributed_worker_resolver;
66use crate :: passthrough_headers:: get_passthrough_headers;
77use crate :: protobuf:: { DistributedCodec , tonic_status_to_datafusion_error} ;
8- use crate :: stage:: { ExecutionTask , Stage } ;
8+ use crate :: stage:: { LocalStage , RemoteStage , Stage } ;
99use crate :: worker:: generated:: worker as pb;
1010use crate :: worker:: generated:: worker:: set_plan_request:: WorkUnitFeedDeclaration ;
1111use crate :: worker:: generated:: worker:: {
@@ -21,7 +21,7 @@ use datafusion::common::HashMap;
2121use datafusion:: common:: instant:: Instant ;
2222use datafusion:: common:: runtime:: JoinSet ;
2323use datafusion:: common:: tree_node:: { Transformed , TreeNode , TreeNodeRecursion } ;
24- use datafusion:: common:: { Result , exec_err, internal_err } ;
24+ use datafusion:: common:: { Result , exec_err} ;
2525use datafusion:: common:: { exec_datafusion_err, internal_datafusion_err} ;
2626use datafusion:: error:: DataFusionError ;
2727use datafusion:: execution:: { SendableRecordBatchStream , TaskContext } ;
@@ -126,10 +126,10 @@ impl DistributedExec {
126126 let _ = self . plan . apply ( |plan| {
127127 if let Some ( boundary) = plan. as_network_boundary ( ) {
128128 let stage = boundary. input_stage ( ) ;
129- for i in 0 ..stage. tasks . len ( ) {
129+ for i in 0 ..stage. task_count ( ) {
130130 expected_keys. push ( TaskKey {
131- query_id : stage. query_id . as_bytes ( ) . to_vec ( ) ,
132- stage_id : stage. num as u64 ,
131+ query_id : serialize_uuid ( & stage. query_id ( ) ) ,
132+ stage_id : stage. num ( ) as u64 ,
133133 task_number : i as u64 ,
134134 } ) ;
135135 }
@@ -193,61 +193,63 @@ impl DistributedExec {
193193 return Ok ( Transformed :: no ( plan) ) ;
194194 } ;
195195
196- let task_estimator = get_distributed_task_estimator ( ctx. session_config ( ) ) ?;
196+ let Stage :: Local ( stage) = plan. input_stage ( ) else {
197+ return exec_err ! ( "Input stage from network boundary was not in Local state" ) ;
198+ } ;
197199
198- let stage = plan . input_stage ( ) ;
200+ let task_estimator = get_distributed_task_estimator ( ctx . session_config ( ) ) ? ;
199201
200- let mut spawner =
201- CoordinatorToWorkerTaskSpawner :: new ( stage, & metrics, & codec, & mut join_set) ?;
202+ let mut spawner = CoordinatorToWorkerTaskSpawner :: new (
203+ stage,
204+ & metrics,
205+ & self . task_metrics ,
206+ & codec,
207+ & mut join_set,
208+ ) ?;
202209
203210 let routed_urls = match task_estimator. route_tasks ( & TaskRoutingContext {
204211 task_ctx : Arc :: clone ( ctx) ,
205- plan : stage. plan . as_ref ( ) . ok_or_else ( || {
206- internal_datafusion_err ! (
207- "network boundary stage missing input plan during routing"
208- )
209- } ) ?,
210- task_count : stage. tasks . len ( ) ,
212+ plan : & stage. plan ,
213+ task_count : stage. tasks ,
211214 available_urls : & available_urls,
212215 } ) {
213216 Ok ( Some ( routed_urls) ) => routed_urls,
214217 // If the user has not defined custom routing with a `route_tasks` implementation, we
215218 // default to round-robin task assignation from a randomized starting point.
216219 Ok ( None ) => {
217220 let start_idx = rand:: rng ( ) . random_range ( 0 ..available_urls. len ( ) ) ;
218- ( 0 ..stage. tasks . len ( ) )
221+ ( 0 ..stage. tasks )
219222 . map ( |i| available_urls[ ( start_idx + i) % available_urls. len ( ) ] . clone ( ) )
220223 . collect ( )
221224 }
222225 Err ( e) => return Err ( exec_datafusion_err ! ( "error routing tasks to workers: {e}" ) ) ,
223226 } ;
224227
225- if routed_urls. len ( ) != stage. tasks . len ( ) {
228+ if routed_urls. len ( ) != stage. tasks {
226229 return Err ( exec_datafusion_err ! (
227230 "number of tasks ({}) was not equal to number of urls ({}) at execution time" ,
228- stage. tasks. len ( ) ,
231+ stage. tasks,
229232 routed_urls. len( )
230233 ) ) ;
231234 }
232235
233- let mut tasks = Vec :: with_capacity ( stage. tasks . len ( ) ) ;
236+ let mut workers = Vec :: with_capacity ( stage. tasks ) ;
234237 for ( i, routed_url) in routed_urls. into_iter ( ) . enumerate ( ) {
235- tasks. push ( ExecutionTask {
236- url : Some ( routed_url. clone ( ) ) ,
237- } ) ;
238+ workers. push ( routed_url. clone ( ) ) ;
238239 // Spawn a task that sends the subplan to the chosen URL.
239240 // There will be as many spawned tasks as workers.
240241 let ( tx, worker_rx) = spawner. send_plan_task ( Arc :: clone ( ctx) , i, routed_url) ?;
241- spawner. metrics_collection_task ( i, worker_rx, Arc :: clone ( & self . task_metrics ) ) ;
242+ spawner. metrics_collection_task ( i, worker_rx) ;
242243 spawner. work_unit_feed_task ( Arc :: clone ( ctx) , i, tx) ?;
243244 }
244245
245- Ok ( Transformed :: yes ( plan. with_input_stage ( Stage {
246- query_id : stage. query_id ,
247- num : stage. num ,
248- plan : None ,
249- tasks,
250- } ) ?) )
246+ Ok ( Transformed :: yes ( plan. with_input_stage ( Stage :: Remote (
247+ RemoteStage {
248+ query_id : stage. query_id ,
249+ num : stage. num ,
250+ workers,
251+ } ,
252+ ) ) ?) )
251253 } ) ?;
252254 Ok ( PreparedPlan {
253255 plan : prepared. data ,
@@ -367,32 +369,31 @@ struct CoordinatorToWorkerTaskSpawner<'a> {
367369 stage_id : usize ,
368370 task_count : usize ,
369371 metrics : & ' a CoordinatorToWorkerMetrics ,
372+ task_metrics : & ' a Arc < MetricsStore > ,
370373 join_set : & ' a mut JoinSet < Result < ( ) > > ,
371374}
372375
373376impl < ' a > CoordinatorToWorkerTaskSpawner < ' a > {
374377 /// Builds a new [CoordinatorToWorkerTaskSpawner] based on the [Stage] that needs to be
375378 /// fanned out to multiple workers.
376379 fn new (
377- stage : & ' a Stage ,
380+ stage : & ' a LocalStage ,
378381 metrics : & ' a CoordinatorToWorkerMetrics ,
382+ task_metrics : & ' a Arc < MetricsStore > ,
379383 codec : & ' a dyn PhysicalExtensionCodec ,
380384 join_set : & ' a mut JoinSet < Result < ( ) > > ,
381385 ) -> Result < Self > {
382- let Some ( plan) = & stage. plan else {
383- return internal_err ! ( "Plan is not set for stage {}" , stage. num) ;
384- } ;
385-
386- let plan_proto =
387- PhysicalPlanNode :: try_from_physical_plan ( Arc :: clone ( plan) , codec) ?. encode_to_vec ( ) ;
386+ let plan_proto = PhysicalPlanNode :: try_from_physical_plan ( Arc :: clone ( & stage. plan ) , codec) ?
387+ . encode_to_vec ( ) ;
388388
389389 Ok ( Self {
390- plan,
390+ plan : & stage . plan ,
391391 plan_proto,
392392 query_id : stage. query_id ,
393393 stage_id : stage. num ,
394- task_count : stage. tasks . len ( ) ,
394+ task_count : stage. tasks ,
395395 metrics,
396+ task_metrics,
396397 join_set,
397398 } )
398399 }
@@ -520,13 +521,13 @@ impl<'a> CoordinatorToWorkerTaskSpawner<'a> {
520521 & mut self ,
521522 task_i : usize ,
522523 mut worker_to_coordinator_rx : WorkerResponseRx ,
523- task_metrics_collection : Arc < MetricsStore > ,
524524 ) {
525525 let task_key = TaskKey {
526526 query_id : serialize_uuid ( & self . query_id ) ,
527527 stage_id : self . stage_id as u64 ,
528528 task_number : task_i as u64 ,
529529 } ;
530+ let task_metrics_collection = Arc :: clone ( self . task_metrics ) ;
530531 tokio:: spawn ( async move {
531532 while let Some ( Ok ( msg) ) = worker_to_coordinator_rx. recv ( ) . await {
532533 let Some ( worker_to_coordinator_msg:: Inner :: TaskMetrics ( pre_order_metrics) ) =
0 commit comments