Skip to content

Commit e623085

Browse files
committed
Refactor stage struct
# Conflicts: # src/execution_plans/distributed.rs
1 parent c5d130a commit e623085

15 files changed

Lines changed: 339 additions & 274 deletions

File tree

benchmarks/src/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ impl RunOpt {
304304
let mut n_tasks = 0;
305305
physical_plan.clone().transform_down(|node| {
306306
if let Some(node) = node.as_network_boundary() {
307-
n_tasks += node.input_stage().tasks.len()
307+
n_tasks += node.input_stage().task_count()
308308
}
309309
Ok(Transformed::no(node))
310310
})?;

console/examples/tpcds_runner.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ async fn run_single_query(
189189
let batches = stream.try_collect::<Vec<_>>().await?;
190190
if explain_analyze {
191191
let output =
192-
datafusion_distributed::explain_analyze(plan, DistributedMetricsFormat::Aggregated)?;
192+
datafusion_distributed::explain_analyze(plan, DistributedMetricsFormat::Aggregated)
193+
.await?;
193194
println!("{output}");
194195
}
195196
Ok(batches)

src/execution_plans/benchmarks/shuffle_bench.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use super::fixture::{
22
InMemoryChannelsResolver, benchmark_schema, make_input_partitions, rows_for_producer,
33
};
44
use crate::common::task_ctx_with_extension;
5+
use crate::stage::RemoteStage;
56
use crate::worker::WorkerConnectionPool;
67
use crate::worker::test_utils::worker_handles::MemoryWorkerHandle;
7-
use crate::{DistributedExt, DistributedTaskContext, ExecutionTask, NetworkShuffleExec, Stage};
8+
use crate::{DistributedExt, DistributedTaskContext, NetworkShuffleExec, Stage};
89
use arrow::datatypes::Schema;
910
use arrow_ipc::CompressionType;
1011
use datafusion::common::Result;
@@ -165,16 +166,14 @@ impl ShuffleBench {
165166
.build()
166167
.task_ctx();
167168
let input_stage_tasks = (0..bench.producer_tasks)
168-
.map(|i| ExecutionTask {
169-
url: Some(Url::parse(&format!("http://localhost:{i}")).unwrap()),
170-
})
169+
.map(|i| Url::parse(&format!("http://localhost:{i}")).unwrap())
171170
.collect();
172171

173172
Ok(ShuffleFixture {
174173
bench,
175174
schema,
176175
task_ctx,
177-
input_stage_tasks,
176+
input_stage_workers: input_stage_tasks,
178177
workers,
179178
})
180179
}
@@ -190,7 +189,7 @@ pub struct ShuffleFixture {
190189
bench: ShuffleBench,
191190
schema: Arc<Schema>,
192191
task_ctx: Arc<datafusion::execution::TaskContext>,
193-
input_stage_tasks: Vec<ExecutionTask>,
192+
input_stage_workers: Vec<Url>,
194193
workers: Vec<MemoryWorkerHandle>,
195194
}
196195

@@ -213,12 +212,11 @@ impl ShuffleFixture {
213212
.await?;
214213
}
215214

216-
let input_stage = Stage {
215+
let input_stage = Stage::Remote(RemoteStage {
217216
query_id,
218217
num: 0,
219-
plan: None,
220-
tasks: self.input_stage_tasks.clone(),
221-
};
218+
workers: self.input_stage_workers.clone(),
219+
});
222220

223221
let mut join_set = JoinSet::default();
224222
for task_index in 0..self.bench.consumer_tasks {

src/execution_plans/benchmarks/transport_bench.rs

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use super::fixture::{
22
InMemoryChannelsResolver, benchmark_schema, make_input_partitions, rows_for_producer,
33
};
44
use crate::common::task_ctx_with_extension;
5+
use crate::stage::RemoteStage;
56
use crate::worker::test_utils::worker_handles::{MemoryWorkerHandle, TcpWorkerHandle};
67
use crate::{
7-
DefaultChannelResolver, DistributedExt, DistributedTaskContext, ExecutionTask,
8-
NetworkShuffleExec, Stage,
8+
DefaultChannelResolver, DistributedExt, DistributedTaskContext, NetworkShuffleExec, Stage,
99
};
1010
use arrow::datatypes::Schema;
1111
use arrow_ipc::CompressionType;
@@ -202,9 +202,7 @@ impl TransportBench {
202202
.build()
203203
.task_ctx(),
204204
input_stage_tasks: (0..self.producer_tasks)
205-
.map(|i| ExecutionTask {
206-
url: Some(Url::parse(&format!("http://localhost:{i}")).unwrap()),
207-
})
205+
.map(|i| Url::parse(&format!("http://localhost:{i}")).unwrap())
208206
.collect(),
209207
workers: PreparedTransportWorkers::InMemory(workers),
210208
})
@@ -239,12 +237,7 @@ impl TransportBench {
239237
.with_distributed_compression(self.compression)?
240238
.build()
241239
.task_ctx(),
242-
input_stage_tasks: workers
243-
.iter()
244-
.map(|worker| ExecutionTask {
245-
url: Some(worker.url.clone()),
246-
})
247-
.collect(),
240+
input_stage_tasks: workers.iter().map(|worker| worker.url.clone()).collect(),
248241
workers: PreparedTransportWorkers::Tcp(workers),
249242
})
250243
}
@@ -260,7 +253,7 @@ pub struct TransportFixture {
260253
bench: TransportBench,
261254
schema: Arc<Schema>,
262255
task_ctx: Arc<datafusion::execution::TaskContext>,
263-
input_stage_tasks: Vec<ExecutionTask>,
256+
input_stage_tasks: Vec<Url>,
264257
workers: PreparedTransportWorkers,
265258
}
266259

@@ -269,12 +262,11 @@ impl TransportFixture {
269262
let query_id = Uuid::new_v4();
270263
self.workers.register_plans(query_id).await?;
271264

272-
let input_stage = Stage {
265+
let input_stage = Stage::Remote(RemoteStage {
273266
query_id,
274267
num: 0,
275-
plan: None,
276-
tasks: self.input_stage_tasks.clone(),
277-
};
268+
workers: self.input_stage_tasks.clone(),
269+
});
278270

279271
let mut join_set = JoinSet::default();
280272
for task_index in 0..self.bench.consumer_tasks {

src/execution_plans/distributed.rs

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::execution_plans::ChildrenIsolatorUnionExec;
55
use crate::networking::get_distributed_worker_resolver;
66
use crate::passthrough_headers::get_passthrough_headers;
77
use crate::protobuf::{DistributedCodec, tonic_status_to_datafusion_error};
8-
use crate::stage::{ExecutionTask, Stage};
8+
use crate::stage::{LocalStage, RemoteStage, Stage};
99
use crate::worker::generated::worker as pb;
1010
use crate::worker::generated::worker::set_plan_request::WorkUnitFeedDeclaration;
1111
use crate::worker::generated::worker::{
@@ -21,7 +21,7 @@ use datafusion::common::HashMap;
2121
use datafusion::common::instant::Instant;
2222
use datafusion::common::runtime::JoinSet;
2323
use datafusion::common::tree_node::{Transformed, TreeNode, TreeNodeRecursion};
24-
use datafusion::common::{Result, exec_err, internal_err};
24+
use datafusion::common::{Result, exec_err};
2525
use datafusion::common::{exec_datafusion_err, internal_datafusion_err};
2626
use datafusion::error::DataFusionError;
2727
use 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

373376
impl<'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

Comments
 (0)