blob: 6c989b1a09eedabe2a1588f62f7f3b68ba614bf0 [file]
// Copyright 2022 The Blaze Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Defines the External shuffle repartition plan
use std::{any::Any, fmt::Debug, sync::Arc};
use arrow::datatypes::SchemaRef;
use async_trait::async_trait;
use datafusion::{
error::Result,
execution::context::TaskContext,
physical_plan::{
expressions::PhysicalSortExpr,
metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricBuilder, MetricsSet},
stream::RecordBatchStreamAdapter,
DisplayAs, DisplayFormatType, ExecutionPlan, Partitioning, SendableRecordBatchStream,
Statistics,
},
};
use datafusion_ext_commons::df_execution_err;
use futures::{stream::once, TryStreamExt};
use crate::{
common::batch_statisitcs::{stat_input, InputBatchStatistics},
memmgr::MemManager,
shuffle::{
single_repartitioner::SingleShuffleRepartitioner,
sort_repartitioner::SortShuffleRepartitioner, ShuffleRepartitioner,
},
};
/// The shuffle writer operator maps each input partition to M output partitions
/// based on a partitioning scheme. No guarantees are made about the order of
/// the resulting partitions.
#[derive(Debug)]
pub struct ShuffleWriterExec {
/// Input execution plan
input: Arc<dyn ExecutionPlan>,
/// Partitioning scheme to use
partitioning: Partitioning,
/// Output data file path
output_data_file: String,
/// Output index file path
output_index_file: String,
/// Metrics
metrics: ExecutionPlanMetricsSet,
}
impl DisplayAs for ShuffleWriterExec {
fn fmt_as(&self, _t: DisplayFormatType, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "ShuffleWriterExec: partitioning={:?}", self.partitioning)
}
}
#[async_trait]
impl ExecutionPlan for ShuffleWriterExec {
/// Return a reference to Any that can be used for downcasting
fn as_any(&self) -> &dyn Any {
self
}
/// Get the schema for this execution plan
fn schema(&self) -> SchemaRef {
self.input.schema()
}
fn output_partitioning(&self) -> Partitioning {
self.partitioning.clone()
}
fn output_ordering(&self) -> Option<&[PhysicalSortExpr]> {
None
}
fn children(&self) -> Vec<Arc<dyn ExecutionPlan>> {
vec![self.input.clone()]
}
fn with_new_children(
self: Arc<Self>,
children: Vec<Arc<dyn ExecutionPlan>>,
) -> Result<Arc<dyn ExecutionPlan>> {
match children.len() {
1 => Ok(Arc::new(ShuffleWriterExec::try_new(
children[0].clone(),
self.partitioning.clone(),
self.output_data_file.clone(),
self.output_index_file.clone(),
)?)),
_ => df_execution_err!("ShuffleWriterExec wrong number of children"),
}
}
fn execute(
&self,
partition: usize,
context: Arc<TaskContext>,
) -> Result<SendableRecordBatchStream> {
// record uncompressed data size
let data_size_metric = MetricBuilder::new(&self.metrics).counter("data_size", partition);
let repartitioner: Arc<dyn ShuffleRepartitioner> = match &self.partitioning {
p if p.partition_count() == 1 => Arc::new(SingleShuffleRepartitioner::new(
self.output_data_file.clone(),
self.output_index_file.clone(),
BaselineMetrics::new(&self.metrics, partition),
)),
Partitioning::Hash(..) => {
let partitioner = Arc::new(SortShuffleRepartitioner::new(
partition,
self.output_data_file.clone(),
self.output_index_file.clone(),
self.partitioning.clone(),
&self.metrics,
));
MemManager::register_consumer(partitioner.clone(), true);
partitioner
}
p => unreachable!("unsupported partitioning: {:?}", p),
};
let input = stat_input(
InputBatchStatistics::from_metrics_set_and_blaze_conf(&self.metrics, partition)?,
self.input.execute(partition, context.clone())?,
)?;
Ok(Box::pin(RecordBatchStreamAdapter::new(
self.schema(),
once(repartitioner.execute(
context.clone(),
partition,
input,
BaselineMetrics::new(&self.metrics, partition),
data_size_metric,
))
.try_flatten(),
)))
}
fn metrics(&self) -> Option<MetricsSet> {
Some(self.metrics.clone_inner())
}
fn statistics(&self) -> Result<Statistics> {
self.input.statistics()
}
}
impl ShuffleWriterExec {
/// Create a new ShuffleWriterExec
pub fn try_new(
input: Arc<dyn ExecutionPlan>,
partitioning: Partitioning,
output_data_file: String,
output_index_file: String,
) -> Result<Self> {
Ok(ShuffleWriterExec {
input,
partitioning,
metrics: ExecutionPlanMetricsSet::new(),
output_data_file,
output_index_file,
})
}
}