Skip to content

feat(scan): run the V2 executor on the segment IO protocol - #10527

Draft
joseph-isaacs wants to merge 1 commit into
ji/segment-io-protocolfrom
ji/segment-io
Draft

joseph-isaacs wants to merge 1 commit into
ji/segment-io-protocolfrom
ji/segment-io

Conversation

@joseph-isaacs

@joseph-isaacs joseph-isaacs commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Stacked on #10568, which adds the segment IO protocol (vortex_io::segments).

This PR moves the planner-driven V2 scan from the byte-range vortex_io::request protocol onto the new protocol, then removes request. It serves the protocol with FileSegmentIo, which reads through each file's existing v1 segment source.

V1 scans are unchanged. At runtime, V2 reads share the file's segment cache, request sharing and coalescing read driver, and no task or thread is added.

The branch is rebased onto develop's planner-chain V2 executor (#10706, #10707, #10708): prune and chunk planners, spawned chunks, and DataFusion and DuckDB drivers.

Changes

  • vortex-scan
    • Planner and Morsel drop the IoConsumer supertrait. They publish NeedsIO(Vec<IoRequest>) and receive deliver(key, result) once per successful fetch.
    • Run and Driver submit each root's requests to that root's SegmentIoSession, and hold the handles.
    • Each item's fetches finish through the item's own FuturesUnordered, so a poll_ready report yields exactly the fetches that finished.
    • Fetches in flight are counted per session rather than per root, because spawned roots share a session.
    • Run::poll_io replaces Run::complete.
    • The scripted test harness is a recording SegmentIoSession.
  • vortex-layout
    • ScanAdaptor fetches each segment once by IoKey::segment while any scan read of it is outstanding, and hands the result to every waiting read.
    • Prune and filter hints name segment keys.
    • ScanFile and PreparedScan carry the file's RegisteredSource, and PreparedScan::io returns its service.
    • SegmentScanIo is removed, along with the byte-range index that ScanCache kept for it.
    • The tests' IO service reads each segment once per session and never reads an announcement, like a file's coalescing driver.
  • vortex-file: segment_io::FileSegmentIo, one per VortexSession.
    • Size requests are answered at submit.
    • Segment requests call SegmentSource::request.
    • VortexFile::scan_file registers the file. A caching file keeps one registration in its scan cache, and with_segment_source renews it.
    • The V2 scan driver reads through SegmentIoSession.
  • vortex-datafusion, vortex-duckdb
    • Their V2 drivers read through SegmentIoSession.
    • The DataFusion opener shares a file's segment source across partitions through VortexFile::with_segment_source, and still shares its ScanCache.
  • vortex-io: request is removed.

Performance

Measured in CI with manually dispatched PR SQL Benchmarks runs on dedicated runners, comparing against the base commit, three runs per side. Numbers are for DataFusion on Vortex (the V2 scan); DuckDB on Vortex is unaffected and serves as a control.

Benchmark This PR / base (median of 3) Base vs itself across runs DuckDB control
TPC-H SF1 NVMe 1.001× 0.997–1.004 1.005×
TPC-H SF10 NVMe 0.992× 0.997–0.999 1.001×
TPC-H SF1 S3 0.925× 0.877–1.021 1.000×

The result is neutral on NVMe, with about 1% gained on SF10. S3 is within its own run-to-run noise. Locally (TPC-H SF1, 22 queries), physical reads drop 14% against the old IO (1143 → 984).

Checks

  • cargo clippy -p vortex-io -p vortex-scan -p vortex-layout -p vortex-file -p vortex-datafusion --all-targets --all-features -- -D warnings
  • cargo clippy -p compress-bench -p vortex-bench -p datafusion-bench --all-targets -- -D warnings
  • cargo +nightly-2026-09-10 fmt on those crates and vortex-duckdb
  • RUSTDOCFLAGS="-D warnings --cfg docsrs" cargo doc --no-deps -p vortex-io -p vortex-scan -p vortex-layout -p vortex-file --all-features
  • cargo test --lib: vortex-io (109), vortex-scan (59), vortex-layout (664), vortex-file (197), vortex-datafusion (778)
  • typos

Not run locally: vortex-duckdb doesn't build here because the DuckDB source download is blocked. Its change is the same deliver swap as the other planners, and CI builds it.

API Changes

  • vortex_io::request is removed.
  • Planner and Morsel drop IoConsumer and gain deliver(IoKey, SourceResult). NeedsIO carries Vec<IoRequest>.
  • ScanFile.segments is replaced by ScanFile.segment_io: RegisteredSource.
  • PreparedScan::io returns Arc<dyn SegmentIoService>.
  • New: vortex_file::segment_io::FileSegmentIo.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N4PmceDHg8rFe5bgkhGFU9

