-
Notifications
You must be signed in to change notification settings - Fork 21
Improve docs and add custom execution plan example #277
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
Merged
gabotechs
merged 7 commits into
main
from
gabrielmusat/add-custom-execution-plan-example
Jan 6, 2026
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
0d8a5e1
Support any dataset in benchmarks
gabotechs 5b539e4
Add TPC-DS schemas
gabotechs f561a4d
Improve docs
gabotechs 5fa178d
Let Claude improve wording
gabotechs 5a0106f
Add custom_execution_plan.rs example
gabotechs f2b4fed
Link to custom_execution_plan.rs example
gabotechs 9eabe6e
Respond to PR feedback
gabotechs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,88 +1,86 @@ | ||
| # Getting Started | ||
|
|
||
| Rather than being opinionated about your setup and how you serve queries to users, | ||
| Distributed DataFusion allows you to plug in your own networking stack and spawn your own gRPC servers that act as | ||
| workers in the cluster. | ||
| Think of this library as vanilla DataFusion, except that certain nodes execute their children on remote | ||
| machines and retrieve data via the Arrow Flight protocol. | ||
|
|
||
| This library aims to provide an experience as close as possible to vanilla DataFusion. | ||
|
|
||
| ## How to use Distributed DataFusion | ||
|
|
||
| Rather than imposing constraints on your infrastructure or query serving patterns, Distributed DataFusion | ||
| allows you to plug in your own networking stack and spawn your own gRPC servers that act as workers in the cluster. | ||
|
|
||
| This project heavily relies on the [Tonic](https://github.com/hyperium/tonic) ecosystem for the networking layer. | ||
| Users of this library are responsible for building their own Tonic server, adding the Arrow Flight distributed | ||
| DataFusion service to it and spawning it on a port so that it can be reached by other workers in the cluster. | ||
| DataFusion service to it and spawning it on a port so that it can be reached by other workers in the cluster. A very | ||
| basic example of this would be: | ||
|
|
||
| For a basic setup, all you need to do is to enrich your DataFusion `SessionStateBuilder` with the tools this project | ||
| ships: | ||
| ```rust | ||
| #[tokio::main] | ||
| async fn main() -> Result<(), Box<dyn Error>> { | ||
| let worker = Worker::default(); | ||
|
|
||
| ```rs | ||
| let state = SessionStateBuilder::new() | ||
| + .with_distributed_worker_resolver(my_custom_worker_resolver) | ||
| + .with_physical_optimizer_rule(Arc::new(DistributedPhysicalOptimizerRule)) | ||
| .build(); | ||
| ``` | ||
| Server::builder() | ||
| .add_service(worker.into_flight_server()) | ||
| .serve(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8000)) | ||
| .await?; | ||
|
|
||
| And the `my_custom_worker_resolver` variable should be an implementation of | ||
| the [WorkerResolverResolver](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/src/networking/worker_resolver.rs) | ||
| trait, which tells Distributed DataFusion how to connect to other workers in the cluster. | ||
| Ok(()) | ||
| } | ||
| ``` | ||
|
|
||
| A very basic example of such an implementation that resolves workers in the localhost machine is: | ||
| Distributed DataFusion requires knowledge of worker locations. Implement the `WorkerResolver` trait to provide | ||
| this information. Here is a simple example of what this would look like with localhost workers: | ||
|
|
||
| ```rust | ||
| #[derive(Clone)] | ||
| struct LocalhostWorkerResolver { | ||
| ports: Vec<u16>, | ||
| } | ||
|
|
||
| #[async_trait] | ||
| impl ChannelResolver for LocalhostChannelResolver { | ||
| impl WorkerResolver for LocalhostWorkerResolver { | ||
| fn get_urls(&self) -> Result<Vec<Url>, DataFusionError> { | ||
| Ok(self.ports.iter().map(|port| Url::parse(&format!("http://localhost:{port}")).unwrap()).collect()) | ||
| Ok(self | ||
| .ports | ||
| .iter() | ||
| .map(|port| Url::parse(&format!("http://localhost:{port}")).unwrap()) | ||
| .collect()) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| > NOTE: This example is not production-ready and is meant to showcase the basic concepts of the library. | ||
|
|
||
| This `WorkerResolver` implementation should resolve URLs of Distributed DataFusion workers, and it's | ||
| also the user of this library's responsibility to spawn a Tonic server that exposes the worker as an Arrow Flight | ||
| service using Tonic. | ||
| Register both the `WorkerResolver` implementation and the `DistributedPhysicalOptimizerRule` in DataFusion's | ||
| `SessionStateBuilder` to enable distributed query planning: | ||
|
|
||
| A basic example of such a server is: | ||
|
|
||
| ```rust | ||
| #[tokio::main] | ||
| async fn main() -> Result<(), Box<dyn Error>> { | ||
| let endpoint = Worker::default(); | ||
|
|
||
| Server::builder() | ||
| .add_service(endpoint.into_flight_server()) | ||
| .serve(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8000)) | ||
| .await?; | ||
|
|
||
| Ok(()) | ||
| ```rs | ||
| let localhost_worker_resolver = LocalhostWorkerResolver { | ||
| ports: vec![8000, 8001, 8002] | ||
| } | ||
| ``` | ||
|
|
||
| ## Next steps | ||
| let state = SessionStateBuilder::new() | ||
| .with_distributed_worker_resolver(localhost_worker_resolver) | ||
| .with_physical_optimizer_rule(Arc::new(DistributedPhysicalOptimizerRule)) | ||
| .build(); | ||
|
|
||
| The next two sections of this guide will walk you through tailoring the library's traits to your own needs: | ||
| let ctx = SessionContext::from(state); | ||
| ``` | ||
|
|
||
| - [Build your own WorkerResolver](worker-resolver.md) | ||
| - [Build your own ChannelResolver](channel-resolver.md) | ||
| - [Build your own TaskEstimator](task-estimator.md) | ||
| - [Build your own distributed DataFusion Worker](worker.md) | ||
| This will leave a DataFusion `SessionContext` ready for executing distributed queries. | ||
|
|
||
| Here are some other resources in the codebase: | ||
| > NOTE: This example is not production-ready and is meant to showcase the basic concepts of the library. | ||
|
|
||
| - [In-memory cluster example](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/examples/in_memory.md) | ||
| - [Localhost cluster example](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/examples/localhost.md) | ||
| ## Next steps | ||
|
|
||
| A more advanced example can be found in the benchmarks that use a cluster of distributed DataFusion workers | ||
| deployed on AWS EC2 machines: | ||
| Depending on your needs, your setup can get more complicated, for example: | ||
|
|
||
| - [AWS EC2 based cluster example](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/benchmarks/cdk/bin/worker.rs) | ||
| - You may want to resolve worker URLs dynamically using the Kubernetes API. | ||
| - You may want to wrap the Arrow Flight clients that connect workers with an observability layer. | ||
| - You may want to be able to execute your own custom ExecutionPlans in a distributed manner. | ||
| - etc... | ||
|
|
||
| Each feature in the project is showcased and tested in its own isolated integration test, so it's recommended to | ||
| review those for a better understanding of how specific features work: | ||
| To learn how to do all that, it's recommended to: | ||
|
|
||
| - [Pass your own ConfigExtension implementations across network boundaries](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/tests/custom_config_extension.rs) | ||
| - [Provide custom protobuf codecs for your own nodes](https://github.com/datafusion-contrib/datafusion-distributed/blob/main/tests/custom_extension_codec.rs) | ||
| - Provide a custom TaskEstimator for controlling the amount of parallelism (coming soon) | ||
| - [Continue reading this guide](worker.md) | ||
| - [Look at examples in the project](https://github.com/datafusion-contrib/datafusion-distributed/tree/main/examples) | ||
| - [Look at the integration tests for finer grained examples](https://github.com/datafusion-contrib/datafusion-distributed/tree/main/tests) | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Is
NetworkCoalesceExecat the top not considered a stage? Do stage indices start at 0 or 1? It could possibly be helpful to make that explicit by writing e.g.Stage iwhereiis the stage index rather than just stage.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.
I don't think so. That part of the plan gets executed locally in a non-distributed context, so we usually talk about it as the "head" of the plan. It might be nice to choose a specific term for that though.
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.
For stage enumeration, I'd not over index to much about it in this drawing, as I wanted to land it as clean as possible. There's not a strong enumeration order requirement, fwiw stage numbers could be just random numbers and it would work the same, so I think it's fine to not represent it in this drawing