Skip to content

Commit fdb1562

Browse files
committed
Add dynamic stage built events
1 parent 3c8bb87 commit fdb1562

8 files changed

Lines changed: 152 additions & 19 deletions

File tree

src/coordinator/prepare_dynamic_plan.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ use crate::distributed_planner::{
55
InjectNetworkBoundaryContext, NetworkBoundaryBuilderResult, ProducerHead, calculate_cost,
66
inject_network_boundaries,
77
};
8+
use crate::events::DynamicStageBuiltHandlers;
89
use crate::events::TaskCountAnnotation::{Desired, Maximum};
910
use crate::execution_plans::SamplerExec;
1011
use crate::stage::{LocalStage, RemoteStage};
1112
use crate::{
12-
BytesCounterMetric, LoadInfo, MaxGaugeMetric, NetworkBoundaryExt, NetworkCoalesceExec, Stage,
13+
BytesCounterMetric, DynamicStageBuiltEvent, LoadInfo, MaxGaugeMetric, NetworkBoundaryExt,
14+
NetworkCoalesceExec, Stage,
1315
};
1416
use dashmap::DashMap;
1517
use datafusion::common::stats::Precision;
@@ -69,6 +71,15 @@ pub(super) async fn prepare_dynamic_plan(
6971
.task_count(&input_stage.plan)?
7072
.merge(Desired(compute_based_task_count));
7173

74+
let ev = DynamicStageBuiltEvent {
75+
session_config: nb_ctx.cfg,
76+
cost,
77+
plan: &input_stage.plan,
78+
};
79+
if let Some(response) = DynamicStageBuiltHandlers::handle(ev).transpose()? {
80+
input_stage.plan = response.plan
81+
};
82+
7283
// Propagate the final task_count inferred based on runtime statistics and compute cost.
7384
// Here is where leaf nodes are scaled up by ScaleUpLeafNodeHandler, and the
7485
// plan is finally left ready for distribution.

src/distributed_ext.rs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ use crate::config_extension_ext::{
33
set_distributed_option_extension, set_distributed_option_extension_from_headers,
44
};
55
use crate::events::{
6-
DesiredTaskCountHandler, DesiredTaskCountHandlers, RouteTasksHandler, RouteTasksHandlers,
7-
ScaleUpLeafNodeHandler, ScaleUpLeafNodeHandlers, WorkerPlanRewriteHandler,
8-
WorkerPlanRewriteHandlers,
6+
DesiredTaskCountHandler, DesiredTaskCountHandlers, DynamicStageBuiltHandlers,
7+
RouteTasksHandler, RouteTasksHandlers, ScaleUpLeafNodeHandler, ScaleUpLeafNodeHandlers,
8+
WorkerPlanRewriteHandler, WorkerPlanRewriteHandlers,
99
};
1010
use crate::passthrough_headers::set_passthrough_headers;
1111
use crate::protocol::set_distributed_channel_resolver;
1212
use crate::work_unit_feed::set_distributed_work_unit_feed;
1313
use crate::worker_resolver::set_distributed_worker_resolver;
1414
use crate::{
15-
ChannelResolver, DistributedConfig, LocalWorkerContext, WorkUnitFeed, WorkUnitFeedProvider,
16-
WorkerResolver, get_distributed_worker_resolver,
15+
ChannelResolver, DistributedConfig, DynamicStageBuiltHandler, LocalWorkerContext, WorkUnitFeed,
16+
WorkUnitFeedProvider, WorkerResolver, get_distributed_worker_resolver,
1717
};
1818
use datafusion::common::DataFusionError;
1919
use datafusion::config::ConfigExtension;
@@ -737,6 +737,39 @@ pub trait DistributedExt: Sized {
737737
&mut self,
738738
handler: T,
739739
);
740+
741+
/// Register an event handler that fires every time a new stage is built during dynamic
742+
/// planning.
743+
///
744+
/// In this handler, users can inspect the plan that is about to be sent to a remote worker,
745+
/// optimize it, or short circuit execution.
746+
///
747+
/// ```rust
748+
/// # use datafusion::common::{exec_err, Result};
749+
/// # use datafusion::execution::SessionStateBuilder;
750+
/// # use datafusion_distributed::{DynamicStageBuiltEvent, DynamicStageBuiltEventResponse};
751+
///
752+
/// fn handle_dynamic_stage_built(event: DynamicStageBuiltEvent) -> Option<Result<DynamicStageBuiltEventResponse>> {
753+
/// if event.cost.cpu.get_value().unwrap_or(&0) > 1024 * 1024 * 1024 {
754+
/// return Some(exec_err!("Plan is too expensive to execute"))
755+
/// }
756+
/// None
757+
/// }
758+
///
759+
/// SessionStateBuilder::new()
760+
/// .with_distributed_dynamic_stage_built_handler(handle_dynamic_stage_built);
761+
/// ```
762+
fn with_distributed_dynamic_stage_built_handler<T: DynamicStageBuiltHandler>(
763+
self,
764+
handler: T,
765+
) -> Self;
766+
767+
/// Same as [DistributedExt::with_distributed_dynamic_stage_built_handler] but with an
768+
/// in-place mutation.
769+
fn set_distributed_dynamic_stage_built_handler<T: DynamicStageBuiltHandler>(
770+
&mut self,
771+
handler: T,
772+
);
740773
}
741774