@codspeed

codspeed Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 7.83%

4 warnings, 1 note

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ Unable to measure the execution of 2 benchmarks

These benchmarks measured no execution time, so their results are not comparable and count as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

⚠️ 23 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

ℹ️ 40 benchmarks have no flamegraph

The flamegraph wasn't generated: the symbols of the profiling trace could not be resolved, because no information about the language frames was collected.

See affected benchmarks

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 2436 untouched benchmarks
⏩ 12 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ CPU Simulation chunked_dict_primitive_canonical_into[f64, (1000, 1000, 10)] 146.6 µs 200.8 µs -26.98%
❌ CPU Simulation test_scan_filter[int] 3 ms 3.4 ms -11.81%
⚡ CPU Simulation test_repeated_scan_scalar_at[int_float] 436.5 µs 358.9 µs +21.61%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing ji/segment-io (054036d) with ji/segment-io-protocol (75bbf20)

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@joseph-isaacs joseph-isaacs changed the title feat(io): a segment-keyed IO system for planner-driven scans feat(file): serve the segment IO protocol through files' segment sources Oct 16, 2026
@joseph-isaacs
joseph-isaacs changed the base branch from develop to ji/segment-io-protocol October 16, 2026 16:45
@joseph-isaacs
joseph-isaacs force-pushed the ji/segment-io-protocol branch from 24ef18f to 8e2defb Compare October 23, 2026 23:56
@joseph-isaacs joseph-isaacs changed the title feat(file): serve the segment IO protocol through files' segment sources feat(scan): run the V2 executor on the segment IO protocol Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

CodSpeed Performance Analysis flags regressions on a5654f2. I don't think this PR causes them:

  • Which benchmarks: vortex-array's chunked_dict_primitive_canonical_into (CPU simulation) and FastLanes bit-packing walltime benchmarks (bitpack_blocked_compress_avx2, …).
  • What this PR changes: only the planner-driven scan's IO (vortex-io, vortex-scan, the V2 executor in vortex-layout, vortex-file's FileSegmentIo, and the DataFusion V2 opener). None of those benchmarks runs that code, and no array or encoding code changes.
  • Measurement: CodSpeed itself warns that base and head ran on different runner types, and that the walltime results ran outside its Macro Runners.
  • The same benchmark family on feat(io): a segment-keyed IO protocol for planner-driven scans #10568: the FastLanes walltime benchmarks also moved there, and that PR only adds an unused module.

No fix is needed, and I can't re-run CodSpeed from here. #10569 runs the same tree, so this applies to its CodSpeed report too.


Generated by Claude Code

Switches the planner-driven scan from the byte-range `vortex_io::request`
protocol to `vortex_io::segments`, and serves it with a session-wide
`FileSegmentIo` that reads through files' v1 segment sources.

- vortex-scan: `Planner` and `Morsel` drop the `IoConsumer` supertrait,
  publish `NeedsIO(Vec<IoRequest>)`, and receive `deliver(key, result)` once
  per successful fetch. `Run`/`Driver` submit each root's requests to its
  `SegmentIoSession` and hold the handles; each item's fetches finish through
  its own `FuturesUnordered`, so a `poll_ready` report yields exactly the
  finished fetches. Fetches in flight are counted per session, since spawned
  roots share one. `Run::poll_io` replaces `Run::complete`. The scripted
  harness is a recording `SegmentIoSession`.
- vortex-layout: `ScanAdaptor` fetches each segment once by `IoKey::segment`
  while any scan read of it is outstanding, and hands the result to every
  waiting read. Prune and filter hints name segment keys. `ScanFile` and
  `PreparedScan` carry the file's `RegisteredSource`, and `PreparedScan::io`
  is its service. `SegmentScanIo` and the cached byte-range index it needed
  are removed.
- vortex-file: `FileSegmentIo`, one per `VortexSession`. Size requests are
  answered at submit; segment requests call `SegmentSource::request`, so they
  share the file's segment cache, request sharing and coalescing read driver.
  `VortexFile::scan_file` registers the file's segment source, and a caching
  file keeps one registration in its scan cache, renewed with its segment
  source. The V2 scan driver reads through `SegmentIoSession`.
- vortex-datafusion, vortex-duckdb: their V2 drivers read through
  `SegmentIoSession`; the DataFusion opener shares a file's segment source
  across partitions through `VortexFile::with_segment_source`.
- vortex-io: `request` is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4PmceDHg8rFe5bgkhGFU9
Signed-off-by: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
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