datafusion_distributed/worker/
impl_coordinator_channel.rs1use crate::common::TreeNodeExt;
2use crate::events::{WorkerPlanRewriteEvent, WorkerPlanRewriteHandlers};
3use crate::execution_plans::SamplerExec;
4use crate::protocol::LocalWorkerContext;
5use crate::work_unit_feed::{RemoteWorkUnitFeedRegistry, set_work_unit_received_time};
6use crate::worker::task_data::TaskDataMetrics;
7use crate::{
8 CoordinatorToWorkerMsg, DistributedConfig, DistributedExt, DistributedTaskContext,
9 SetPlanRequest, TaskData, TaskMetrics, Worker, WorkerQueryContext, WorkerToCoordinatorMsg,
10};
11use datafusion::common::tree_node::TreeNodeRecursion;
12use datafusion::common::{DataFusionError, Result, exec_datafusion_err};
13use datafusion::execution::SessionStateBuilder;
14use datafusion::physical_plan::ExecutionPlan;
15use datafusion::prelude::SessionConfig;
16use futures::stream::{BoxStream, FuturesUnordered};
17use futures::{FutureExt, StreamExt, TryStreamExt};
18use http::HeaderMap;
19use std::sync::{Arc, OnceLock};
20use tokio::sync::oneshot;
21use tokio::sync::oneshot::Sender;
22
23impl Worker {
24 pub async fn coordinator_channel(
25 &self,
26 headers: HeaderMap,
27 request: SetPlanRequest,
28 stream: BoxStream<'static, Result<CoordinatorToWorkerMsg>>,
29 ) -> Result<BoxStream<'static, Result<WorkerToCoordinatorMsg>>> {
30 let key = request.task_key;
31
32 let entry = self
33 .task_data_entries
34 .get_with(key, async { Default::default() })
35 .await;
36
37 let mut remote_work_unit_feed_registry = RemoteWorkUnitFeedRegistry::default();
38 for decl in request.work_unit_feed_declarations {
39 remote_work_unit_feed_registry.add(decl.id, decl.partitions);
40 }
41
42 let (metrics_tx, metrics_rx) = oneshot::channel();
43 let mut load_info_rxs = vec![];
44
45 let task_data = || async {
46 let mut cfg = SessionConfig::default()
47 .with_extension(Arc::new(remote_work_unit_feed_registry.receivers))
48 .with_extension(Arc::new(DistributedTaskContext {
49 task_index: request.task_key.task_number,
50 task_count: request.task_count,
51 }))
52 .with_extension(Arc::new(LocalWorkerContext {
53 local_worker: self.clone(),
54 self_url: request.target_worker_url,
55 }))
56 .with_distributed_option_extension_from_headers::<DistributedConfig>(&headers)?;
57
58 let d_cfg = DistributedConfig::from_config_options(cfg.options())?;
59 let shuffle_batch_size = d_cfg.shuffle_batch_size;
60 let collect_metrics = d_cfg.collect_metrics;
61 if shuffle_batch_size != 0 {
62 cfg = cfg.with_batch_size(shuffle_batch_size);
63 }
64
65 let session_state = self
66 .session_builder
67 .build_session_state(WorkerQueryContext {
68 builder: SessionStateBuilder::new()
69 .with_default_features()
70 .with_config(cfg)
71 .with_runtime_env(Arc::clone(&self.runtime)),
72 headers,
73 })
74 .await?;
75
76 let task_ctx = session_state.task_ctx();
77 let plan = request.plan.decode(&task_ctx)?;
78
79 let ev = WorkerPlanRewriteEvent {
80 plan,
81 session_config: session_state.config(),
82 };
83 let plan = WorkerPlanRewriteHandlers::handle(ev)?.plan;
84 load_info_rxs =
85 SamplerExec::kick_off_first_sampler(Arc::clone(&plan), Arc::clone(&task_ctx))?;
86
87 Ok::<_, DataFusionError>(TaskData {
89 base_plan: plan,
90 final_plan: Arc::new(OnceLock::new()),
91 task_ctx,
92 metrics_tx: match collect_metrics {
93 true => Arc::new(std::sync::Mutex::new(Some(metrics_tx))),
94 false => Arc::new(std::sync::Mutex::new(None)),
95 },
96 task_data_metrics: Arc::new(TaskDataMetrics::new(request.query_start_time_ns)),
97 })
98 };
99
100 let task_data_result = task_data().await.map_err(Arc::new);
101
102 entry
103 .write(task_data_result.clone())
104 .map_err(|e| exec_datafusion_err!("{e}"))?;
105
106 let task_data = task_data_result.map_err(DataFusionError::Shared)?;
107
108 let mut work_unit_senders = Some(remote_work_unit_feed_registry.senders);
110 let task_data_entries = Arc::clone(&self.task_data_entries);
111
112 #[allow(clippy::disallowed_methods)]
124 tokio::spawn(async move {
125 let mut stream = stream.map_ok(set_work_unit_received_time);
126 while let Some(Ok(msg)) = stream.next().await {
127 match msg {
128 CoordinatorToWorkerMsg::WorkUnitBatch(work_unit_batch) => {
129 let Some(work_unit_senders) = work_unit_senders.as_mut() else {
130 continue;
131 };
132 for wu in work_unit_batch.batch {
133 let id = wu.id;
134 let partition = wu.partition;
135 let Some(tx) = work_unit_senders.get(&(wu.id, partition)) else {
136 continue;
137 };
138 if tx.send(Ok(wu)).is_err() {
139 work_unit_senders.remove(&(id, partition));
142 continue;
143 }
144 }
145 }
146 CoordinatorToWorkerMsg::WorkUnitEos => {
147 let _ = work_unit_senders.take();
155 }
156 }
157 }
158
159 let metrics_tx = task_data.metrics_tx.lock().unwrap().take();
160 if let Some(Ok(plan)) = task_data.final_plan.get() {
161 let d_ctx = DistributedTaskContext {
162 task_index: key.task_number,
163 task_count: request.task_count,
164 };
165 let task_data_metrics = &task_data.task_data_metrics;
166 task_data_metrics.mark_execution_finished();
167 if let Some(metrics_tx) = metrics_tx {
168 send_metrics_via_channel(metrics_tx, plan, d_ctx, task_data_metrics);
169 }
170 }
171 task_data_entries.invalidate(&key).await
172 });
173
174 let load_info_stream = FuturesUnordered::from_iter(load_info_rxs)
175 .filter_map(async |load_info_or_channel_dropped| {
176 let load_info = load_info_or_channel_dropped.ok()?;
178 Some(WorkerToCoordinatorMsg::LoadInfo(load_info))
179 })
180 .chain(futures::stream::once(async move {
181 WorkerToCoordinatorMsg::LoadInfoEos
182 }));
183
184 let metrics_stream = metrics_rx.into_stream();
188 let metrics_stream = metrics_stream.filter_map(async |task_metrics_or_channel_dropped| {
189 let task_metrics = task_metrics_or_channel_dropped.ok()?;
190 Some(WorkerToCoordinatorMsg::TaskMetrics(task_metrics))
191 });
192
193 Ok(futures::stream::select(load_info_stream, metrics_stream)
194 .map(Ok)
195 .boxed())
196 }
197}
198
199fn send_metrics_via_channel(
202 metrics_tx: Sender<TaskMetrics>,
203 plan: &Arc<dyn ExecutionPlan>,
204 dt_ctx: DistributedTaskContext,
205 task_data_metrics: &Arc<TaskDataMetrics>,
206) {
207 let mut pre_order_plan_metrics = vec![];
208 let _ = plan.apply_with_dt_ctx(dt_ctx, |node, _| {
209 pre_order_plan_metrics.push(node.metrics().unwrap_or_default());
210 Ok(TreeNodeRecursion::Continue)
211 });
212
213 let _ = metrics_tx.send(TaskMetrics {
215 pre_order_plan_metrics,
216 task_metrics: task_data_metrics.to_metrics_set(),
217 });
218}