wombatu-kun opened a new pull request, #9926:
URL: https://github.com/apache/paimon/pull/9926
### Purpose
`HadoopFileIO` hands out a stream that does not implement
`VectoredReadable`, so all ten `instanceof VectoredReadable` gates in the tree
take the fallback branch: `seek` plus a sequential read, four of them under a
`synchronized` block on the shared stream. `LocalFileIO` has implemented the
interface since #3369, `HadoopFileIO` never did.
That fallback is expensive because `HadoopSeekableInputStream.seek` turns a
forward gap of up to `MIN_SKIP_BYTES` (1 MiB) into `skipFully`, and no Hadoop
stream overrides `skip`, so `java.io.InputStream.skip` applies and that reads
into a scratch buffer and discards. A 4 KiB page 600 KiB ahead costs 604 KiB
off the wire.
`pread` now comes from Hadoop's `PositionedReadable`, which
`FSDataInputStream` is guaranteed to provide: its constructor rejects a stream
that is not both `Seekable` and `PositionedReadable`.
**When the new branch is taken, and how often.** At all ten gates, whenever
the stream came from `HadoopFileIO` on a non object store:
`CachingSeekableInputStream`, `BlockCache`, `FileBasedBloomFilter`,
`FMIndexFile`, `ParquetFileReader`, `RecordReaderUtils`, `BlockPrefetcher`,
`MosaicInputFileAdapter`, `VFSInputStream` and the vector index adapter. On
HDFS that is every Parquet and ORC read, every bloom filter probe and every
vector index search.
**Why the scheme is checked rather than the module.** `HadoopFileIO` is not
the `hdfs://` implementation, it is the universal Hadoop fallback in
`FileIO.get`, so object stores reach it too. A block file system implements a
real positional read (`DFSInputStream` has its own, unsynchronized, over its
own `BlockReader`); an object store does not, and inherits `FSInputStream`'s
emulation, `synchronized { seek(pos); read; finally seek(oldPos); }`. On OSS
that is two `reopen` calls per read. Object stores want Hadoop's own
`readVectored`, which does not exist before 3.3.5 while this project compiles
against 2.8.5; separate change.
Also fixes `VectoredReadUtils.readSingleRange`, which this change is what
makes reachable: it caught `Exception`, so an `Error` left the range's future
uncompleted and the submitted task's own `Future` was dropped. `FSError`
extends `Error` and `RawLocalFileSystem` raises it for any `IOException`, which
turned a read failure into a permanent block in ORC's untimed `get()` and the
vector index's `join()`.
### Benchmark
MiniDFSCluster, a beam of 32 scattered 4 KiB pages, 200 measured rounds per
run, median of 3 runs each, measured back to back:
| | before | after |
| --- | --- | --- |
| mean | 20.36 ms | 8.00 ms |
| p50 | 19.31 ms | 7.31 ms |
| p90 | 25.15 ms | 10.89 ms |
| p99 | 49.32 ms | 45.76 ms |
Bytes the stream is asked for over 200 scattered 4 KiB reads, counted at the
`FSDataInputStream` boundary:
| gap between reads | before | after |
| --- | --- | --- |
| 64 KiB | 13,045,760 | 819,200 |
| 256 KiB | 52,170,752 | 819,200 |
| 768 KiB | 156,504,064 | 819,200 |
### Tests
`FileIOBehaviorTestBase` had no read-path test at all; four were added,
covering the capability, `pread`'s return contract, that it leaves `getPos`
alone, and eight concurrent readers. They run for `LocalFileIO`, for
`HadoopFileIO` over a Hadoop local FS and for HDFS on `MiniDFSCluster`.
`HadoopFileIOVectoredReadTest` pins that `pread` reaches `PositionedReadable`
rather than `seek`, and the scheme gate. Reverting both production changes
fails 11 tests.
--
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]