Problem
insert_broadcast_execs always inserts a new BroadcastExec beneath an eligible join, even when the build input already has the canonical broadcast shape:
CoalescePartitionsExec
BroadcastExec
input
For a fetch-less CoalescePartitionsExec, the pass unwraps the coalesce and uses its input as the payload for a new broadcast. If that input is already a BroadcastExec, the resulting plan becomes:
CoalescePartitionsExec
BroadcastExec
BroadcastExec
input
Applying the pass repeatedly adds another broadcast layer each time.
Impact
The nested broadcasts preserve query results, but they create unnecessary cache state and duplicate memory reservations for the same batches. This increases memory pressure and can make spilling or resource-exhaustion failures more likely.
It also means the optimizer pass is not idempotent, which is surprising when plans have already been processed or when an earlier custom optimizer has inserted the canonical broadcast shape.
Proposed behavior
Before inserting a broadcast, inspect the build input:
- If it is already
CoalescePartitionsExec(BroadcastExec(...)) with no fetch, leave it unchanged.
- If the build is directly a
BroadcastExec, add only the required coalesce wrapper rather than another broadcast layer, if that shape is supported.
- Preserve the existing handling for fetch-bearing
CoalescePartitionsExec, where the fetch must remain below the broadcast.
The exact normalization can vary, but a second application of insert_broadcast_execs should produce the same plan as the first.
Testing
Add focused tests for:
HashJoinExec with an existing canonical broadcast build.
NestedLoopJoinExec with an existing canonical broadcast build.
CrossJoinExec with an existing canonical broadcast build.
- A direct
BroadcastExec build input, if supported.
- A fetch-bearing coalesce to ensure its limit remains below the broadcast.
- Applying
insert_broadcast_execs twice and asserting that the second result is structurally unchanged.
Plan assertions should verify that every eligible build contains exactly one BroadcastExec layer.
Acceptance criteria
- Existing canonical broadcast build inputs are not wrapped again.
- Repeated optimizer application is idempotent.
- HashJoin, NestedLoopJoin, and CrossJoin are covered.
- Fetch-bearing coalesce semantics are preserved.
- Existing broadcast planning and correctness tests continue to pass.
References
Problem
insert_broadcast_execsalways inserts a newBroadcastExecbeneath an eligible join, even when the build input already has the canonical broadcast shape:For a fetch-less
CoalescePartitionsExec, the pass unwraps the coalesce and uses its input as the payload for a new broadcast. If that input is already aBroadcastExec, the resulting plan becomes:Applying the pass repeatedly adds another broadcast layer each time.
Impact
The nested broadcasts preserve query results, but they create unnecessary cache state and duplicate memory reservations for the same batches. This increases memory pressure and can make spilling or resource-exhaustion failures more likely.
It also means the optimizer pass is not idempotent, which is surprising when plans have already been processed or when an earlier custom optimizer has inserted the canonical broadcast shape.
Proposed behavior
Before inserting a broadcast, inspect the build input:
CoalescePartitionsExec(BroadcastExec(...))with no fetch, leave it unchanged.BroadcastExec, add only the required coalesce wrapper rather than another broadcast layer, if that shape is supported.CoalescePartitionsExec, where the fetch must remain below the broadcast.The exact normalization can vary, but a second application of
insert_broadcast_execsshould produce the same plan as the first.Testing
Add focused tests for:
HashJoinExecwith an existing canonical broadcast build.NestedLoopJoinExecwith an existing canonical broadcast build.CrossJoinExecwith an existing canonical broadcast build.BroadcastExecbuild input, if supported.insert_broadcast_execstwice and asserting that the second result is structurally unchanged.Plan assertions should verify that every eligible build contains exactly one
BroadcastExeclayer.Acceptance criteria
References
src/distributed_planner/insert_broadcast.rs