Problem
All four AutoTuner paths for spark.sql.shuffle.partitions raise or hold; none lowers. Three multiply by a fixed factor on a yes/no trigger without looking at data volume. The fourth, the ColumnarExchange bound, is size-aware — but once a doubling carries the value past it, it stops being the maximum and drops out. The count ratchets up with nothing to check it.
Evidence
Three successive runs of one query, same ~4.08 TB shuffle, 12 × 16-core cluster. Every raise came from spill ×2:
| ran at |
recommended |
outcome |
| 500 |
1900 |
cut off at 694s |
| 1900 |
3800 |
cut off at 760s |
| 3800 |
7600 |
succeeded in 303s |
The third run succeeded and was still told to double, over 3.0 GB of residual spill. Nothing can bring 7600 back down. Meanwhile ceil(4.079 TB / 1 GiB) = 3800 — the configuration that worked — was computable from the first run.
What's needed
A pass sizing partitions from measured shuffle input, able to move the value either way, with the existing spill and OOM paths as floors. Sizing from consumer-stage totals, not the largest single exchange. See #2129.
Open questions
- Should the target derive from
spark.rapids.sql.batchSizeBytes? Raising the batch for kernel efficiency silently halves the partition count — two unrelated concerns on one knob.
- May a measured target correct a spill-driven raise? A pass computes 3840 against that 7600, but only helps if existing raises aren't inviolable.
Problem
All four AutoTuner paths for
spark.sql.shuffle.partitionsraise or hold; none lowers. Three multiply by a fixed factor on a yes/no trigger without looking at data volume. The fourth, theColumnarExchangebound, is size-aware — but once a doubling carries the value past it, it stops being the maximum and drops out. The count ratchets up with nothing to check it.Evidence
Three successive runs of one query, same ~4.08 TB shuffle, 12 × 16-core cluster. Every raise came from spill ×2:
The third run succeeded and was still told to double, over 3.0 GB of residual spill. Nothing can bring 7600 back down. Meanwhile
ceil(4.079 TB / 1 GiB) = 3800— the configuration that worked — was computable from the first run.What's needed
A pass sizing partitions from measured shuffle input, able to move the value either way, with the existing spill and OOM paths as floors. Sizing from consumer-stage totals, not the largest single exchange. See #2129.
Open questions
spark.rapids.sql.batchSizeBytes? Raising the batch for kernel efficiency silently halves the partition count — two unrelated concerns on one knob.