Skip to content

Commit d6f90e3

Browse files
committed
stage display formatting as indent and tree done
1 parent 37e04c6 commit d6f90e3

File tree

4 files changed

+82
-31
lines changed

4 files changed

+82
-31
lines changed

src/physical_optimizer.rs

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,12 @@ struct StagePlanner {
105105
plan_head: Option<Arc<dyn ExecutionPlan>>,
106106
/// Current depth in the plan tree, as we walk the tree
107107
depth: usize,
108-
/// Input stages collected so far. Each entry is a tuple of (tree depth, stage).
108+
/// Input stages collected so far. Each entry is a tuple of (plan tree depth, stage).
109109
/// This allows us to keep track of the depth in the plan tree
110110
/// where we created the stage. That way when we create a new
111111
/// stage, we can tell if it is a peer to the current input stages or
112112
/// should be a parent (if its depth is a smaller number)
113-
input_stages: Vec<(usize, Arc<ExecutionStage>)>,
113+
input_stages: Vec<(usize, ExecutionStage)>,
114114
/// current stage number
115115
stage_counter: usize,
116116
/// Optional codec to assist in serializing and deserializing any custom
@@ -135,32 +135,45 @@ impl StagePlanner {
135135
}
136136

137137
fn finish(mut self) -> Result<Arc<ExecutionStage>> {
138-
if self.input_stages.is_empty() {
139-
Ok(Arc::new(ExecutionStage::new(
138+
let stage = if self.input_stages.is_empty() {
139+
ExecutionStage::new(
140140
self.stage_counter,
141141
self.plan_head
142142
.take()
143143
.ok_or_else(|| internal_datafusion_err!("No plan head set"))?,
144144
vec![],
145-
)))
145+
)
146146
} else if self.depth < self.input_stages[0].0 {
147147
// There is more plan above the last stage we created, so we need to
148148
// create a new stage that includes the last plan head
149-
Ok(Arc::new(ExecutionStage::new(
149+
ExecutionStage::new(
150150
self.stage_counter,
151151
self.plan_head
152152
.take()
153153
.ok_or_else(|| internal_datafusion_err!("No plan head set"))?,
154154
self.input_stages
155-
.iter()
156-
.map(|(_, stage)| stage.clone())
155+
.into_iter()
156+
.map(|(_, stage)| Arc::new(stage))
157157
.collect(),
158-
)))
158+
)
159159
} else {
160160
// We have a plan head, and we are at the same depth as the last stage we created,
161161
// so we can just return the last stage
162-
Ok(self.input_stages.last().unwrap().1.clone())
162+
self.input_stages.last().unwrap().1.clone()
163+
};
164+
165+
// assign the proper tree depth to each stage in the tree
166+
fn assign_tree_depth(stage: &ExecutionStage, depth: usize) {
167+
stage
168+
.depth
169+
.store(depth as u64, std::sync::atomic::Ordering::Relaxed);
170+
for input in stage.child_stages_iter() {
171+
assign_tree_depth(input, depth + 1);
172+
}
163173
}
174+
assign_tree_depth(&stage, 0);
175+
176+
Ok(Arc::new(stage))
164177
}
165178
}
166179

@@ -191,26 +204,13 @@ impl TreeNodeRewriter for StagePlanner {
191204
.map(|(_, stage)| stage.clone())
192205
.collect::<Vec<_>>();
193206

194-
println!(
195-
"\n\n\n ---------------------- \ncreating a stage, depth = {}, input_stages ={}, child_stages = {}, plan=\n{}",
196-
self.depth,
197-
self.input_stages
198-
.iter()
199-
.map(|(depth, stage)| format!("({},{})", depth, stage.num))
200-
.collect::<Vec<_>>()
201-
.join(", "),
202-
child_stages
203-
.iter()
204-
.map(|stage| format!("{}", stage.num))
205-
.collect::<Vec<_>>()
206-
.join(", "),
207-
208-
displayable(plan.as_ref()).indent(false)
209-
);
210-
211207
self.input_stages.retain(|(depth, _)| *depth <= self.depth);
212208

213-
let mut stage = ExecutionStage::new(self.stage_counter, plan.clone(), child_stages);
209+
let mut stage = ExecutionStage::new(
210+
self.stage_counter,
211+
plan.clone(),
212+
child_stages.into_iter().map(Arc::new).collect(),
213+
);
214214

215215
if let Some(partitions_per_task) = self.partitions_per_task {
216216
stage = stage.with_maximum_partitions_per_task(partitions_per_task);
@@ -219,7 +219,7 @@ impl TreeNodeRewriter for StagePlanner {
219219
stage = stage.with_codec(codec.clone());
220220
}
221221

222-
self.input_stages.push((self.depth, Arc::new(stage)));
222+
self.input_stages.push((self.depth, stage));
223223

224224
// As we are walking up the plan tree, we've now put what we've encountered so far
225225
// into a stage. We want to replace this plan now with an ArrowFlightReadExec

src/stage/display.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,51 @@ use crate::common::util::display_plan_with_partition_in_out;
3333

3434
use super::ExecutionStage;
3535

36+
// Unicode box-drawing characters for creating borders and connections.
37+
const LTCORNER: &str = "┌"; // Left top corner
38+
const RTCORNER: &str = "┐"; // Right top corner
39+
const LDCORNER: &str = "└"; // Left bottom corner
40+
const RDCORNER: &str = "┘"; // Right bottom corner
41+
42+
const TMIDDLE: &str = "┬"; // Top T-junction (connects down)
43+
const LMIDDLE: &str = "├"; // Left T-junction (connects right)
44+
const DMIDDLE: &str = "┴"; // Bottom T-junction (connects up)
45+
46+
const VERTICAL: &str = "│"; // Vertical line
47+
const HORIZONTAL: &str = "─"; // Horizontal line
48+
3649
impl DisplayAs for ExecutionStage {
3750
fn fmt_as(&self, t: DisplayFormatType, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3851
match t {
39-
DisplayFormatType::Default | DisplayFormatType::Verbose => {
52+
DisplayFormatType::Default => {
4053
write!(f, "{}", self.name)
4154
}
55+
DisplayFormatType::Verbose => {
56+
writeln!(
57+
f,
58+
"{}{}{}{}",
59+
LTCORNER,
60+
HORIZONTAL.repeat(5),
61+
format!(" {} ", self.name),
62+
HORIZONTAL.repeat(50 - 7 - self.name.len())
63+
)?;
64+
let plan_str = display_plan_with_partition_in_out(self.plan.as_ref())
65+
.map_err(|_| std::fmt::Error {})?;
66+
let plan_str = plan_str.replace(
67+
'\n',
68+
&format!("\n{}{}", " ".repeat(self.depth()), VERTICAL),
69+
);
70+
writeln!(f, "{}{}{}", " ".repeat(self.depth()), VERTICAL, plan_str)?;
71+
write!(
72+
f,
73+
"{}{}{}",
74+
" ".repeat(self.depth()),
75+
LDCORNER,
76+
HORIZONTAL.repeat(50)
77+
)?;
78+
79+
Ok(())
80+
}
4281
DisplayFormatType::TreeRender => write!(
4382
f,
4483
"{}",

src/stage/stage.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::any::Any;
2+
use std::sync::atomic::{AtomicU64, Ordering};
13
use std::sync::Arc;
24

35
use datafusion::error::Result;
@@ -7,6 +9,7 @@ use datafusion::prelude::SessionContext;
79
use datafusion_proto::physical_plan::PhysicalExtensionCodec;
810

911
use itertools::Itertools;
12+
use tokio::sync::RwLock;
1013

1114
use crate::task::ExecutionTask;
1215

@@ -30,6 +33,8 @@ pub struct ExecutionStage {
3033
pub tasks: Vec<ExecutionTask>,
3134
/// An optional codec to assist in serializing and deserializing this stage
3235
pub codec: Option<Arc<dyn PhysicalExtensionCodec>>,
36+
/// tree depth of our location in the stage tree, used for display only
37+
pub(crate) depth: AtomicU64,
3338
}
3439

3540
impl Clone for ExecutionStage {
@@ -43,6 +48,7 @@ impl Clone for ExecutionStage {
4348
inputs: self.inputs.to_vec(),
4449
tasks: self.tasks.clone(),
4550
codec: self.codec.clone(),
51+
depth: AtomicU64::new(self.depth.load(Ordering::Relaxed)),
4652
}
4753
}
4854
}
@@ -78,6 +84,7 @@ impl ExecutionStage {
7884
partition_group,
7985
}],
8086
codec: None,
87+
depth: AtomicU64::new(0),
8188
}
8289
}
8390

@@ -138,6 +145,10 @@ impl ExecutionStage {
138145
};
139146
format!("Stage {:<3}{}", self.num, child_str)
140147
}
148+
149+
pub(crate) fn depth(&self) -> usize {
150+
self.depth.load(Ordering::Relaxed) as usize
151+
}
141152
}
142153

143154
impl ExecutionPlan for ExecutionStage {
@@ -164,6 +175,7 @@ impl ExecutionPlan for ExecutionStage {
164175
inputs: children,
165176
tasks: self.tasks.clone(),
166177
codec: self.codec.clone(),
178+
depth: AtomicU64::new(self.depth.load(Ordering::Relaxed)),
167179
}))
168180
}
169181

src/task.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Display for ExecutionTask {
9696
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
9797
write!(
9898
f,
99-
"Task[partitions: {}, {}]",
99+
"Task: partitions: {}\n{}]",
100100
format_pg(&self.partition_group),
101101
self.worker_addr
102102
.as_ref()

0 commit comments

Comments
 (0)