Skip to content

fix(scheduler): keep an empty exchange whose parent needs hash partitioning - #2837

Open
0xdeafbeef wants to merge 1 commit into
apache:mainfrom
0xdeafbeef:fix-aqe-empty-input-loop
Open

0xdeafbeef wants to merge 1 commit into
apache:mainfrom
0xdeafbeef:fix-aqe-empty-input-loop

Conversation

@0xdeafbeef

Copy link
Copy Markdown

Which issue does this PR close?

No issue filed; the bug is described below.

Rationale for this change

With adaptive query planning on, this query never finishes when empty has no rows and the join is planned as partitioned:

SELECT c.id, e.id AS other FROM customers c LEFT JOIN empty e ON e.email = c.email

Setting ballista.planner.propagate_empty.enabled = false avoids it.

The job creates a new stage on every replan:

  1. The stage that reads empty finishes with zero rows, and PropagateEmptyExecRule replaces its exchange with an EmptyExec.
  2. The join above stays, because the rule keeps a join that has an embedded projection (the fix(scheduler): harden PropagateEmptyExecRule against grouping sets, embedded projections, and dropped exchange partitioning #2200 guard).
  3. The join still requires hash-partitioned input, and EmptyExec reports UnknownPartitioning. So EnsureRequirements adds a hash RepartitionExec over the EmptyExec, and DistributedExchangeRule turns it into a new stage.
  4. The new stage finishes with zero rows, and the next replan does the same with it.

The same happens to other outer and anti joins with an embedded projection, to a mark join, and to a window function with PARTITION BY. The rule has no rewrite for a mark join or for the window's SortExec, so they stay too.

What changes are included in this PR?

PropagateEmptyExecRule now leaves the finished exchange in place when an operator above it stays and requires its hash partitioning. That operator reads the empty stage through a shuffle reader, which reports the hash partitioning, so EnsureRequirements adds nothing and no new stage is created.

The rule's rewrites are unchanged: it still removes an inner join with an empty side, and the #2200 guard still applies.

A join whose empty side finishes while the join is still a DynamicJoinSelectionExec runs one more empty stage before the plan settles.

Are there any user-facing changes?

Queries that hung on an empty input now finish. No API or configuration change.
Maybe someone's cat will get cold :(
https://xkcd.com/1172/

…ioning

With adaptive query planning on, a query never finished when an input
turned out to be empty at run time below an outer or anti join with an
embedded projection, a mark join, or a window function with
PARTITION BY.

PropagateEmptyExecRule replaced the finished exchange of the empty
stage with an EmptyExec, which reports UnknownPartitioning. The
operator above stayed and still required hash-partitioned input, so
EnsureRequirements added a hash RepartitionExec over the EmptyExec, and
DistributedExchangeRule turned it into a new stage. The new stage
finished with zero rows, and the next replan did the same with it.

The rule now leaves the finished exchange in place when an operator
above it stays and requires its hash partitioning. That operator reads
the empty stage through a shuffle reader, which reports the hash
partitioning, so no new stage is created.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant