yashmayya opened a new pull request, #19330:
URL: https://github.com/apache/pinot/pull/19330

   `UNION ALL` is a pure concatenation, so any row-to-worker mapping produces 
the same result and no redistribution is ever required. The V2 physical 
optimizer has always known this — `TraitAssignment#assignSetOp` returns early 
for `Union.all` ("UNION ALL means we can return duplicates, so no trait 
required"). The V1 (default) planner did not: 
`PinotSetOpExchangeNodeInsertRule` unconditionally hash-shuffled every set-op 
input on the full output row, `UNION ALL` included.
   
   This brings V1 in line. A `UNION ALL` input now gets a **local exchange** — 
`SINGLETON`, which is how Pinot already spells "local exchange" — carrying the 
projected columns as keys. The union stage inherits its inputs' worker 
assignment and each sender hands its rows to the worker on its own server: no 
shuffle, no hashing, and no network hop when sender and receiver land together.
   
   Opt out per query with `/*+ 
setOpOptions(is_colocated_by_set_op_keys='false') */`, which restores the 
full-row shuffle.
   
   ### Why the keys are attached
   
   They are unused while the exchange stays local. They are there so the 
mailbox layer can **promote** the exchange to a real `HASH_DISTRIBUTED` shuffle 
when the branches cannot share a worker assignment — the same idiom 
`PinotJoinExchangeNodeInsertRule` already uses for `distribution_type='local'`:
   
   > `// NOTE: We use SINGLETON to represent local distribution. Add keys to 
the exchange because we might want to switch it to HASH distribution to 
increase parallelism.`
   
   It also means no new marker or discriminator is needed anywhere. `SINGLETON` 
is shared with the colocated dynamic-broadcast semi-join build side, which is 
*keyless* and must never be redistributed (every receiver needs the whole build 
side — the non-colocated variant broadcasts for exactly that reason). Because a 
`UNION ALL` input always carries keys, the existing `"Local exchange with 
parallelism requires keys"` guard keeps protecting that path untouched.
   
   ### Worker assignment
   
   `WorkerManager` previously rejected a stage with more than one 
local-exchange child outright, which every multi-branch `UNION ALL` would hit. 
It now accepts them when they all agree on worker map, partition parallelism, 
partition function and partition classes — the stage inherits from the first, 
so it may only do so when that one describes them all. A child with no worker 
at all (a fully pruned leaf) can never anchor it, or the stage would end up 
with no workers and silently drop every live sibling's rows.
   
   When they disagree, the stage falls back to the regular assignment and each 
branch is promoted to a hash shuffle. Branches that still line up 1-to-1 stay 
local, so a misaligned sibling does not cost the aligned ones anything.
   
   ### Co-residency fix
   
   The parallel wiring in `computeDirectExchangeWithParallelism` addresses a 
whole contiguous receiver range at the range's **first** host. That only holds 
when the receiver map was derived from the sender by 
`assignWorkersForLocalExchange`. When the stage instead takes its workers from 
the candidate servers, a range spans several hosts — blocks would be posted to 
a mailbox on the wrong server and the real receiver would block until the query 
deadline.
   
   This now verifies co-residency instead of assuming it, and falls back to a 
hash shuffle when it does not hold. That is a fix for the existing callers too, 
not only for `UNION ALL`.
   
   A consequence: a local exchange whose worker counts do not divide evenly 
used to throw. It now falls back to a hash shuffle, which is correct for every 
local-exchange kind since `HashExchange` routes each key consistently. A join 
hinted `local` on both sides with mismatched worker counts previously failed 
outright and now plans and runs correctly.
   
   Note the 1-to-1 path is unaffected: it already resolves the server per 
worker and emits separate mailboxes when sender and receiver diverge, so 
aligned-but-not-co-located stays correct and merely loses the in-process 
handover.
   
   ### Set-op output distribution
   
   `PinotRelDistributionTraitRule` had no `SetOp` case, so every set operation 
fell through to `RANDOM_DISTRIBUTED`. It now derives the output from what the 
input exchanges actually do:
   
   - all inputs are hash exchanges (`INTERSECT` / `EXCEPT` / distinct `UNION`, 
or `UNION ALL` with the hint set to `'false'`) → hash distributed on all output 
columns, so a downstream exchange keyed on those columns can skip its own 
shuffle. This holds for `is_colocated_by_set_op_keys='true'` too: the hint 
asserts that rows equal across all projected columns already share a worker, 
which is exactly the claimed distribution, and we take it at its word just as 
the exchange does.
   - any input is a local exchange (the `UNION ALL` case) → nothing claimed, 
because a local exchange redistributes nothing and therefore guarantees 
nothing. This is what keeps a deduplicating consumer — for example the 
aggregate `UnionToDistinctRule` puts over a distinct `UNION` — from skipping a 
shuffle it needs.
   
   This part is independently useful and could be split into its own PR if 
reviewers prefer; it is not required for the `UNION ALL` change, since set ops 
already defaulted to `RANDOM_DISTRIBUTED`.
   
   ### Notes
   
   - V1 only. The V2 physical optimizer (`usePhysicalOptimizer=true`) already 
plans `UNION ALL` without a shuffle and is untouched.
   - No wire-format change: `SINGLETON` and `HASH_DISTRIBUTED` are pre-existing 
`DistributionType` values already handled by `BlockExchange`, and the decision 
is broker-side at plan time. No rolling-upgrade concern and no 
`backward-incompat` label.
   - `is_colocated_by_set_op_keys='true'` is now a no-op on a `UNION ALL` (its 
inputs get a local exchange either way); only `'false'` changes anything there. 
The hint is unreleased, so no compatibility shim is owed.
   - There is deliberately **no** cluster-wide kill switch. Correctness never 
depends on this, the guards above prevent mis-wiring structurally, the 
per-query hint is the escape hatch, and V2 ships the same behaviour with no 
switch. Happy to add a broker config if reviewers would rather have one.
   - Behaviour change: the default plan shape for every hint-free `UNION ALL` 
changes. Because the union stage inherits its inputs' worker layout instead of 
redistributing, input skew is carried into the union stage rather than 
rebalanced; in practice the next exchange above the union re-partitions.
   - One small unrelated fix rides along because this feature's own escape 
hatch can reach it: `isDirectExchangeCompatible` divided by zero when a leaf 
stage had all of its segments pruned.
   
   ### Testing
   
   - `QueryCompilationTest` — local exchange with keys by default, the hint 
opt-out, misaligned branches where only the misaligned one is promoted to a 
shuffle, distinct set ops unaffected, and both directions of the distribution 
derivation.
   - `MailboxAssignmentVisitorTest` — promotion to a hash shuffle on unequal 
counts, per-host addressing when a receiver range spans servers, zero-sender 
wiring, and a negative control proving a *keyless* local exchange (the 
semi-join build side) still fails loudly.
   - `ExplainPhysicalPlans.json` / `SetOpPlans.json` / `AggregatePlans.json` — 
updated plan shapes; the `'false'` opt-out plan is retained.
   - `QueryHints.json` (compared against H2, replayed on both optimizers) — 
`UNION ALL` correctness, dedup and `GROUP BY` above a local union, the 
colocated `INTERSECT` claim, and the mismatched-partition-count case.
   
   ### Known gaps
   
   Three of the four disagreement checks in `canInheritWorkerAssignment` 
(partition parallelism, partition classes, partition function) have no direct 
test. Not a known bug, but a place a future change could regress quietly. Happy 
to add them here if preferred.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to