742775
/// Trait to have a unified interface for getting structs & properties from SessionConfig that are used in distributed context.
@@ -917,6 +950,10 @@ impl DistributedExt for SessionConfig {
917950
WorkerPlanRewriteHandlers::push_custom(self, Arc::new(h));
918951
}
919952

953+
fn set_distributed_dynamic_stage_built_handler<T: DynamicStageBuiltHandler>(&mut self, h: T) {
954+
DynamicStageBuiltHandlers::push_custom(self, Arc::new(h));
955+
}
956+
920957
delegate! {
921958
to self {
922959
#[call(set_distributed_option_extension)]
@@ -1024,6 +1061,10 @@ impl DistributedExt for SessionConfig {
10241061
#[call(set_distributed_worker_plan_rewrite_handler)]
10251062
#[expr($;self)]
10261063
fn with_distributed_worker_plan_rewrite_handler<H: WorkerPlanRewriteHandler>(mut self, h: H) -> Self;
1064+
1065+
#[call(set_distributed_dynamic_stage_built_handler)]
1066+
#[expr($;self)]
1067+
fn with_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(mut self, h: H) -> Self;
10271068
}
10281069
}
10291070
}
@@ -1172,6 +1213,11 @@ impl DistributedExt for SessionStateBuilder {
11721213
#[call(set_distributed_worker_plan_rewrite_handler)]
11731214
#[expr($;self)]
11741215
fn with_distributed_worker_plan_rewrite_handler<H: WorkerPlanRewriteHandler>(mut self, h: H) -> Self;
1216+
1217+
fn set_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(&mut self, h: H);
1218+
#[call(set_distributed_dynamic_stage_built_handler)]
1219+
#[expr($;self)]
1220+
fn with_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(mut self, h: H) -> Self;
11751221
}
11761222
}
11771223
}
@@ -1322,6 +1368,11 @@ impl DistributedExt for SessionState {
13221368
#[call(set_distributed_worker_plan_rewrite_handler)]
13231369
#[expr($;self)]
13241370
fn with_distributed_worker_plan_rewrite_handler<H: WorkerPlanRewriteHandler>(mut self, h: H) -> Self;
1371+
1372+
fn set_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(&mut self, h: H);
1373+
#[call(set_distributed_dynamic_stage_built_handler)]
1374+
#[expr($;self)]
1375+
fn with_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(mut self, h: H) -> Self;
13251376
}
13261377
}
13271378
}
@@ -1465,6 +1516,11 @@ impl DistributedExt for SessionContext {
14651516
#[call(set_distributed_worker_plan_rewrite_handler)]
14661517
#[expr($;self)]
14671518
fn with_distributed_worker_plan_rewrite_handler<H: WorkerPlanRewriteHandler>(self, h: H) -> Self;
1519+
1520+
fn set_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(&mut self, h: H);
1521+
#[call(set_distributed_dynamic_stage_built_handler)]
1522+
#[expr($;self)]
1523+
fn with_distributed_dynamic_stage_built_handler<H: DynamicStageBuiltHandler>(self, h: H) -> Self;
14681524
}
14691525
}
14701526
}

