Skip to content

Commit e14cc15

Browse files
committed
Refactor impl_set_plan.rs into impl_coordinator_channel.rs
1 parent 8285ee3 commit e14cc15

5 files changed

Lines changed: 116 additions & 115 deletions

File tree

src/work_unit_feed/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ mod work_unit_feed;
55
mod work_unit_feed_provider;
66
mod work_unit_feed_registry;
77

8-
pub(crate) use remote_work_unit_feed::{RemoteWorkUnitFeedRegistry, RemoteWorkUnitFeedTxs};
8+
pub(crate) use remote_work_unit_feed::RemoteWorkUnitFeedRegistry;
99
pub(crate) use work_unit_feed_registry::{WorkUnitFeedRegistry, set_distributed_work_unit_feed};
1010

1111
pub use work_unit::WorkUnit;
Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,44 @@
11
use crate::common::deserialize_uuid;
2-
use crate::config_extension_ext::set_distributed_option_extension_from_headers;
3-
use crate::protobuf::DistributedCodec;
4-
use crate::work_unit_feed::{RemoteWorkUnitFeedRegistry, RemoteWorkUnitFeedTxs};
2+
use crate::work_unit_feed::RemoteWorkUnitFeedRegistry;
3+
use crate::worker::LocalWorkerContext;
4+
use crate::worker::generated::worker::coordinator_to_worker_msg::Inner;
55
use crate::worker::generated::worker::set_plan_request::WorkUnitFeedDeclaration;
6-
use crate::worker::generated::worker::{PreOrderTaskMetrics, SetPlanRequest};
7-
use crate::worker::worker_connection_pool::LocalWorkerContext;
8-
use crate::{DistributedConfig, DistributedTaskContext, Worker, WorkerQueryContext};
9-
use datafusion::error::DataFusionError;
10-
use datafusion::execution::{SessionStateBuilder, TaskContext};
11-
use datafusion::physical_plan::ExecutionPlan;
6+
use crate::worker::generated::worker::worker_service_server::WorkerService;
7+
use crate::worker::generated::worker::{
8+
CoordinatorToWorkerMsg, WorkerToCoordinatorMsg, worker_to_coordinator_msg,
9+
};
10+
use crate::{
11+
DistributedCodec, DistributedConfig, DistributedExt, DistributedTaskContext, TaskData, Worker,
12+
WorkerQueryContext,
13+
};
14+
use datafusion::common::DataFusionError;
15+
use datafusion::execution::SessionStateBuilder;
1216
use datafusion::prelude::SessionConfig;
1317
use datafusion_proto::physical_plan::AsExecutionPlan;
1418
use datafusion_proto::protobuf::PhysicalPlanNode;
19+
use futures::{FutureExt, StreamExt};
1520
use std::sync::Arc;
1621
use std::sync::atomic::AtomicUsize;
1722
use tokio::sync::oneshot;
18-
use tonic::Status;
19-
use tonic::metadata::MetadataMap;
23+
use tonic::{Request, Response, Status, Streaming};
2024
use url::Url;
2125

