Repository navigation
Run the sqllogictest corpus across a matrix of batch sizes #26700
Description
Is your feature request related to a problem or challenge?
Almost all sqllogictest files run with a single datafusion.execution.batch_size (the default 8192). Most slt tables are small, so nearly every operator sees one input batch per partition. Code paths that only run when the input is split across many batches are rarely exercised end to end: carrying state across batch boundaries, partial emits, coalescing, buffered join sides, and window frames that span batches.
#20919 showed that this gap hides real bugs. Building with batch_size = 1 produced panics, internal assertion failures and wrong results, not just changed EXPLAIN output.
# configMatrix: (#24493) can already rerun a file across several values of a config key. Today only a few files sweep batch_size with it (sort_merge_join_matrix.slt, piecewise_merge_join_matrix.slt, mark_join_matrix.slt, null_aware_multi_column.slt, piecewise_merge_join_nested_nulls.slt). The rest of the corpus, covering aggregates, windows, sorts, unnest, limits, set operations and the other join files, runs at one batch size only.
Describe the solution you'd like
Run the slt tests for all operators under a matrix of batch sizes, for example 1, 2, 7, 8192. That would be a small size, a size that does not evenly divide typical row counts, and the default.
A possible shape:
- Add a runner option, e.g.
--batch-sizes 1,2,7,8192, that runs every file once per value. It would reuse theconfigMatrixmachinery, so failures are reported with the combination that produced them. No per-file directive would be needed. - For non-default sizes, check only what does not depend on batch size:
- Skip
EXPLAINrecords, since plans can change with batch size. - Compare results without regard to row order unless the query has an
ORDER BY. - Give in-file
SET datafusion.execution.batch_sizeprecedence, or skip those files for the sweep.
- Skip
- Run the sweep in CI, either in the extended-tests job or nightly, so it does not slow down the main
sqllogictestrun. - Fix or file the failures it finds, then make the sweep required.
The default run stays as it is today and still checks plans and exact output.
Describe alternatives you've considered
- Add
# configMatrix: datafusion.execution.batch_size=...to each file. This works with the existing runner, but it means editing about 600.slt/.slt.partfiles. About 170 of them containEXPLAIN, so they would first need to be split into a plan file and a matrix file. It also relies on every new file opting in. - Change the default
batch_sizeonly in theslttest context. That tests one other value instead of several, and it changes the expected output of everyEXPLAINthat prints the batch size. - Fuzz tests per operator. These are useful, but they cover only the operators someone writes a fuzzer for and none of the SQL-level shapes in the corpus.
Additional context
- Improve DataFusion stability for small
batch_sizevalues #20919: crashes and wrong results withbatch_size = 1 - Support config matrix for slt tests #24390 / Support config matrix for SLT tests #24493:
configMatrixsupport forslt - Run the sqllogictest corpus under an alternative physical optimizer pipeline #26152: a similar proposal to run the corpus under an alternative optimizer pipeline using the same machinery
Activity
jayzhan211 commentedon Oct 24, 2026
I'd prefer to test multiple batch sizes only when the batch size actually matters. Otherwise, we can just test with the default size to minimize the number of tests we run while keeping the same coverage.
comphead commentedon Oct 24, 2026
These are the
sltfiles to amend, grouped by operator. Paths are relative todatafusion/sqllogictest/test_files/.Markers:
EXPLAIN, so it needs a plan/matrix split (or the runner has to skipEXPLAINfor non-default sizes).datafusion.execution.batch_sizeitself, so thatSEThas to be removed or reconciled with the sweep.Aggregation
aggregate.slt(E)aggregate_any_value.sltaggregate_filter_selection.slt(S)aggregate_memory_spill.slt(E, S)aggregate_repartition.slt(E)aggregate_skip_partial.sltaggregates_topk.slt(E)array_agg.slt(E)distinct_on.slt(E)first_last_nested.sltfirst_last_ordered.sltgroup.sltgroup_by.slt(E)grouping.slt(E)grouping_set_repartition.slt(E)ordered_aggregate_key_types.slt(E, S)ordered_aggregate_spill.slt(E, S)range_sorted_time_bin_agg.slt(E)single_distinct_to_groupby.slt(E)min_max/*.sltspark/aggregate/avg.slt,spark/aggregate/collect.slt,spark/aggregate/try_sum.sltWindows
window.slt(E, S)window_limits.slt(E)window_topk_pushdown.slt(E)window_topn.slt(E)array_agg_sliding_window.sltlead_lag_extreme_offsets.sltqualify.slt(E)Joins
joins.slt(E)join.slt.part(E), included byjoin_only.sltandjoin_disable_repartition_joins.slt(E)join_is_not_distinct_from.slt(E)join_limit_pushdown.slt(E)join_lists.sltjoin_dynamic_filter_transfer.slt(E)joins_conditionless.sltsame_column_name_cross_join.sltasof_join.slt(E)lateral_join.slt(E)sort_merge_join.slt(E)sort_merge_join_batches.slt(E, S)sort_merge_join_spill.slt(E, S)pwmj.slt(E)piecewise_merge_join_batches.slt(E, S)nested_loop_join_existence.slt(E, S)nested_loop_join_lambda.slt(E)nested_loop_join_spill.slt(E, S)null_aware_anti_join.slt(E, S)null_aware_mark_join.slt(E, S)These files already sweep
batch_sizewithconfigMatrix:sort_merge_join_matrix.slt,piecewise_merge_join_matrix.slt,piecewise_merge_join_nested_nulls.slt,mark_join_matrix.slt,null_aware_multi_column.slt.Limits, TopK and sort
limit.slt(E)limit_single_row_batches.sltlimit_pruning.slt(E)topk.slt(E)push_down_topk_through_join.slt(E)order.slt(E)order_by_all.sltsubquery_sort.slt(E)Set operations
union.slt(E, S)union_by_name.sltintersection.sltRecursive CTE and subqueries
subquery.slt(E, S)Repartition
repartition.slt(E)repartition_scan.slt(E)range_partitioning.slt(E)jayzhan211 commentedon Oct 24, 2026
The current approach applies one batch size matrix per file, but I think configuring batch sizes per query would be more flexible.
comphead commentedon Oct 24, 2026
thats my vision too, start with operators. joins/windows/aggregations/topk/limit, etc
comphead commentedon Oct 24, 2026
I feel we can generalize it next way:
2010YOUY01 commentedon Oct 24, 2026
Thanks for driving this! IMO, this is the highest-ROI improvement we can make to our testing right now.
Suggestion: make it more automatic
I think the existing practice is manual fuzzing:
batch_sizevalues and repeat the same query in ansltfileI think it would be great if we could make this more automatic (no need to manually define which
batch_sizevalues to test).The implementation might look like:
batch_size/target_partitions, let the SLT runner decide the values (Q1: batch_size=8192, Q2: batch_size=7, ...)Then the runner could generate random configs across different fuzzing runs, and we could set the CI runner to run as many tests as possible within a time limit, instead of enumerating all combinations, which could become very slow as we keep adding config matrix dimensions.
2010YOUY01 commentedon Oct 24, 2026
If we do some refactor to split existing correctness and EXPLAIN tests, config fuzzing will likely become easier 🤔
comphead commentedon Oct 27, 2026
Thanks @2010YOUY01 @jayzhan211 I'll come up with first PR soon and we can discuss there