[common] Read a lone vectored range on the calling thread - #9920
wombatu-kun wants to merge 2 commits into
Conversation
JingsongLi
left a comment
There was a problem hiding this comment.
[P1] Keep the public vectored-read API asynchronous\n\nLocation: ; contract: .\n\n promises asynchronous reads, but the new single-range branch invokes on the caller thread. Any non-vector-index caller with one range now blocks unexpectedly; was a seek-safety switch, not a scheduling opt-in. Preserve the default asynchronous contract and add an explicit inline option used only by the vector reader that immediately joins its future.
JingsongLi
left a comment
There was a problem hiding this comment.
Correction — the previous review was garbled by command formatting.
[P1] Keep the public vectored-read API asynchronous
Location: paimon-common/src/main/java/org/apache/paimon/fs/VectoredReadUtils.java:79-82; contract: paimon-common/src/main/java/org/apache/paimon/fs/VectoredReadable.java:70-77.
VectoredReadable.readVectored promises asynchronous reads, but the new single-range branch invokes readSingleRange on the caller thread. Any non-vector-index caller with one range now blocks unexpectedly; sequentialReadFallback was a seek-safety switch, not a scheduling opt-in. Preserve the default asynchronous contract and add an explicit inline option used only by the vector reader that immediately joins its future.
|
Done 837e145 - gated on the new ReadOptions.withInlineSingleRange, which only the vector index reader sets; ReadOptions.from leaves it off. |
Purpose
readVectoredalready reads a single range inline, but only undersequentialReadFallback. That flag guardsfallbackToReadSequence, which callsSeekableInputStream.seek, so it really answers "may I move the stream position?". A caller that shares one stream across threads has to turn the flag off, and thereby also loses the inline path: a lone 4 KiB read is submitted toIO_THREAD_POOLthrough aBlockingExecutorand joined back. This adds an opt-in that reads such a range on the calling thread through the samereadSingleRangethe pool worker would have run. A single range never coalesced with anything and never reachedsplitBatches, so the I/O is identical and only the hand-off is gone.When the new branch is taken. Only on the new opt-in
ReadOptions.withInlineSingleRange(true), with exactly one range and an uninterrupted calling thread. The defaultReadOptions.from(readable)leaves it off, soreadVectoredkeeps its asynchronous contract for every existing caller, and Parquet and ORC are untouched.How often. The only caller that opts in is the vector index reader,
NativeVectorGlobalIndexReader.SeekableStreamVectorIndexInput, which joins every range immediately after the call, and single-range callbacks dominate its traffic: paimon-vindex reads the index header at open, streams resident sections chunk by chunk duringoptimizeForSearch(), preloads the DiskANN adjacency, and reads every probed IVF cluster payload with a one-rangepread. An IVF search withnprobe = 32pays 32 hand-offs per shard per query on top of the reads themselves.An interrupted caller keeps the executor path on purpose:
FileChannelis anAbstractInterruptibleChannel, so an inline read on an interrupted thread closes the channel and breaks the stream for every other reader sharing it, whileBlockingExecutor.submitfails fast onsemaphore.acquire()and leaves it intact.One 4 KiB range through
LocalFileIOover a 128 MiB page-cached file at random offsets, with the vector index reader's options, 20k warm-up plus 100k measured calls per JVM, median of 3 runs before and 8 after (JDK 8):What is saved is the hand-off, so it is the same number of microseconds on a slower device but a smaller share of the total.
Tests
VectoredReadUtilsTestcovers the opt-in from both sides: a single range is served on the calling thread with the option on, and on the executor under default options. The interrupt guard and the option copy chain are covered too, andSeekableStreamVectorIndexInputTestcovers the vector reader end to end.