22-
#[derive(Clone, Debug)]
23-
/// TaskData stores state for a single task being executed by this Endpoint. It may be shared
24-
/// by concurrent requests for the same task which execute separate partitions.
25-
pub struct TaskData {
26-
/// Task context suitable for execute different partitions from the same task.
27-
pub(super) task_ctx: Arc<TaskContext>,
28-
/// Plan to be executed.
29-
pub(crate) plan: Arc<dyn ExecutionPlan>,
30-
/// `num_partitions_remaining` is initialized to the total number of partitions in the task (not
31-
/// only tasks in the partition group). This is decremented for each request to the endpoint
32-
/// for this task. Once this count is zero, the task is likely complete. The task may not be
33-
/// complete because it's possible that the same partition was retried and this count was
34-
/// decremented more than once for the same partition.
35-
pub(super) num_partitions_remaining: Arc<AtomicUsize>,
36-
/// Sender half of the metrics channel. `impl_execute_task` takes this (via `Option::take`)
37-
/// once all partitions have finished or been dropped, sending the collected metrics back to
38-
/// the coordinator through the `CoordinatorChannel` side channel.
39-
pub(super) metrics_tx: Arc<std::sync::Mutex<Option<oneshot::Sender<PreOrderTaskMetrics>>>>,
40-
}
41-
42-
impl TaskData {
43-
/// Returns the number of partitions remaining to be processed.
44-
pub(crate) fn num_partitions_remaining(&self) -> usize {
45-
self.num_partitions_remaining
46-
.load(std::sync::atomic::Ordering::Relaxed)
47-
}
48-
49-
/// Returns the total number of partitions in this task.
50-
pub(crate) fn total_partitions(&self) -> usize {
51-
self.plan.properties().partitioning.partition_count()
52-
}
53-
}
54-
5526
impl Worker {
56-
/// Sets the plan for a task and returns a receiver that will yield the collected metrics
57-
/// once all partitions of that task have finished executing (or been dropped early).
58-
pub(crate) async fn impl_set_plan(
27+
pub(super) async fn impl_coordinator_channel(
5928
&self,
60-
request: SetPlanRequest,
61-
grpc_headers: MetadataMap,
62-
) -> Result<
63-
(
64-
RemoteWorkUnitFeedTxs,
65-
oneshot::Receiver<PreOrderTaskMetrics>,
66-
),
67-
Status,
68-
> {
29+
request: Request<Streaming<CoordinatorToWorkerMsg>>,
30+
) -> Result<Response<<Worker as WorkerService>::CoordinatorChannelStream>, Status> {
31+
let (grpc_headers, _ext, mut body) = request.into_parts();
32+
33+
// The first message must be a SetPlanRequest.
34+
let Some(msg) = body.next().await else {
35+
return Err(Status::internal("Empty Coordinator stream"));
36+
};
37+
let Some(Inner::SetPlanRequest(request)) = msg?.inner else {
38+
return Err(Status::internal(
39+
"First Coordinator message must be SetPlanRequest",
40+
));
41+
};
6942
let key = request.task_key.ok_or_else(missing("task_key"))?;
7043

7144
let entry = self
@@ -94,11 +67,12 @@ impl Worker {
9467
.with_extension(Arc::new(LocalWorkerContext {
9568
self_url: Url::parse(&request.target_worker_url)
9669
.map_err(|e| DataFusionError::External(Box::new(e)))?,
97-
}));
98-
set_distributed_option_extension_from_headers::<DistributedConfig>(&mut cfg, &headers)?;
70+
}))
71+
.with_distributed_option_extension_from_headers::<DistributedConfig>(&headers)?;
9972

100-
let shuffle_batch_size =
101-
DistributedConfig::from_config_options(cfg.options())?.shuffle_batch_size;
73+
let d_cfg = DistributedConfig::from_config_options(cfg.options())?;
74+
let shuffle_batch_size = d_cfg.shuffle_batch_size;
75+
let collect_metrics = d_cfg.collect_metrics;
10276
if shuffle_batch_size != 0 {
10377
cfg = cfg.with_batch_size(shuffle_batch_size);
10478
}
@@ -129,7 +103,10 @@ impl Worker {
129103
plan,
130104
task_ctx,
131105
num_partitions_remaining: Arc::new(AtomicUsize::new(total_partitions)),
132-
metrics_tx: Arc::new(std::sync::Mutex::new(Some(metrics_tx))),
106+
metrics_tx: match collect_metrics {
107+
true => Arc::new(std::sync::Mutex::new(Some(metrics_tx))),
108+
false => Arc::new(std::sync::Mutex::new(None)),
109+
},
133110
})
134111
};
135112

@@ -138,7 +115,39 @@ impl Worker {
138115
"Logic error while setting plan for TaskKey {key:?}: the plan was set twice. This is a bug in datafusion-distributed, please report it."
139116
))
140117
})?;
141-
Ok((remote_work_unit_feed_registry.senders, metrics_rx))
118+
119+
// Continue reading remaining messages (work unit feed data) in the background.
120+
let work_unit_senders = remote_work_unit_feed_registry.senders;
121+
tokio::spawn(async move {
122+
while let Some(Ok(msg)) = body.next().await {
123+
let Some(Inner::WorkUnit(msg)) = msg.inner else {
124+
continue;
125+
};
126+
let Ok(id) = deserialize_uuid(&msg.id) else {
127+
continue;
128+
};
129+
let Some(tx) = work_unit_senders.get(&(id, msg.partition as usize)) else {
130+
continue;
131+
};
132+
if tx.send(Ok(msg.body)).is_err() {
133+
break; // channel closed
134+
}
135+
}
136+
});
137+
138+
// Stream back the metrics once the task finishes executing.
139+
// The oneshot receiver resolves when impl_execute_task sends the collected
140+
// metrics after all partitions have finished or been dropped.
141+
let metrics_stream = metrics_rx.into_stream();
142+
let metrics_stream = metrics_stream.filter_map(|task_metrics| async move {
143+
match task_metrics {
144+
Ok(task_metrics) => Some(WorkerToCoordinatorMsg {
145+
inner: Some(worker_to_coordinator_msg::Inner::TaskMetrics(task_metrics)),
146+
}),
147+
Err(_) => None, // channel dropped without sending any message
148+
}
149+
});
150+
Ok(Response::new(metrics_stream.map(Ok).boxed()))
142151
}
143152
}
144153

src/worker/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
pub(crate) mod generated;
2+
mod impl_coordinator_channel;
23
mod impl_execute_task;
3-
mod impl_set_plan;
44
mod session_builder;
55
mod single_write_multi_read;
66
mod spawn_select_all;
7+
mod task_data;
78
#[cfg(any(test, feature = "integration"))]
89
pub(crate) mod test_utils;
910
mod worker_connection_pool;
@@ -18,4 +19,4 @@ pub use session_builder::{
1819
};
1920
pub use worker_service::Worker;
2021

21-
pub use impl_set_plan::TaskData;
22+
pub use task_data::TaskData;

src/worker/task_data.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use crate::worker::generated::worker::PreOrderTaskMetrics;
2+
use datafusion::execution::TaskContext;
3+
use datafusion::physical_plan::ExecutionPlan;
4+
use std::sync::Arc;
5+
use std::sync::atomic::AtomicUsize;
6+
use tokio::sync::oneshot;
7+
8+
#[derive(Clone, Debug)]
9+
/// TaskData stores state for a single task being executed by this Endpoint. It may be shared
10+
/// by concurrent requests for the same task which execute separate partitions.
11+
pub struct TaskData {
12+
/// Task context suitable for execute different partitions from the same task.
13+
pub(super) task_ctx: Arc<TaskContext>,
14+
/// Plan to be executed.
15+
pub(crate) plan: Arc<dyn ExecutionPlan>,
16+
/// `num_partitions_remaining` is initialized to the total number of partitions in the task (not
17+
/// only tasks in the partition group). This is decremented for each request to the endpoint
18+
/// for this task. Once this count is zero, the task is likely complete. The task may not be
19+
/// complete because it's possible that the same partition was retried and this count was
20+
/// decremented more than once for the same partition.
21+
pub(super) num_partitions_remaining: Arc<AtomicUsize>,
22+
/// Sender half of the metrics channel. `impl_execute_task` takes this (via `Option::take`)
23+
/// once all partitions have finished or been dropped, sending the collected metrics back to
24+
/// the coordinator through the `CoordinatorChannel` side channel.
25+
pub(super) metrics_tx: Arc<std::sync::Mutex<Option<oneshot::Sender<PreOrderTaskMetrics>>>>,
26+
}
27+
28+
impl TaskData {
29+
/// Returns the number of partitions remaining to be processed.
30+
pub(crate) fn num_partitions_remaining(&self) -> usize {
31+
self.num_partitions_remaining
32+
.load(std::sync::atomic::Ordering::Relaxed)
33+
}
34+
35+
/// Returns the total number of partitions in this task.
36+
pub(crate) fn total_partitions(&self) -> usize {
37+
self.plan.properties().partitioning.partition_count()
38+
}
39+
}

src/worker/worker_service.rs

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
use crate::common::deserialize_uuid;
21
use crate::worker::WorkerSessionBuilder;
3-
use crate::worker::generated::worker::coordinator_to_worker_msg::Inner;
42
use crate::worker::generated::worker::worker_service_server::{WorkerService, WorkerServiceServer};
5-
use crate::worker::generated::worker::worker_to_coordinator_msg;
63
use crate::worker::generated::worker::{
74
CoordinatorToWorkerMsg, ExecuteTaskRequest, TaskKey, WorkerToCoordinatorMsg,
85
};
9-
use crate::worker::impl_set_plan::TaskData;
106
use crate::worker::single_write_multi_read::SingleWriteMultiRead;
7+
use crate::worker::task_data::TaskData;
118
use crate::{
129
DefaultSessionBuilder, ObservabilityServiceImpl, ObservabilityServiceServer, WorkerResolver,
1310
};
@@ -16,7 +13,6 @@ use async_trait::async_trait;
1613
use datafusion::common::DataFusionError;
1714
use datafusion::execution::runtime_env::RuntimeEnv;
1815
use datafusion::physical_plan::ExecutionPlan;
19-
use futures::StreamExt;
2016
use moka::future::Cache;
2117
use std::borrow::Cow;
2218
use std::sync::Arc;
@@ -186,51 +182,7 @@ impl WorkerService for Worker {
186182
&self,
187183
request: Request<Streaming<CoordinatorToWorkerMsg>>,
188184
) -> Result<Response<Self::CoordinatorChannelStream>, Status> {
189-
let (grpc_headers, _ext, mut body) = request.into_parts();
190-
191-
// The first message must be a SetPlanRequest.
192-
let Some(msg) = body.next().await else {
193-
return Err(Status::internal("Empty Coordinator stream"));
194-
};
195-
let Some(Inner::SetPlanRequest(request)) = msg?.inner else {
196-
return Err(Status::internal(
197-
"First Coordinator message must be SetPlanRequest",
198-
));
199-
};
200-
let (work_unit_senders, metrics_rx) = self.impl_set_plan(request, grpc_headers).await?;
201-
202-
// Continue reading remaining messages (work unit feed data) in the background.
203-
tokio::spawn(async move {
204-
while let Some(Ok(msg)) = body.next().await {
205-
let Some(Inner::WorkUnit(msg)) = msg.inner else {
206-
continue;
207-
};
208-
let Ok(id) = deserialize_uuid(&msg.id) else {
209-
continue;
210-
};
211-
let Some(tx) = work_unit_senders.get(&(id, msg.partition as usize)) else {
212-
continue;
213-
};
214-
if tx.send(Ok(msg.body)).is_err() {
215-
break; // channel closed
216-
}
217-
}
218-
});
219-
220-
// Stream back the metrics once the task finishes executing.
221-
// The oneshot receiver resolves when impl_execute_task sends the collected
222-
// metrics after all partitions have finished or been dropped.
223-
let stream = futures::stream::once(async move {
224-
match metrics_rx.await {
225-
Ok(task_metrics) => Ok(WorkerToCoordinatorMsg {
226-
inner: Some(worker_to_coordinator_msg::Inner::TaskMetrics(task_metrics)),
227-
}),
228-
Err(_) => Err(Status::internal(
229-
"Metrics channel closed before metrics were sent",
230-
)),
231-
}
232-
});
233-
Ok(Response::new(stream.boxed()))
185+
self.impl_coordinator_channel(request).await
234186
}
235187

236188
type ExecuteTaskStream = BoxStream<FlightData>;

0 commit comments

Comments
 (0)