You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@datafusion.apache.org by "andygrove (via GitHub)" <gi...@apache.org> on 2026/07/16 16:31:25 UTC

[I] UNION ALL silently returns inflated results when the union stage exceeds one wave of executor task slots [datafusion-ballista]

andygrove opened a new issue, #2067:
URL: https://github.com/apache/datafusion-ballista/issues/2067

   ## Describe the bug
   
   A `UNION ALL` over two file scans returns **massively inflated results** whenever
   the union's stage has more tasks than the executor has concurrent task slots.
   The query does not error — it silently over-counts, so aggregates come back
   several times too large.
   
   ## To Reproduce
   
   SF1 TPC-DS parquet, 1 scheduler + 1 executor, release build:
   
   ```sql
   select round(sum(p),2) from
    (select ws_ext_sales_price p from web_sales
     union all
     select cs_ext_sales_price p from catalog_sales);
   ```
   
   With `--partitions 16` and an executor started `-c 8`:
   
   ```
   expected 5492796521.50   (single-process DataFusion, same data)
   got     42072988115.00   (~8x inflated; varies run to run)
   ```
   
   Summing each table separately is correct, and the same aggregate over a single
   table (no `UNION ALL`) is correct — only the union is wrong.
   
   Note `select count(*)` over the union is **not** a reproducer: it is answered
   from parquet statistics without reading data. The aggregate must touch a column.
   
   ## The rule: it breaks exactly when the stage exceeds one wave of task slots
   
   A `UnionExec` has as many partitions as its children combined, so an N-partition
   union stage runs `2N` tasks. The result is correct **iff those tasks fit in a
   single concurrent wave** on the executor:
   
   Executor `-c 8`:
   
   | `--partitions` | union stage tasks | result |
   |---:|---:|---|
   | 3 | 6 | correct |
   | 4 | 8 | correct |
   | 5 | 10 | **wrong** (12808834840.20) |
   | 6 | 12 | **wrong** (16466853999.55) |
   | 8 | 16 | **wrong** (16466853999.55) |
   
   Same binary, same data, executor `-c 32` — the threshold moves with the slot
   count, which isolates the cause:
   
   | `--partitions` | union stage tasks | result |
   |---:|---:|---|
   | 5 | 10 | correct |
   | 8 | 16 | correct |
   | 16 | 32 | correct |
   | 20 | 40 | **wrong** (20124873158.90) |
   
   So: **correct iff `2 * partitions <= executor slots`.**
   
   ## Suspected cause
   
   `restrict_scan_to_partition` (`ballista/executor/src/execution_engine.rs`) exists
   precisely because DF54's `DataSourceExec` hands file groups out from a shared
   work-queue that is only divided correctly when all partitions of a plan instance
   are polled concurrently (#1907). It pins each task's scan to its own file group —
   but bails out early:
   
   ```rust
   if partition_id >= config.file_groups.len() {
       return None;   // no restriction -> scan left free to drain the whole queue
   }
   ```
   
   Its own doc comment anticipates this case:
   
   > Returns `None` ... for a `partition_id` outside the source's file groups (e.g.
   > when an operator between the scan and the stage output changed the partition
   > count).
   
   A `UnionExec` is exactly such an operator: with `target_partitions=16` each scan
   has 16 file groups but the union stage has 32 partitions, so tasks 16..31 skip
   the restriction and their scans are left unrestricted. An unrestricted scan only
   behaves when the whole stage happens to be polled in one concurrent wave, which
   is precisely the boundary measured above.
   
   Fixing it presumably means mapping the stage's output partition to each scan's
   *local* partition index (a union offsets its right child's partitions by the left
   child's count) rather than using the stage `partition_id` directly, so every
   scan is pinned to the right file group.
   
   ## Impact
   
   - **16 of the 99 TPC-DS queries use `UNION ALL`** (q2, q4, q5, q11, q14, q23,
     q33, q54, q56, q60, q66, q71, q74, q76, q77, q80), so this is broadly reachable
     on any realistic partitions/slots ratio.
   - It accounts for the q2 and q5 failures currently skipped in the TPC-DS
     correctness gate as "non-deterministic (ORDER BY ties)". That label is wrong:
     with a total order imposed and `LIMIT` removed, both still mismatch, so they
     are real wrong results, not tie ambiguity. (q31 and q71 *are* genuinely
     ordering-only — both verify OK once a total order is imposed.)
   - Silent: nothing errors, results are just too large.
   
   ## Expected behavior
   
   `UNION ALL` returns the same result as single-process DataFusion, independent of
   `target_partitions` and executor slot count.
   
   ## Additional context
   
   - Reproduced at SF1 on a 1-scheduler / 1-executor native cluster, `upstream/main`
     (e843c8e8), default static planner, under both `prefer_hash_join=true` and
     `false`.
   - Regression surface introduced with the DataFusion 54 upgrade; closely related to
     #1907, whose fix covers the non-union case (a plain scan verifies correctly at
     any partition/slot ratio) but leaves partitions beyond `file_groups.len()`
     unrestricted.
   


-- 
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: github-unsubscribe@datafusion.apache.org.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: github-unsubscribe@datafusion.apache.org
For additional commands, e-mail: github-help@datafusion.apache.org


Re: [I] UNION ALL silently returns inflated results when the union stage exceeds one wave of executor task slots [datafusion-ballista]

Posted by "andygrove (via GitHub)" <gi...@apache.org>.
andygrove commented on issue #2067:
URL: https://github.com/apache/datafusion-ballista/issues/2067#issuecomment-4994615135

   Fixed in #2070.
   
   Root cause: `restrict_scan_to_partition` applied the stage's partition number directly to every scan, but a `UnionExec` concatenates its children's partitions — stage partition `N + k` is the right child's local partition `k`. For partitions past a scan's group count the guard bailed out and left that scan **unrestricted**, free to read its whole table.
   
   The fix walks the plan from the stage root and mirrors `UnionExec::execute`'s own partition arithmetic, pinning each scan to the group its local partition reads. The minimal case is now correct at every partition count tried (4, 5, 6, 8, 16, 20, 32), and q2/q5 verify in their original form and join the gate.


-- 
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: github-unsubscribe@datafusion.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: github-unsubscribe@datafusion.apache.org
For additional commands, e-mail: github-help@datafusion.apache.org


Re: [I] UNION ALL silently returns inflated results when the union stage exceeds one wave of executor task slots [datafusion-ballista]

Posted by "andygrove (via GitHub)" <gi...@apache.org>.
andygrove closed issue #2067: UNION ALL silently returns inflated results when the union stage exceeds one wave of executor task slots
URL: https://github.com/apache/datafusion-ballista/issues/2067


-- 
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: github-unsubscribe@datafusion.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: github-unsubscribe@datafusion.apache.org
For additional commands, e-mail: github-help@datafusion.apache.org