Skip to content

fix(datasource): scope file scan work stealing to opened partitions - #173

Closed
phillipleblanc wants to merge 4 commits into
spiceai-54from
phillip/cluster-workstealing-fix
Closed

fix(datasource): scope file scan work stealing to opened partitions#173
phillipleblanc wants to merge 4 commits into
spiceai-54from
phillip/cluster-workstealing-fix

Conversation

@phillipleblanc

@phillipleblanc phillipleblanc commented Jun 26, 2026

Copy link
Copy Markdown

Summary

DataSourceExec creates sibling-shared state once per scan execution so file-scan streams can steal unopened files from one another. The old SharedWorkSource was populated with all file groups up front. That is only safe when the same process opens all output partitions of the scan.

Distributed execution engines such as Ballista can run each output partition in a separate process. In that model, every process recreated a shared queue containing the full input and its one local stream could drain every file group. That caused N-fold scan amplification and, with partial aggregation above the scan, inflated aggregate results.

This fixes the ownership model instead of adding a session flag: SharedWorkSource now starts empty, and each FileStream registers only the file group for the partition it opens. A process that opens one partition can only scan that partition's files; a local process that opens all partitions still gets cross-partition work stealing.

Changes

  • Create an empty SharedWorkSource for file scans.
  • Register a partition's file group when FileStreamBuilder opens that partition.
  • Track registered partitions so the same file group is not enqueued twice.
  • Reorder the currently queued files after registration so TopK file ordering still applies to available work.
  • Keep preserve_order and partitioned_by_file_group on local work sources, as before.
  • Remove the execution.enable_file_scan_work_stealing option and the DataSourceExec gate from the earlier version of this PR.

Test plan

  • cargo test -p datafusion-datasource --lib
  • CI

…ross-partition scan work-stealing

DataSourceExec shares one SharedWorkSource queue across all of a scan's
partition streams via a per-instance OnceLock, so sibling streams in the same
process steal work and each file is opened exactly once. That is correct only
when all output partitions execute in the same process. Distributed engines
(e.g. Ballista) run each partition in a separate process, where every process
rebuilds the full queue over all file_groups and reads the entire input —
N-fold scan amplification and, with a Hash-shuffled partial aggregate above the
scan, N-fold inflated aggregate results.

Add `execution.enable_file_scan_work_stealing` (default true, so single-node
behavior is unchanged) and gate `create_sibling_state` in
`DataSourceExec::execute` on it. When false, each partition reads only its own
file group (`WorkSource::Local`) — the correct behavior under distributed
execution.

Claude-Session: https://claude.ai/code/session_01G8drJk8Fx5ynRQZ4WkuVju

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new execution config option to control cross-partition work-stealing for file scans, addressing correctness issues in distributed execution environments where each scan partition may run in a separate process.

Changes:

  • Added execution.enable_file_scan_work_stealing (default true) to explicitly control whether sibling scan partitions share a single work queue.
  • Gated DataSourceExec::execute sibling-state initialization on this option so disabling it forces per-partition local file-group scanning.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
datafusion/datasource/src/source.rs Gates creation/usage of per-execution sibling shared state based on the new execution option.
datafusion/common/src/config.rs Adds the execution.enable_file_scan_work_stealing configuration option with detailed rationale and guidance for distributed engines.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread datafusion/datasource/src/source.rs Outdated
Comment thread datafusion/datasource/src/source.rs Outdated
Copilot review follow-ups on the `enable_file_scan_work_stealing` gate in
`DataSourceExec::execute`:

- Document that `create_sibling_state` is the work-stealing hook and is
  currently only implemented by file-scan sources (`FileScanConfig`); every
  other `DataSource` inherits the default `None`, so the gate is effectively
  file-scan scoped and the option name is accurate. Note that a future
  `DataSource` introducing its own sibling state would also be governed by the
  flag and should revisit the intended behavior.

- Add a regression test that drives `DataSourceExec::execute` with a probe
  `DataSource` and asserts the gate flips sibling state: shared when
  `enable_file_scan_work_stealing=true`, absent when `false` (so each partition
  opens in isolation and reads only its own file group — no cross-partition
  work-stealing under distributed execution).

Claude-Session: https://claude.ai/code/session_01G8drJk8Fx5ynRQZ4WkuVju
@phillipleblanc

Copy link
Copy Markdown
Author

Addressed the review feedback in 3e97c01:

  1. Gate scope — expanded the comment in DataSourceExec::execute to make explicit that create_sibling_state is the work-stealing hook and is currently only implemented by file-scan sources (FileScanConfig); every other DataSource inherits the default None, so the gate is effectively file-scan scoped and the enable_file_scan_work_stealing name is accurate. Also flagged that a future DataSource adding its own sibling state would be governed by this flag and should revisit the intended behavior.

  2. Regression test — added work_stealing_gate_controls_sibling_state, which drives DataSourceExec::execute with a probe DataSource and asserts the gate flips sibling state: shared when enable_file_scan_work_stealing=true, absent when false (each partition opens in isolation and reads only its own file group — no cross-partition work-stealing).

Copilot AI review requested due to automatic review settings June 28, 2026 23:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

Create SharedWorkSource empty and register each partition's file group only when that partition is opened. This preserves local sibling work stealing while preventing distributed workers that execute a single partition from scanning every file group.

Remove the execution.enable_file_scan_work_stealing config gate and add/adjust morsel tests for single-partition registration and late partition registration.
@phillipleblanc phillipleblanc changed the title feat(datasource): add enable_file_scan_work_stealing option to gate cross-partition scan work-stealing fix(datasource): scope file scan work stealing to opened partitions Jun 29, 2026
@phillipleblanc

Copy link
Copy Markdown
Author

Superseded by the upstream-aligned ballista-side fix.

apache/datafusion-ballista#1906 restricts each task's DataSourceExec to its partition's file group in the executor (apache#1907) and disables physical uncorrelated scalar subqueries in the restricted config (apache#1909) — fixing the scan over-read/amplification and TPC-H q11/q15/q22 with no DataFusion change. Both are now ported into the Ballista fork (spiceai/datafusion-ballista#57), so this DataFusion-side change isn't needed and we avoid a fork divergence.

Recommend closing — or repurposing the register-on-open approach as an upstream apache/datafusion proposal if we want the root-cause fix to live in DataFusion.

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.

2 participants