Skip to content

Commit 24e95f9

Browse files
authored
fix AQE planning with S3 (#1962)
1 parent a96c381 commit 24e95f9

2 files changed

Lines changed: 161 additions & 4 deletions

File tree

ballista/scheduler/src/state/aqe/planner.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use datafusion::common;
3131
use datafusion::common::{HashMap, exec_err};
3232
use datafusion::error::DataFusionError;
3333
use datafusion::execution::context::SessionContext;
34+
use datafusion::execution::runtime_env::RuntimeEnv;
3435
use datafusion::execution::{SessionState, SessionStateBuilder};
3536
use datafusion::logical_expr::LogicalPlan;
3637
use datafusion::physical_optimizer::PhysicalOptimizerRule;
@@ -87,6 +88,7 @@ impl AdaptivePlanner {
8788
/// # Arguments:
8889
///
8990
/// * `session_config` - The session configuration for the job.
91+
/// * `runtime_env` - runtime environment
9092
/// * `plan` - The physical execution plan for the job.
9193
/// * `job_name` - The name of the job.
9294
/// * `physical_optimizer_rules` - A list of physical optimizer rules to apply.
@@ -95,12 +97,16 @@ impl AdaptivePlanner {
9597
/// A new instance of `AdaptivePlanner` or an error if the initialization fails.
9698
pub fn try_new_with_optimizers(
9799
session_config: &SessionConfig,
100+
runtime_env: Arc<RuntimeEnv>,
98101
plan: Arc<dyn ExecutionPlan>,
99102
job_name: String,
100103
physical_optimizer_rules: Vec<PhysicalOptimizerRuleRef>,
101104
) -> common::Result<Self> {
102-
let session_state =
103-
Self::create_session_state(session_config, physical_optimizer_rules);
105+
let session_state = Self::create_session_state(
106+
session_config,
107+
runtime_env,
108+
physical_optimizer_rules,
109+
);
104110
let planner = DefaultPhysicalPlanner::default();
105111
let plan = planner.optimize_physical_plan(plan, &session_state, |_, _| {})?;
106112

@@ -133,6 +139,7 @@ impl AdaptivePlanner {
133139
let plan_id_generator = Arc::new(AtomicUsize::new(0));
134140
Self::try_new_with_optimizers(
135141
session_config,
142+
RuntimeEnv::default().into(),
136143
plan,
137144
job_name,
138145
Self::default_optimizers(plan_id_generator),
@@ -159,12 +166,16 @@ impl AdaptivePlanner {
159166
// running standard set of optimizers, which will
160167
// after each stage.
161168
let plan_id_generator = Arc::new(AtomicUsize::new(0));
162-
let state = Self::create_session_state(
169+
let runtime_env = ctx.runtime_env();
170+
let plan_preparation_stage = Self::create_session_state(
163171
ctx.state().config(),
172+
ctx.runtime_env(),
164173
Self::plan_preparation_optimizers(plan_id_generator.clone()),
165174
);
166175

167-
let plan = state.create_physical_plan(logical_plan).await?;
176+
let plan = plan_preparation_stage
177+
.create_physical_plan(logical_plan)
178+
.await?;
168179

169180
// Note: the signature requires a JobId, but we are passing a JobName. The below is a
170181
// dirty fix but this seems like a bug or a design flaw.
@@ -175,6 +186,7 @@ impl AdaptivePlanner {
175186

176187
Self::try_new_with_optimizers(
177188
ctx.state().config(),
189+
runtime_env,
178190
plan,
179191
job_name,
180192
Self::default_optimizers(plan_id_generator),
@@ -546,11 +558,13 @@ impl AdaptivePlanner {
546558
/// A new `SessionState` instance.
547559
fn create_session_state(
548560
session_config: &SessionConfig,
561+
runtime_env: Arc<RuntimeEnv>,
549562
physical_optimizers: Vec<PhysicalOptimizerRuleRef>,
550563
) -> SessionState {
551564
SessionStateBuilder::new_with_default_features()
552565
.with_physical_optimizer_rules(physical_optimizers)
553566
.with_config(session_config.clone())
567+
.with_runtime_env(runtime_env)
554568
.build()
555569
}
556570
/// Recursively finds runnable exchanges in the execution plan.

examples/tests/object_store.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,149 @@ mod custom_s3_config {
371371
Ok(())
372372
}
373373

374+
#[tokio::test]
375+
async fn should_configure_aqe_s3_execute_sql_write_remote()
376+
-> datafusion::error::Result<()> {
377+
let test_data = examples_test_data();
378+
379+
//
380+
// Minio cluster setup
381+
//
382+
let container = crate::common::create_minio_container();
383+
let node = container.start().await.unwrap();
384+
385+
node.exec(crate::common::create_bucket_command())
386+
.await
387+
.unwrap();
388+
389+
let endpoint_host = node.get_host().await.unwrap();
390+
let endpoint_port = node.get_host_port_ipv4(9000).await.unwrap();
391+
392+
log::info!(
393+
"MINIO testcontainers host: {}, port: {}",
394+
endpoint_host,
395+
endpoint_port
396+
);
397+
398+
//
399+
// Session Context and Ballista cluster setup
400+
//
401+
402+
// Setting up configuration producer
403+
//
404+
// configuration producer registers user defined config extension
405+
// S3Option with relevant S3 configuration
406+
let config_producer =
407+
Arc::new(ballista_core::object_store::session_config_with_s3_support);
408+
// Setting up runtime producer
409+
//
410+
// Runtime producer creates object store registry
411+
// which can create object store connecter based on
412+
// S3Option configuration.
413+
let runtime_producer: RuntimeProducer =
414+
Arc::new(ballista_core::object_store::runtime_env_with_s3_support);
415+
416+
// Session builder creates SessionState
417+
//
418+
// which is configured using runtime and configuration producer,
419+
// producing same runtime environment, and providing same
420+
// object store registry.
421+
422+
let session_builder =
423+
Arc::new(ballista_core::object_store::session_state_with_s3_support);
424+
425+
let state = session_builder(config_producer())?;
426+
427+
// setting up ballista cluster with new runtime, configuration, and session state producers
428+
let (host, port) = crate::common::setup_test_cluster_with_builders(
429+
config_producer,
430+
runtime_producer,
431+
session_builder,
432+
)
433+
.await;
434+
let url = format!("df://{host}:{port}");
435+
436+
// establishing cluster connection,
437+
let ctx: SessionContext = SessionContext::remote_with_state(&url, state).await?;
438+
439+
ctx.sql("SET ballista.planner.adaptive.enabled = true")
440+
.await?
441+
.show()
442+
.await?;
443+
// setting up relevant S3 options
444+
ctx.sql("SET s3.allow_http = true").await?.show().await?;
445+
ctx.sql(&format!("SET s3.access_key_id = '{}'", ACCESS_KEY_ID))
446+
.await?
447+
.show()
448+
.await?;
449+
ctx.sql(&format!("SET s3.secret_access_key = '{}'", SECRET_KEY))
450+
.await?
451+
.show()
452+
.await?;
453+
ctx.sql(&format!(
454+
"SET s3.endpoint = 'http://{}:{}'",
455+
endpoint_host, endpoint_port
456+
))
457+
.await?
458+
.show()
459+
.await?;
460+
ctx.sql("SET s3.allow_http = true").await?.show().await?;
461+
462+
// verifying that we have set S3Options
463+
ctx.sql("select name, value from information_schema.df_settings where name like 's3.%'").await?.show().await?;
464+
465+
ctx.register_parquet(
466+
"test",
467+
&format!("{test_data}/alltypes_plain.parquet"),
468+
Default::default(),
469+
)
470+
.await?;
471+
472+
let write_dir_path =
473+
&format!("s3://{}/write_test.parquet", crate::common::BUCKET);
474+
475+
ctx.sql("select * from test")
476+
.await?
477+
.write_parquet(write_dir_path, Default::default(), Default::default())
478+
.await?;
479+
480+
ctx.register_parquet("written_table", write_dir_path, Default::default())
481+
.await?;
482+
483+
let result = ctx
484+
.sql("select id, string_col, timestamp_col from written_table where id > 4")
485+
.await?
486+
.collect()
487+
.await?;
488+
let expected = [
489+
"+----+------------+---------------------+",
490+
"| id | string_col | timestamp_col |",
491+
"+----+------------+---------------------+",
492+
"| 5 | 31 | 2009-03-01T00:01:00 |",
493+
"| 6 | 30 | 2009-04-01T00:00:00 |",
494+
"| 7 | 31 | 2009-04-01T00:01:00 |",
495+
"+----+------------+---------------------+",
496+
];
497+
498+
assert_batches_eq!(expected, &result);
499+
500+
let result = ctx
501+
.sql("select max(id) as max, min(id) as min from written_table")
502+
.await?
503+
.collect()
504+
.await?;
505+
let expected = [
506+
"+-----+-----+",
507+
"| max | min |",
508+
"+-----+-----+",
509+
"| 7 | 0 |",
510+
"+-----+-----+",
511+
];
512+
513+
assert_batches_eq!(expected, &result);
514+
Ok(())
515+
}
516+
374517
// this test shows how to register external ObjectStoreRegistry and configure it
375518
// using infrastructure provided by ballista standalone.
376519
//

0 commit comments

Comments
 (0)