Problem
The AQE coalesce-shuffle-partitions rule (CoalescePartitionsRule) assumes every leaf ExchangeExec in a stage's alignment group shares the same upstream partition count M. It reads M from leaf 0 and, if any other leaf disagrees, bails the whole group:
https://github.com/apache/datafusion-ballista/blob/main/ballista/scheduler/src/state/aqe/optimizer_rule/coalesce_partitions.rs#L231-L246
let m = as_exchange(&leaves[0])
.properties()
.partitioning
.partition_count();
// TODO: per-M subgrouping; for now bail on heterogeneous M (Q22 panic guard).
if leaves
.iter()
.any(|arc| as_exchange(arc).properties().partitioning.partition_count() != m)
{
return Ok(plan);
}
The bail was added as a guard after a panic on TPC-H Q22 (the element-wise byte-sum over summed[i] indexes into a vec![0u64; m] and would go out of bounds when a leaf has more than M partitions). It is tracked only as an inline TODO; there is no issue for it and it is not on the "enable AQE by default" checklist (#2092).
Impact
Any stage whose leaf exchanges do not all share the same partition count is skipped entirely by the coalesce rule, so it keeps one task per upstream partition. The guard is correct (it prevents the panic) but it is a coverage gap: heterogeneous-M stages never benefit from coalescing.
Proposed direction
Implement per-M subgrouping: partition the leaves into alignment subgroups by their upstream partition count, and run the sum → bin-pack → set_coalesce path independently per subgroup, so each subgroup coalesces against its own M. Leaves that must stay co-partitioned with each other (same hash key, same M) land in the same subgroup, preserving the join partition-count invariant within the subgroup.
Acceptance criteria
Parent epic: #2092 (Enable AQE by default) / #1359 (AQE design)
Problem
The AQE coalesce-shuffle-partitions rule (
CoalescePartitionsRule) assumes every leafExchangeExecin a stage's alignment group shares the same upstream partition countM. It readsMfrom leaf 0 and, if any other leaf disagrees, bails the whole group:https://github.com/apache/datafusion-ballista/blob/main/ballista/scheduler/src/state/aqe/optimizer_rule/coalesce_partitions.rs#L231-L246
The bail was added as a guard after a panic on TPC-H Q22 (the element-wise byte-sum over
summed[i]indexes into avec![0u64; m]and would go out of bounds when a leaf has more thanMpartitions). It is tracked only as an inlineTODO; there is no issue for it and it is not on the "enable AQE by default" checklist (#2092).Impact
Any stage whose leaf exchanges do not all share the same partition count is skipped entirely by the coalesce rule, so it keeps one task per upstream partition. The guard is correct (it prevents the panic) but it is a coverage gap: heterogeneous-
Mstages never benefit from coalescing.Proposed direction
Implement per-
Msubgrouping: partition the leaves into alignment subgroups by their upstream partition count, and run the sum → bin-pack →set_coalescepath independently per subgroup, so each subgroup coalesces against its ownM. Leaves that must stay co-partitioned with each other (same hash key, sameM) land in the same subgroup, preserving the join partition-count invariant within the subgroup.Acceptance criteria
Msubgroup.M.Parent epic: #2092 (Enable AQE by default) / #1359 (AQE design)