Reader: concurrent scan with a confined arena per worker thread

Open
#505 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
12/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
java
Domain
data, performance

Research direction

This is a design proposal, so the first step is deciding the open questions, not editing code. Read ScanIterator, Chunk and the existing confined-versus-shared arena handling, plus ADR 0003 for the exception contract on worker threads. Done means a recorded decision on shared versus per-worker mapping and ordered versus unordered delivery, then an A/B run with RealFileScanBenchmark.

Written by the indexing model from the issue text.

Description

enhancement perf

Parked design for a concurrent reader scan. Follow-up to #474 (writer side, shipped).

Reference behavior

  • Rust (vortex-layout/src/scan/scan_builder.rs): the scan is split into row-range tasks (SplitBy), each spawned onto the session runtime, up to concurrency * num_workers in flight (default 4 per worker). ordered (default) uses .buffered(n), otherwise .buffer_unordered(n). A per-split map function runs on the spawned task.
  • .NET (Vorticity): MaxDegreeOfParallelism per session or per scan, MaxConcurrentReads for I/O, shared SegmentCache, concurrent scans over one open file.
  • vortex-java today: ScanIterator is single-threaded, every arena is ofConfined.

Proposed shape

A confined Arena per worker thread, so the callback runs on the worker and the caller never touches decoded memory from another thread:

scan.parallel(executor)            // executor + bounded in-flight window on ScanOptions
    .map(chunk -> r)               // runs on the worker; chunk and its arena close on return
  • R must not reference chunk memory (aggregates, copied-out values); Javadoc states the contract.
  • Results delivered ordered (Rust buffered, default) or unordered (buffer_unordered).
  • The file mapping becomes ofShared (one close at the end), since workers must read it. Alternative: each worker maps its own view.
  • scan() / ScanIterator / Chunk lifetime rules stay unchanged.

Pros

  • Rust-shaped (map on the spawned task, ordered/unordered, bounded window), so parity with the reference.
  • Confined arenas keep the cheap alloc/close path; Arena.ofShared().close() needs a cross-thread handshake per chunk.
  • No change to the existing single-threaded API or its safety checks.
  • Same bounded-window idea as the writer's pending queue / MAX_IN_FLIGHT_SEGMENTS.
  • Overlaps decode with consumption; wide files and slow consumers benefit most.

Cons

  • No ordered, parallel, zero-copy Iterator<Chunk> for a consumer thread: a chunk decoded on a worker cannot be read elsewhere. That case would need a shared per-chunk arena (slower close, and relaxes the "second next() while a Chunk is open is rejected" rule).
  • Callback API is less ergonomic than an iterator; callers who must keep data have to copy out.
  • Mapping arena goes shared; any cross-thread use after close() becomes a hazard to test (ADR 0003 security contract still applies on worker threads: only VortexException may escape).
  • Worker exceptions must be propagated and in-flight work cancelled/drained on failure (the writer's discardPending equivalent).
  • Needs A/B on all 7 real files (RealFileScanBenchmark, interleaved): memory-bandwidth-bound scans may not scale, and oversubscribing the pool gains nothing.

Out of scope / not planned

  • Per-column parallel decode within a chunk: not a Rust pattern, so it would be a deliberate divergence needing an explicit decision.
  • Parallel I/O for VortexHttpReader (concurrent range requests): independent of this, worth its own ticket if pursued.
  • Writer follow-ups (parallel scheme trials in CascadingCompressor.chooseBest): comment on #474.
Dominant language
Java
Stars
3
Forks
2
Avg merge
9h 19m
Merged PRs (30d)
154

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dfa1/vortex-java

All issues in dfa1/vortex-java

Similar issues

More Java issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.