11use crate :: channel_resolver_ext:: get_distributed_channel_resolver;
2+ use crate :: execution_plans:: MetricsWrapperExec ;
23use crate :: execution_plans:: NetworkCoalesceExec ;
34use crate :: metrics:: TaskMetricsRewriter ;
45use crate :: { ChannelResolver , NetworkShuffleExec , PartitionIsolatorExec } ;
@@ -10,7 +11,6 @@ use datafusion::physical_plan::{
1011 DisplayAs , DisplayFormatType , ExecutionPlan , ExecutionPlanProperties , displayable,
1112} ;
1213use datafusion:: prelude:: SessionContext ;
13- use datafusion:: sql:: sqlparser:: keywords:: CHAIN ;
1414use itertools:: Itertools ;
1515use rand:: Rng ;
1616use std:: collections:: VecDeque ;
@@ -303,18 +303,22 @@ impl ExecutionPlan for StageExec {
303303 ) -> Result < Arc < dyn ExecutionPlan > > {
304304 let num_children = children. len ( ) ;
305305 let child_stage_execs = children
306- . into_iter ( )
307- . filter ( |child| child. as_any ( ) . downcast_ref :: < StageExec > ( ) . is_some ( ) )
308- . map ( |child| child. as_any ( ) . downcast_ref :: < StageExec > ( ) . unwrap ( ) . clone ( ) ) . collect :: < Vec < _ > > ( ) ;
306+ . into_iter ( )
307+ . filter ( |child| child. as_any ( ) . downcast_ref :: < StageExec > ( ) . is_some ( ) )
308+ . map ( |child| child. as_any ( ) . downcast_ref :: < StageExec > ( ) . unwrap ( ) . clone ( ) )
309+ . collect :: < Vec < _ > > ( ) ;
309310 if child_stage_execs. len ( ) != num_children {
310311 return plan_err ! ( "not all children are StageExec" ) ;
311312 }
312- let stage = StageExec {
313- query_id : self . query_id . clone ( ) ,
313+ let stage = StageExec {
314+ query_id : self . query_id ,
314315 num : self . num ,
315316 name : self . name . clone ( ) ,
316317 plan : self . plan . clone ( ) ,
317- inputs : child_stage_execs. into_iter ( ) . map ( |s| InputStage :: Decoded ( Arc :: new ( s) ) ) . collect ( ) ,
318+ inputs : child_stage_execs
319+ . into_iter ( )
320+ . map ( |s| InputStage :: Decoded ( Arc :: new ( s) ) )
321+ . collect ( ) ,
318322 tasks : self . tasks . clone ( ) ,
319323 depth : self . depth ,
320324 display_ctx : self . display_ctx . clone ( ) ,
@@ -369,12 +373,12 @@ impl ExecutionPlan for StageExec {
369373 }
370374}
371375
376+ use crate :: metrics:: proto:: MetricsSetProto ;
377+ use crate :: protobuf:: StageKey ;
372378use bytes:: Bytes ;
379+ use datafusion:: common:: HashMap ;
373380use datafusion:: common:: tree_node:: { TreeNode , TreeNodeRecursion } ;
374381use datafusion:: physical_expr:: Partitioning ;
375- use datafusion:: common:: HashMap ;
376- use crate :: metrics:: proto:: MetricsSetProto ;
377- use crate :: protobuf:: StageKey ;
378382
379383/// Be able to display a nice tree for stages.
380384///
@@ -410,14 +414,25 @@ impl DisplayCtx {
410414 }
411415}
412416
417+ #[ derive( Clone , Copy ) ]
418+ enum TaskFmt {
419+ All ,
420+ TaskID { task_id : usize } ,
421+ }
422+
413423impl StageExec {
414- fn format ( & self , plan : & dyn ExecutionPlan , indent : usize , f : & mut String ) -> std:: fmt:: Result {
415- // println!("plan {:?}", plan);
424+ fn format (
425+ & self ,
426+ plan : & dyn ExecutionPlan ,
427+ indent : usize ,
428+ task_fmt : TaskFmt ,
429+ f : & mut String ,
430+ ) -> std:: fmt:: Result {
416431 let mut node_str = match & self . display_ctx {
417432 None => displayable ( plan) . one_line ( ) . to_string ( ) ,
418- Some ( _) => {
419- DisplayableExecutionPlan :: with_metrics ( plan ) . one_line ( ) . to_string ( )
420- }
433+ Some ( _) => DisplayableExecutionPlan :: with_metrics ( plan )
434+ . one_line ( )
435+ . to_string ( ) ,
421436 } ;
422437 node_str. pop ( ) ;
423438 write ! ( f, "{} {node_str}" , " " . repeat( indent) ) ?;
@@ -455,17 +470,29 @@ impl StageExec {
455470 ) ?;
456471 }
457472
458- if let Some ( isolator) = plan. as_any ( ) . downcast_ref :: < PartitionIsolatorExec > ( ) {
459- write ! (
460- f,
461- " {}" ,
462- format_tasks_for_partition_isolator( isolator, & self . tasks)
463- ) ?;
473+ let mut maybe_partition_isolator = plan;
474+ if self . display_ctx . is_some ( ) {
475+ if let Some ( wrapper) = plan. as_any ( ) . downcast_ref :: < MetricsWrapperExec > ( ) {
476+ maybe_partition_isolator = wrapper. get_inner ( ) . as_ref ( ) ;
477+ }
478+ }
479+
480+ if let Some ( isolator) = maybe_partition_isolator
481+ . as_any ( )
482+ . downcast_ref :: < PartitionIsolatorExec > ( )
483+ {
484+ let task_info = match task_fmt {
485+ TaskFmt :: All => format_tasks_for_partition_isolator ( isolator, & self . tasks ) ,
486+ TaskFmt :: TaskID { task_id } => {
487+ format_task_for_partition_isolator ( isolator, task_id, self . tasks . len ( ) )
488+ }
489+ } ;
490+ write ! ( f, " {}" , task_info) ?;
464491 }
465492 writeln ! ( f) ?;
466493
467494 for child in plan. children ( ) {
468- self . format ( child. as_ref ( ) , indent + 2 , f) ?;
495+ self . format ( child. as_ref ( ) , indent + 2 , task_fmt , f) ?;
469496 }
470497 Ok ( ( ) )
471498 }
@@ -491,7 +518,7 @@ impl DisplayAs for StageExec {
491518 ) ?;
492519
493520 let mut plan_str = String :: new ( ) ;
494- self . format ( self . plan . as_ref ( ) , 0 , & mut plan_str) ?;
521+ self . format ( self . plan . as_ref ( ) , 0 , TaskFmt :: All , & mut plan_str) ?;
495522 let plan_str = plan_str
496523 . split ( '\n' )
497524 . filter ( |v| !v. is_empty ( ) )
@@ -511,7 +538,7 @@ impl DisplayAs for StageExec {
511538 }
512539 Some ( display_ctx) => {
513540 for ( i, _) in self . tasks . iter ( ) . enumerate ( ) {
514- let mut extra_spacing = "" . to_string ( ) ;
541+ let mut extra_spacing = "" . to_string ( ) ;
515542 if i > 0 {
516543 writeln ! ( f) ?; // Add newline for each task
517544 extra_spacing = " " . repeat ( self . depth ) ; // with_indent() in DisplayableExectutionPlan will not add indentation for tasks, so we add it manually.
@@ -535,34 +562,38 @@ impl DisplayAs for StageExec {
535562 let mut plan_str = String :: new ( ) ;
536563 let plan = match display_ctx. metrics . get ( & key) {
537564 Some ( metrics) => {
538- let result = TaskMetricsRewriter :: new ( metrics. to_owned ( ) ) . enrich_task_with_metrics ( self . plan . clone ( ) ) ;
565+ let result = TaskMetricsRewriter :: new ( metrics. to_owned ( ) )
566+ . enrich_task_with_metrics ( self . plan . clone ( ) ) ;
539567 if let Err ( e) = result {
540568 write ! ( f, "Error enriching task with metrics: {}" , e) ?;
541569 return Err ( std:: fmt:: Error ) ;
542570 }
543571 result. unwrap ( )
544572 }
545- None => {
546- self . plan . clone ( )
547- }
573+ None => self . plan . clone ( ) ,
548574 } ;
549- self . format ( plan. as_ref ( ) , 0 , & mut plan_str) ?;
550- let plan_str = plan_str
551- . split ( '\n' )
552- . filter ( |v| !v. is_empty ( ) )
553- . collect :: < Vec < _ > > ( )
554- . join ( & format ! ( "\n {}{}" , " " . repeat( self . depth) , VERTICAL ) ) ;
555- writeln ! ( f, "{}{}{}" , " " . repeat( self . depth) , VERTICAL , plan_str) ?;
556- // Add bottom border
575+ self . format (
576+ plan. as_ref ( ) ,
577+ 0 ,
578+ TaskFmt :: TaskID { task_id : i } ,
579+ & mut plan_str,
580+ ) ?;
581+ let plan_str = plan_str
582+ . split ( '\n' )
583+ . filter ( |v| !v. is_empty ( ) )
584+ . collect :: < Vec < _ > > ( )
585+ . join ( & format ! ( "\n {}{}" , " " . repeat( self . depth) , VERTICAL ) ) ;
586+ writeln ! ( f, "{}{}{}" , " " . repeat( self . depth) , VERTICAL , plan_str) ?;
587+ // Add bottom border
557588 write ! (
558589 f,
559590 "{}{}{}" ,
560591 " " . repeat( self . depth) ,
561592 LDCORNER ,
562593 HORIZONTAL . repeat( 50 )
563- ) ?;
594+ ) ?;
564595 }
565- return Ok ( ( ) ) ;
596+ Ok ( ( ) )
566597 }
567598 }
568599 }
@@ -604,7 +635,7 @@ fn format_task_for_stage(task_number: usize, head: &Arc<dyn ExecutionPlan>) -> S
604635 . map ( |v| format ! ( "p{v}" ) )
605636 . join ( "," ) ;
606637 result += "] " ;
607-
638+
608639 result
609640}
610641
@@ -631,6 +662,28 @@ fn format_tasks_for_partition_isolator(
631662 result
632663}
633664
665+ fn format_task_for_partition_isolator (
666+ isolator : & PartitionIsolatorExec ,
667+ task_number : usize ,
668+ num_tasks : usize ,
669+ ) -> String {
670+ let input_partitions = isolator. input ( ) . output_partitioning ( ) . partition_count ( ) ;
671+ let partition_groups = PartitionIsolatorExec :: partition_groups ( input_partitions, num_tasks) ;
672+
673+ let n: usize = partition_groups. iter ( ) . map ( |v| v. len ( ) ) . sum ( ) ;
674+ let mut partitions = vec ! [ "__" . to_string( ) ; n] ;
675+
676+ let mut result = "Task " . to_string ( ) ;
677+ partition_groups
678+ . get ( task_number)
679+ . unwrap ( )
680+ . iter ( )
681+ . enumerate ( )
682+ . for_each ( |( j, p) | partitions[ * p] = format ! ( "p{j}" ) ) ;
683+ result += & format ! ( "t{task_number}:[{}] " , partitions. join( "," ) ) ;
684+ result
685+ }
686+
634687// num_colors must agree with the colorscheme selected from
635688// https://graphviz.org/doc/info/colors.html
636689const NUM_COLORS : usize = 6 ;
0 commit comments