src/distributed_planner/inject_network_boundaries.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ pub(crate) async fn inject_network_boundaries(
159159
#[derive(Clone)]
160160
pub(crate) struct InjectNetworkBoundaryContext<'a> {
161161
pub(crate) d_cfg: &'a DistributedConfig,
162+
pub(crate) cfg: &'a SessionConfig,
162163

163-
cfg: &'a SessionConfig,
164164
worker_resolver: Arc<WorkerResolverExtension>,
165165
nb_builder: &'a (dyn NetworkBoundaryBuilder + Send + Sync),
166166
task_counts: &'a Mutex<HashMap<usize, TaskCountAnnotation>>,

src/distributed_planner/statistics/cost.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::Cost;
12
use crate::distributed_planner::statistics::complexity_cpu::complexity_cpu;
23
use crate::distributed_planner::statistics::complexity_memory::complexity_memory;
34
use crate::distributed_planner::statistics::complexity_network::complexity_network;
@@ -8,13 +9,6 @@ use datafusion::physical_plan::{ExecutionPlan, Statistics};
89
use std::ops::AddAssign;
910
use std::sync::Arc;
1011

11-
#[derive(Default, Debug)]
12-
pub(crate) struct Cost {
13-
pub(crate) cpu: Precision<usize>,
14-
pub(crate) memory: Precision<usize>,
15-
pub(crate) network: Precision<usize>,
16-
}
17-
1812
impl AddAssign for Cost {
1913
fn add_assign(&mut self, rhs: Self) {
2014
self.cpu = sum_precision(self.cpu, rhs.cpu);

src/distributed_planner/statistics/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,4 @@ mod cost;
66
mod default_bytes_for_datatype;
77
mod plan_statistics;
88

9-
#[allow(unused)] // will be used in a follow-up PR.
109
pub(crate) use cost::calculate_cost;

src/events/dynamic_stage_built.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::events::common::EventHandlerChain;
2+
use datafusion::common::{Result, stats::Precision};
3+
use datafusion::physical_plan::ExecutionPlan;
4+
use datafusion::prelude::SessionConfig;
5+
use std::sync::Arc;
6+
7+
/// Estimated cost associated to the fragment of the plan. Calculated based on the amount of data
8+
/// that will flow through each node and the static cpu, memory and network complexity of the
9+
/// different nodes based on the algorithms that will run inside them.
10+
#[derive(Default, Debug, Clone, Copy)]
11+
pub struct Cost {
12+
/// Estimated cost of CPU measured in bytes.
13+
pub cpu: Precision<usize>,
14+
/// Estimated amount of memory consumed measured in bytes.
15+
pub memory: Precision<usize>,
16+
/// Estimated amount of data that will need to be transferred over the wire in bytes.
17+
pub network: Precision<usize>,
18+
}
19+
20+
/// Event definition fired for each individual stage during dynamic planning.
21+
#[derive(Copy, Clone)]
22+
pub struct DynamicStageBuiltEvent<'a> {
23+
/// Cost associated to the plan in the field below and all its children recursively until
24+
/// network boundaries.
25+
pub cost: Cost,
26+
/// Plan that is about to be sent to a remote worker for runtime sampling.
27+
pub plan: &'a Arc<dyn ExecutionPlan>,
28+
/// SessionConfig in scope at the moment of building the stage.
29+
pub session_config: &'a SessionConfig,
30+
}
31+
32+
pub struct DynamicStageBuiltEventResponse {
33+
/// A potentially modified plan (e.g., optimizations applied).
34+
pub plan: Arc<dyn ExecutionPlan>,
35+
}
36+
37+
impl DynamicStageBuiltEventResponse {
38+
pub fn new(plan: Arc<dyn ExecutionPlan>) -> Self {
39+
Self { plan }
40+
}
41+
}
42+
43+
pub trait DynamicStageBuiltHandler: Send + Sync + 'static {
44+
fn handle(&self, ev: DynamicStageBuiltEvent) -> Option<Result<DynamicStageBuiltEventResponse>>;
45+
}
46+
47+
impl<F> DynamicStageBuiltHandler for F
48+
where
49+
F: Send + Sync + 'static,
50+
F: for<'a> Fn(DynamicStageBuiltEvent<'a>) -> Option<Result<DynamicStageBuiltEventResponse>>,
51+
{
52+
fn handle(&self, ev: DynamicStageBuiltEvent) -> Option<Result<DynamicStageBuiltEventResponse>> {
53+
self(ev)
54+
}
55+
}
56+
57+
pub(crate) type DynamicStageBuiltHandlers = EventHandlerChain<dyn DynamicStageBuiltHandler>;
58+
59+
impl DynamicStageBuiltHandlers {
60+
pub(crate) fn handle(
61+
ev: DynamicStageBuiltEvent,
62+
) -> Option<Result<DynamicStageBuiltEventResponse>> {
63+
ev.session_config
64+
.get_extension::<DynamicStageBuiltHandlers>()?
65+
.find_map(|handler| handler.handle(ev))
66+
}
67+
}

