Skip to content

Commit 2aa55a4

Browse files
committed
handle spill case
Signed-off-by: Ruihang Xia <waynestxia@gmail.com>
1 parent 4b7264c commit 2aa55a4

1 file changed

Lines changed: 82 additions & 3 deletions

File tree

‎datafusion/physical-plan/src/aggregates/row_hash.rs‎

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,13 +1297,15 @@ impl GroupedHashAggregateStream {
12971297

12981298
// Recreate `group_values` for streaming merge so group ids are assigned
12991299
// in first-seen order, as required by `GroupOrderingFull`.
1300-
// The pre-spill multi-column collector may use `vectorized_intern`, which
1301-
// can assign new group ids out of input order under hash collisions.
1300+
// The pre-spill collector may not track group ids for DISTINCT-only
1301+
// aggregation. The pre-spill multi-column collector may use
1302+
// `vectorized_intern`, which can assign new group ids out of input
1303+
// order under hash collisions.
13021304
let group_schema = self
13031305
.spill_state
13041306
.merging_group_by
13051307
.group_schema(&self.spill_state.spill_schema)?;
1306-
if group_schema.fields().len() > 1 {
1308+
if self.accumulators.is_empty() || group_schema.fields().len() > 1 {
13071309
self.group_values = new_group_values(group_schema, &self.group_ordering)?;
13081310
}
13091311

@@ -1393,9 +1395,12 @@ mod tests {
13931395
use super::*;
13941396
use crate::InputOrderMode;
13951397
use crate::execution_plan::ExecutionPlan;
1398+
use crate::metrics::MetricValue;
13961399
use crate::test::TestMemoryExec;
13971400
use arrow::array::{Int32Array, Int64Array};
13981401
use arrow::datatypes::{DataType, Field, Schema};
1402+
use datafusion_execution::config::SessionConfig;
1403+
use datafusion_execution::memory_pool::FairSpillPool;
13991404
use datafusion_execution::runtime_env::RuntimeEnvBuilder;
14001405
use datafusion_functions_aggregate::count::count_udaf;
14011406
use datafusion_physical_expr::aggregate::AggregateExprBuilder;
@@ -1507,6 +1512,80 @@ mod tests {
15071512
Ok(())
15081513
}
15091514

1515+
#[tokio::test]
1516+
async fn test_spill_distinct_single_primitive_group_by() -> Result<()> {
1517+
let schema = Arc::new(Schema::new(vec![Field::new(
1518+
"group_col",
1519+
DataType::Int32,
1520+
false,
1521+
)]));
1522+
1523+
let num_distinct = 512;
1524+
let num_spills = 24;
1525+
let input_partitions = vec![
1526+
(0..num_spills)
1527+
.map(|_| {
1528+
Ok(RecordBatch::try_new(
1529+
Arc::clone(&schema),
1530+
vec![Arc::new(Int32Array::from_iter_values(0..num_distinct))],
1531+
)?)
1532+
})
1533+
.collect::<Result<Vec<_>>>()?,
1534+
];
1535+
1536+
let session_config = SessionConfig::new().with_batch_size(4);
1537+
let runtime = RuntimeEnvBuilder::new()
1538+
.with_memory_pool(Arc::new(FairSpillPool::new(6000)))
1539+
.build_arc()?;
1540+
let task_ctx = Arc::new(
1541+
TaskContext::default()
1542+
.with_session_config(session_config)
1543+
.with_runtime(runtime),
1544+
);
1545+
1546+
let group_expr = vec![(col("group_col", &schema)?, "group_col".to_string())];
1547+
let exec = TestMemoryExec::try_new(&input_partitions, Arc::clone(&schema), None)?;
1548+
let exec = Arc::new(TestMemoryExec::update_cache(&Arc::new(exec)));
1549+
1550+
let aggregate_exec = AggregateExec::try_new(
1551+
AggregateMode::Single,
1552+
PhysicalGroupBy::new_single(group_expr),
1553+
Vec::<Arc<AggregateFunctionExpr>>::new(),
1554+
vec![],
1555+
exec,
1556+
Arc::clone(&schema),
1557+
)?;
1558+
1559+
let mut stream =
1560+
GroupedHashAggregateStream::new(&aggregate_exec, &Arc::clone(&task_ctx), 0)?;
1561+
let mut values = Vec::new();
1562+
1563+
while let Some(result) = stream.next().await {
1564+
let batch = result?;
1565+
let group_col = batch
1566+
.column(0)
1567+
.as_primitive::<arrow::datatypes::Int32Type>();
1568+
values.extend(group_col.values().iter().copied());
1569+
}
1570+
1571+
let spill_count = aggregate_exec
1572+
.metrics()
1573+
.unwrap()
1574+
.iter()
1575+
.find_map(|metric| match metric.value() {
1576+
MetricValue::SpillCount(count) => Some(count.value()),
1577+
_ => None,
1578+
})
1579+
.unwrap_or_default();
1580+
assert!(spill_count > 0, "expected test to exercise spilling");
1581+
1582+
values.sort_unstable();
1583+
let expected = (0..num_distinct).collect::<Vec<_>>();
1584+
assert_eq!(values, expected);
1585+
1586+
Ok(())
1587+
}
1588+
15101589
#[tokio::test]
15111590
async fn test_skip_aggregation_probe_not_locked_until_skip() -> Result<()> {
15121591
// Test that the probe is not locked until we actually decide to skip.

0 commit comments

Comments
 (0)