-
Notifications
You must be signed in to change notification settings - Fork 55
refactor: SessionConfig.extensions instead ConfigOptions.extensions #550
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fa3fb46
068b0da
ff757be
302551b
aa5231b
86635ce
93990e6
b4df221
6eab687
d495385
efb8121
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,7 @@ | ||
| use crate::distributed_planner::task_estimator::CombinedTaskEstimator; | ||
| use crate::protocol::ChannelResolverExtension; | ||
| use crate::work_unit_feed::WorkUnitFeedRegistry; | ||
| use crate::worker_resolver::WorkerResolverExtension; | ||
| use crate::{TaskEstimator, WorkerResolver}; | ||
| use datafusion::common::{DataFusionError, extensions_options, not_impl_err, plan_err}; | ||
| use datafusion::config::{ConfigExtension, ConfigField, ConfigOptions, Visit}; | ||
| use datafusion::common::{DataFusionError, extensions_options, plan_err}; | ||
| use datafusion::config::{ConfigExtension, ConfigOptions}; | ||
| use datafusion::execution::TaskContext; | ||
| use datafusion::prelude::SessionConfig; | ||
| use std::fmt::{Debug, Formatter}; | ||
| use std::sync::Arc; | ||
|
|
||
| extensions_options! { | ||
|
|
@@ -76,18 +70,6 @@ extensions_options! { | |
| /// If `dynamic_task_count` is enabled, this value is the amount of bytes/second each | ||
| /// partition is expected to handle. Lower values will result in greater parallelism. | ||
| pub bytes_per_partition_per_second: usize, default = 16 * 1024 * 1024 | ||
| /// Collection of [TaskEstimator]s that will be applied to leaf nodes in order to | ||
| /// estimate how many tasks should be spawned for the [Stage] containing the leaf node. | ||
| pub(crate) __private_task_estimator: CombinedTaskEstimator, default = CombinedTaskEstimator::default() | ||
| /// [ChannelResolver] implementation that tells the distributed planner information about | ||
| /// the available workers ready to execute distributed tasks. | ||
| pub(crate) __private_channel_resolver: ChannelResolverExtension, default = ChannelResolverExtension::default() | ||
| /// [WorkerResolver] implementation that tells the distributed planner information about | ||
| /// the available workers ready to execute distributed tasks. | ||
| pub(crate) __private_worker_resolver: WorkerResolverExtension, default = WorkerResolverExtension::not_implemented() | ||
| /// [WorkUnitFeedRegistry] that contains a set of getters that, applied to each node in a | ||
| /// plan, will return the [crate::WorkUnitFeed]s present in all nodes. | ||
| pub(crate) __private_work_unit_feed_registry: WorkUnitFeedRegistry, default = WorkUnitFeedRegistry::default() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -100,19 +82,6 @@ fn cardinality_task_count_factor_default() -> f64 { | |
| } | ||
|
|
||
| impl DistributedConfig { | ||
| /// Appends a [TaskEstimator] to the list. [TaskEstimator] will be executed sequentially in | ||
| /// order on leaf nodes, and the first one to provide a value is the one that gets to decide | ||
| /// how many tasks are used for that [Stage]. | ||
| pub fn with_task_estimator( | ||
| mut self, | ||
| task_estimator: impl TaskEstimator + Send + Sync + 'static, | ||
| ) -> Self { | ||
| self.__private_task_estimator | ||
| .user_provided | ||
| .push(Arc::new(task_estimator)); | ||
| self | ||
| } | ||
|
|
||
| /// Gets the [DistributedConfig] from the [ConfigOptions]'s extensions. | ||
| pub fn from_config_options(cfg: &ConfigOptions) -> Result<&Self, DataFusionError> { | ||
| let Some(distributed_cfg) = cfg.extensions.get::<DistributedConfig>() else { | ||
|
|
@@ -121,7 +90,6 @@ impl DistributedConfig { | |
| Ok(distributed_cfg) | ||
| } | ||
|
|
||
| /// Gets the [DistributedConfig] from the [ConfigOptions]'s extensions. | ||
| pub fn from_config_options_mut(cfg: &mut ConfigOptions) -> Result<&mut Self, DataFusionError> { | ||
| let Some(distributed_cfg) = cfg.extensions.get_mut::<DistributedConfig>() else { | ||
|
Comment on lines
92
to
94
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for removing the docs in this function? |
||
| return plan_err!("DistributedConfig is not in ConfigOptions.extensions"); | ||
|
|
@@ -138,85 +106,8 @@ impl DistributedConfig { | |
| pub fn from_task_context(ctx: &Arc<TaskContext>) -> Result<&Self, DataFusionError> { | ||
| Self::from_session_config(ctx.session_config()) | ||
| } | ||
|
|
||
| /// Returns the [WorkerResolver] currently in scope for this [DistributedConfig]. | ||
| pub fn worker_resolver(&self) -> &Arc<dyn WorkerResolver> { | ||
| &self.__private_worker_resolver.0 | ||
| } | ||
| } | ||
|
|
||
| impl ConfigExtension for DistributedConfig { | ||
| const PREFIX: &'static str = "distributed"; | ||
| } | ||
|
|
||
| // FIXME: Ideally, both ChannelResolverExtension and TaskEstimators would be passed as | ||
| // extensions in SessionConfig's AnyMap instead of the ConfigOptions. However, we need | ||
| // to pass this as ConfigOptions as we need these two fields to be present during | ||
| // planning in the DistributedQueryPlanner, and the signature of the create_physical_plan() | ||
| // method there accepts a SessionState which only provides ConfigOptions. | ||
| // The following PR addresses this: https://github.com/apache/datafusion/pull/18168 | ||
| // but it still has not been accepted or merged. | ||
| // Because of this, all the boilerplate trait implementations below are needed. | ||
| impl ConfigField for ChannelResolverExtension { | ||
| fn visit<V: Visit>(&self, _: &mut V, _: &str, _: &'static str) { | ||
| // nothing to do. | ||
|
Rich-T-kid marked this conversation as resolved.
|
||
| } | ||
|
|
||
| fn set(&mut self, _: &str, _: &str) -> datafusion::common::Result<()> { | ||
| not_impl_err!("Not implemented") | ||
| } | ||
| } | ||
|
|
||
| impl Debug for ChannelResolverExtension { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "ChannelResolverExtension") | ||
| } | ||
| } | ||
|
|
||
| impl ConfigField for WorkerResolverExtension { | ||
| fn visit<V: Visit>(&self, _: &mut V, _: &str, _: &'static str) { | ||
| // nothing to do. | ||
| } | ||
|
|
||
| fn set(&mut self, _: &str, _: &str) -> datafusion::common::Result<()> { | ||
| not_impl_err!("Not implemented") | ||
| } | ||
| } | ||
|
|
||
| impl Debug for WorkerResolverExtension { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "WorkerResolverExtension") | ||
| } | ||
| } | ||
|
|
||
| impl ConfigField for CombinedTaskEstimator { | ||
| fn visit<V: Visit>(&self, _: &mut V, _: &str, _: &'static str) { | ||
| //nothing to do. | ||
| } | ||
|
|
||
| fn set(&mut self, _: &str, _: &str) -> Result<(), DataFusionError> { | ||
| not_impl_err!("not implemented") | ||
| } | ||
| } | ||
|
|
||
| impl Debug for CombinedTaskEstimator { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "TaskEstimators") | ||
| } | ||
| } | ||
|
|
||
| impl ConfigField for WorkUnitFeedRegistry { | ||
| fn visit<V: Visit>(&self, _: &mut V, _: &str, _: &'static str) { | ||
| //nothing to do. | ||
| } | ||
|
|
||
| fn set(&mut self, _: &str, _: &str) -> Result<(), DataFusionError> { | ||
| not_impl_err!("not implemented") | ||
| } | ||
| } | ||
|
|
||
| impl Debug for WorkUnitFeedRegistry { | ||
| fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
| write!(f, "WorkUnitFeedRegistry") | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| use crate::common::TreeNodeExt; | ||
| use crate::distributed_planner::CombinedTaskEstimator; | ||
| use crate::distributed_planner::inject_network_boundaries::{ | ||
| CardinalityBasedNetworkBoundaryBuilder, inject_network_boundaries, | ||
| }; | ||
|
|
@@ -70,18 +71,19 @@ impl QueryPlanner for DistributedQueryPlanner { | |
| return Ok(original_plan); | ||
| } | ||
|
|
||
| let cfg = session_state.config_options().as_ref(); | ||
| let session_cfg = session_state.config(); | ||
| let cfg = session_cfg.options(); | ||
| let d_cfg = DistributedConfig::from_config_options(cfg)?; | ||
|
|
||
| // The plan already contains network boundaries set by the user. Just ensure they have nice | ||
| // unique identifiers for each stage, and move forward with it. | ||
| if original_plan.exists(|plan| Ok(plan.is_network_boundary()))? { | ||
| // Ensure the leafs are appropriately scaled up. | ||
| let task_estimator = CombinedTaskEstimator::from_session_config(session_cfg); | ||
| let scaled = original_plan.transform_down_with_task_count(1, |plan, task_count| { | ||
| if !plan.children().is_empty() { | ||
| return Ok(Transformed::no(plan)); | ||
| } | ||
| let task_estimator = &d_cfg.__private_task_estimator; | ||
| match task_estimator.scale_up_leaf_node(&plan, task_count, cfg)? { | ||
| None => Ok(Transformed::no(plan)), | ||
| Some(scaled) => Ok(Transformed::yes(scaled)), | ||
|
|
@@ -104,9 +106,9 @@ impl QueryPlanner for DistributedQueryPlanner { | |
| plan = Arc::new(CoalescePartitionsExec::new(plan)); | ||
| } | ||
|
|
||
| let cfg = session_state.config_options(); | ||
| let session_config_opt = session_state.config_options(); | ||
|
|
||
| plan = insert_broadcast_execs(plan, cfg)?; | ||
| plan = insert_broadcast_execs(plan, session_config_opt)?; | ||
|
|
||
| if d_cfg.dynamic_task_count { | ||
| // The task count will be decided dynamically at execution time. | ||
|
|
@@ -116,14 +118,15 @@ impl QueryPlanner for DistributedQueryPlanner { | |
| } | ||
|
|
||
| // Compute per-node task counts and inject `Network*Exec` nodes at the stage boundaries. | ||
| plan = inject_network_boundaries(plan, CardinalityBasedNetworkBoundaryBuilder, cfg).await?; | ||
| plan = inject_network_boundaries(plan, CardinalityBasedNetworkBoundaryBuilder, session_cfg) | ||
| .await?; | ||
|
|
||
| plan = prepare_network_boundaries(plan)?; | ||
| if !plan.exists(|plan| Ok(plan.is_network_boundary()))? { | ||
| return Ok(original_plan); | ||
| } | ||
|
|
||
| let plan = partial_reduce_below_network_shuffles(plan, cfg)?; | ||
| let plan = partial_reduce_below_network_shuffles(plan, session_config_opt)?; | ||
| let plan = push_fetch_into_network_coalesce(plan)?; | ||
|
|
||
|
Comment on lines
109
to
131
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we leave this unrelated variable name changes out of the PR? Not that there's a problem with the new names, but if for some reason we need to revert this PR in the future, not changing unrelated things will yield less chances of conflicts with other PRs. |
||
| Ok(Arc::new( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Damn... this is actually how people are expected to access their worker resolver, and the API is taking a hit on usability now...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What comes to mind is, in the same way that we have the
DistributedExt::with_distributed_worker_resolverandDistributedExt::set_distributed_worker_resolver, we can have theDistributedExt::get_distributed_worker_resolverfor retrieving the worker resolver.How does that sound?