1
1
use crate :: channel_resolver_ext:: get_distributed_channel_resolver;
2
+ use crate :: execution_plans:: MetricsWrapperExec ;
2
3
use crate :: execution_plans:: NetworkCoalesceExec ;
3
4
use crate :: metrics:: TaskMetricsRewriter ;
4
5
use crate :: { ChannelResolver , NetworkShuffleExec , PartitionIsolatorExec } ;
@@ -10,7 +11,6 @@ use datafusion::physical_plan::{
10
11
DisplayAs , DisplayFormatType , ExecutionPlan , ExecutionPlanProperties , displayable,
11
12
} ;
12
13
use datafusion:: prelude:: SessionContext ;
13
- use datafusion:: sql:: sqlparser:: keywords:: CHAIN ;
14
14
use itertools:: Itertools ;
15
15
use rand:: Rng ;
16
16
use std:: collections:: VecDeque ;
@@ -303,18 +303,22 @@ impl ExecutionPlan for StageExec {
303
303
) -> Result < Arc < dyn ExecutionPlan > > {
304
304
let num_children = children. len ( ) ;
305
305
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 < _ > > ( ) ;
309
310
if child_stage_execs. len ( ) != num_children {
310
311
return plan_err ! ( "not all children are StageExec" ) ;
311
312
}
312
- let stage = StageExec {
313
- query_id : self . query_id . clone ( ) ,
313
+ let stage = StageExec {
314
+ query_id : self . query_id ,
314
315
num : self . num ,
315
316
name : self . name . clone ( ) ,
316
317
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 ( ) ,
318
322
tasks : self . tasks . clone ( ) ,
319
323
depth : self . depth ,
320
324
display_ctx : self . display_ctx . clone ( ) ,
@@ -369,12 +373,12 @@ impl ExecutionPlan for StageExec {
369
373
}
370
374
}
371
375
376
+ use crate :: metrics:: proto:: MetricsSetProto ;
377
+ use crate :: protobuf:: StageKey ;
372
378
use bytes:: Bytes ;
379
+ use datafusion:: common:: HashMap ;
373
380
use datafusion:: common:: tree_node:: { TreeNode , TreeNodeRecursion } ;
374
381
use datafusion:: physical_expr:: Partitioning ;
375
- use datafusion:: common:: HashMap ;
376
- use crate :: metrics:: proto:: MetricsSetProto ;
377
- use crate :: protobuf:: StageKey ;
378
382
379
383
/// Be able to display a nice tree for stages.
380
384
///
@@ -410,14 +414,25 @@ impl DisplayCtx {
410
414
}
411
415
}
412
416
417
+ #[ derive( Clone , Copy ) ]
418
+ enum TaskFmt {
419
+ All ,
420
+ TaskID { task_id : usize } ,
421
+ }
422
+
413
423
impl 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 {
416
431
let mut node_str = match & self . display_ctx {
417
432
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 ( ) ,
421
436
} ;
422
437
node_str. pop ( ) ;
423
438
write ! ( f, "{} {node_str}" , " " . repeat( indent) ) ?;
@@ -455,17 +470,29 @@ impl StageExec {
455
470
) ?;
456
471
}
457
472
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) ?;
464
491
}
465
492
writeln ! ( f) ?;
466
493
467
494
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) ?;
469
496
}
470
497
Ok ( ( ) )
471
498
}
@@ -491,7 +518,7 @@ impl DisplayAs for StageExec {
491
518
) ?;
492
519
493
520
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) ?;
495
522
let plan_str = plan_str
496
523
. split ( '\n' )
497
524
. filter ( |v| !v. is_empty ( ) )
@@ -511,7 +538,7 @@ impl DisplayAs for StageExec {
511
538
}
512
539
Some ( display_ctx) => {
513
540
for ( i, _) in self . tasks . iter ( ) . enumerate ( ) {
514
- let mut extra_spacing = "" . to_string ( ) ;
541
+ let mut extra_spacing = "" . to_string ( ) ;
515
542
if i > 0 {
516
543
writeln ! ( f) ?; // Add newline for each task
517
544
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 {
535
562
let mut plan_str = String :: new ( ) ;
536
563
let plan = match display_ctx. metrics . get ( & key) {
537
564
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 ( ) ) ;
539
567
if let Err ( e) = result {
540
568
write ! ( f, "Error enriching task with metrics: {}" , e) ?;
541
569
return Err ( std:: fmt:: Error ) ;
542
570
}
543
571
result. unwrap ( )
544
572
}
545
- None => {
546
- self . plan . clone ( )
547
- }
573
+ None => self . plan . clone ( ) ,
548
574
} ;
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
557
588
write ! (
558
589
f,
559
590
"{}{}{}" ,
560
591
" " . repeat( self . depth) ,
561
592
LDCORNER ,
562
593
HORIZONTAL . repeat( 50 )
563
- ) ?;
594
+ ) ?;
564
595
}
565
- return Ok ( ( ) ) ;
596
+ Ok ( ( ) )
566
597
}
567
598
}
568
599
}
@@ -604,7 +635,7 @@ fn format_task_for_stage(task_number: usize, head: &Arc<dyn ExecutionPlan>) -> S
604
635
. map ( |v| format ! ( "p{v}" ) )
605
636
. join ( "," ) ;
606
637
result += "] " ;
607
-
638
+
608
639
result
609
640
}
610
641
@@ -631,6 +662,28 @@ fn format_tasks_for_partition_isolator(
631
662
result
632
663
}
633
664
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
+
634
687
// num_colors must agree with the colorscheme selected from
635
688
// https://graphviz.org/doc/info/colors.html
636
689
const NUM_COLORS : usize = 6 ;
0 commit comments