src/events/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod common;
22
mod defaults;
33
mod desired_task_count;
4+
mod dynamic_stage_built;
45
mod route_tasks;
56
mod scale_up_leaf_node;
67
mod worker_plan_rewrite;
@@ -14,6 +15,10 @@ pub use desired_task_count::{
1415
DesiredTaskCountEvent, DesiredTaskCountEventResponse, DesiredTaskCountHandler,
1516
TaskCountAnnotation,
1617
};
18+
pub(crate) use dynamic_stage_built::DynamicStageBuiltHandlers;
19+
pub use dynamic_stage_built::{
20+
Cost, DynamicStageBuiltEvent, DynamicStageBuiltEventResponse, DynamicStageBuiltHandler,
21+
};
1722
pub(crate) use route_tasks::RouteTasksHandlers;
1823
pub use route_tasks::{RouteTasksEvent, RouteTasksEventResponse, RouteTasksHandler};
1924
pub(crate) use scale_up_leaf_node::ScaleUpLeafNodeHandlers;

src/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ pub use distributed_planner::{
2323
DistributedConfig, NetworkBoundary, NetworkBoundaryExt, SessionStateBuilderExt,
2424
};
2525
pub use events::{
26-
DesiredTaskCountEvent, DesiredTaskCountEventResponse, DesiredTaskCountHandler, RouteTasksEvent,
27-
RouteTasksEventResponse, RouteTasksHandler, ScaleUpLeafNodeEvent, ScaleUpLeafNodeEventResponse,
28-
ScaleUpLeafNodeHandler, TaskCountAnnotation, WorkerPlanRewriteEvent,
29-
WorkerPlanRewriteEventResponse, WorkerPlanRewriteHandler,
26+
Cost, DesiredTaskCountEvent, DesiredTaskCountEventResponse, DesiredTaskCountHandler,
27+
DynamicStageBuiltEvent, DynamicStageBuiltEventResponse, DynamicStageBuiltHandler,
28+
RouteTasksEvent, RouteTasksEventResponse, RouteTasksHandler, ScaleUpLeafNodeEvent,
29+
ScaleUpLeafNodeEventResponse, ScaleUpLeafNodeHandler, TaskCountAnnotation,
30+
WorkerPlanRewriteEvent, WorkerPlanRewriteEventResponse, WorkerPlanRewriteHandler,
3031
};
3132
pub use execution_plans::{
3233
BroadcastExec, DistributedLeafExec, NetworkBroadcastExec, NetworkCoalesceExec,

0 commit comments

Comments
 (0)