avantgardnerio opened a new pull request, #2204:
URL: https://github.com/apache/datafusion-ballista/pull/2204

   ## Summary
   
   Adds a value-range index (`.value.idx`) sidecar next to each 
passthrough-shuffle output file, so a downstream reader with an assigned value 
cut range can seek into the data file without full-file read amplification. Six 
commits:
   
   1. `ValueIndexExec` — passthrough tap resolving ORDER BY from 
`input.output_ordering()` at construction. Refuses unordered input at `try_new`.
   2. Writer path — accumulates `(sampled_row, ORDER BY values)` per non-empty 
batch, flushes an Arrow IPC file on EOS at 
`{work_dir}/{job_id}/{stage_id}/{partition}/data-{task_id}.value.idx`. 
Constructor mirrors `ShuffleWriterExec::try_new` so the scheduler/executor 
stamps both from the same identifying inputs.
   3. Rename pass — `first_row` → `sampled_row` throughout; also generalizes 
`sample_first_row` → `sample_row(batch, order_by, row_idx: usize)` so stride 
sampling drops in without a rename churn.
   4. `ValueIndexReader` — opens the file, validates schema envelope (version, 
row-index scheme, `sampled_row` column shape), exposes `total_row_count`, 
`num_leaves`, `sampled_rows`, `leaf_batch`, `leaf_row_range` (with 
`total_row_count` sentinel for the last leaf).
   5. Binary search — `binary_search_by_value(target, sort_options)` via 
`RowConverter` (composite / arbitrary Arrow-comparable / asc/desc/nulls-first 
all by construction) and `binary_search_by_sampled_row(target: u64)` (utility 
for ROWS-frame halo).
   6. **Format switch: passthrough shuffle now writes Arrow IPC file format 
instead of stream format.** `FileReader::set_index` + footer block index is 
what makes the value-index reader's `sampled_row → byte range` translation 
O(1). See tradeoff section below.
   
   ## File-format agnostic by design
   
   The sidecar itself only stores `(sampled_row, ORDER BY values)` — no byte 
offsets. Byte-range resolution at read time is delegated to the data file's own 
format-native chunk metadata. The tap sees only `RecordBatch`; the reader 
translation step is format-specific.
   
   **Requirement on the data file's format:** a footer (or equivalent index at 
a known location) we can read once to enumerate batch/row-group boundaries, 
plus per-chunk row counts. Every serious columnar format has this.
   
   Concretely:
   
   - **Arrow IPC file** — footer's `Block { offset, metadata_length, 
body_length }` + each RecordBatch message's row count in its metadata. What 
this PR implements.
   - **Vortex** — chunk index in the file layout, exposed by the Vortex reader. 
`ValueIndexReader` doesn't need to know the format; only whichever code sits 
between it and the data file does. When Vortex-format shuffle lands (Spice has 
this in their private Ballista fork per Phillip LeBlanc's DataFusion Community 
Showcase Vol. 2 talk 2026-07), the tap and sidecar reader are unchanged — a 
Vortex-specific `sampled_row → byte range` resolver plugs in alongside the 
Arrow IPC one.
   - **Parquet, ORC** — row-group index in the footer. Same shape.
   
   So `ValueIndexExec` writing side is fully format-agnostic (it evaluates the 
ORDER BY expression, records rows). The read-side is a small format-specific 
translator on top of `ValueIndexReader::leaf_row_range`. This PR ships the 
Arrow IPC translator via `FileReader::set_index`.
   
   ## Wins
   
   - **Enables range-GET reads on object-store-backed shuffle.** Consumer 
downloads a KB-scale index, binary-searches its assigned value cut range, then 
range-GETs just the matching batch. Previously impossible on Stream format.
   - Sidecar design ports to any columnar format with a chunk index (Arrow IPC 
file today, Vortex when it lands upstream, Parquet/ORC if ever relevant).
   - No API change downstream. Same `RecordBatch`es come out of the reader. 
Sort-shuffle path is untouched.
   - New surface is off by default — nothing constructs `ValueIndexExec` yet. 
Planner rule to insert it will follow as a separate PR.
   
   ## Regressions (honest)
   
   - **Full-file remote reads pay ~2 small extra range-GETs.** FileReader's 
footer-open sequence is: read 10-byte tail marker → read footer flatbuffer → 
then batches. StreamReader was one sequential pass. On S3 that's 2 KB-scale 
GETs of overhead per shuffle file — a rounding error against typical MB-scale 
batch bodies, but real. Local reads (kernel page cache) are unaffected.
   - Sort-shuffle path is unchanged — its own hand-rolled `.arrow.index` offset 
table + `StreamWriter` on the consolidated `data.arrow` continues to work as 
before. The tradeoff above applies only to the passthrough shuffle path.
   - Not strictly "purely additive" because of the format switch, but the 
switch is small, motivated by the concrete downstream consumer landing in the 
same PR, and touches only shuffle write + local/Flight readers.
   
   ## Test plan
   
   - [x] `cargo test -p ballista-core --lib` (222 pass, includes 9 new 
`value_index` tests)
   - [x] `cargo test -p ballista-executor --lib` (53 pass — flight_service 
still works with FileReader)
   - [x] `cargo clippy --all-targets -p ballista-core -p ballista-executor` 
clean
   - [x] `cargo fmt --all`
   - [x] New e2e test `range_download_via_sidecar_and_footer` runs the full 
pipeline (Datasource → Sort → RuntimeStats → ORRE → ValueIndexExec → 
ShuffleWriter) and demonstrates: `binary_search_by_value(target)` → leaf → 
`FileReader::set_index(leaf_idx)` → batch containing target value. Also asserts 
the current sampling invariant `leaf_idx == batch_idx` so a future 
stride-sampling change trips this test rather than silently producing wrong 
reads.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


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