test: cover DF-promoted CollectLeft broadcast safety gap - #89
Conversation
With the new defaults from this PR, DataFusion's own JoinSelection runs first under the production session config and can promote a HashJoinExec to PartitionMode::CollectLeft for `small LEFT JOIN big` with no swap, because the small side is already on the left. maybe_promote_to_broadcast then short-circuits on `partition_mode != Partitioned`, so the new collect_left_broadcast_safe guard never sees the join and plan_query_stages_internal lowers it via UnresolvedShuffleExec::new_broadcast — re-opening the apache#1055 unsafety that pinning the thresholds to zero used to prevent. Existing tests miss this because make_broadcast_test_ctx pins DF's threshold to 0; only the Ballista promotion path is exercised. This test mirrors the production session (DF and Ballista thresholds both at 10 MB) and asserts that no LEFT join ends up CollectLeft and no broadcast reader is produced. Currently fails on this branch; intended to drive the fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Confirmed and fixed in apache#1900 (commit b40a670). This was a real gap — DataFusion's own The fix moves the broadcast-safety check to the front of Your test is pulled into apache#1900 verbatim (just swapped the explanatory comment for a current-state one to match the repo's comment convention) and now passes. Thanks for catching it — I'll leave this PR for you to close. |
|
This seems like a good case for AQE in DF. If we had that infrastructure, we could port |
Stacked on apache#1900 — adds a single failing test that exposes a gap in the safety guard.
What the test shows
With the new session defaults from apache#1900 (
hash_join_single_partition_threshold = 10 MB), DataFusion's ownJoinSelectionruns duringcreate_physical_planand can produceHashJoinExec(LEFT, CollectLeft)forsmall LEFT JOIN big— no swap, because the small side is already on the left.try_collect_leftin DF doesn't restrict by join type (it doesn't need to in standalone DF, where there's only one probe task).Ballista's
maybe_promote_to_broadcastshort-circuits onpartition_mode != Partitioned(planner.rs:294), so the newcollect_left_broadcast_safeguard never sees the join.plan_query_stages_internalthen lowers it viaUnresolvedShuffleExec::new_broadcastatplanner.rs:145-184— re-opening the apache#1055 unsafety that pinning the thresholds to zero used to prevent.Reproducer output on this branch:
Why existing tests don't catch it
make_broadcast_test_ctxpinsdatafusion.optimizer.hash_join_single_partition_threshold = 0(planner.rs:1494-1497), so DF can never promote on its own in test sessions — only the Ballista promotion path is exercised, which is correctly guarded. The new test mirrors the production session (DF and Ballista thresholds both at 10 MB) so DF'sJoinSelectionis what does the promotion.Why AQE is unaffected
DelayJoinSelectionRuleruns inplan_preparation_optimizersbefore DF'sJoinSelectionand wraps everyHashJoinExecin aDynamicJoinSelectionExec, so DF's rule finds nothing to promote. The static path has no equivalent wrapper.Suggested fix shape (for apache#1900)
In
maybe_promote_to_broadcast, whenpartition_mode == CollectLeftand!collect_left_broadcast_safe(*hash_join.join_type()), demote toPartitionedinstead of returning early. Same predicate, same module.This PR intentionally contains only the test — happy to land the fix here or in apache#1900, whichever you prefer.
Checklist