sunchao commented on code in PR #3726:
URL: https://github.com/apache/parquet-java/pull/3726#discussion_r4178558095


##########
parquet-hadoop/src/main/java/org/apache/parquet/hadoop/ParquetFileReader.java:
##########
@@ -1358,32 +1355,150 @@ private boolean 
arePartsValidForVectoredIo(List<ConsecutivePartList> allParts) {
    * If directly implemented by a Filesystem then it is likely to be a more 
efficient
    * operation such as a scatter-gather read (native IO) or set of parallel
    * GET requests against an object store.
+   * Submission and all requested ranges share the vectored-read timeout. 
Failed
+   * operations invalidate the stream; cleanup waits for submission to exit 
before
+   * closing it, even if the backend does not respond promptly to interruption.
+   * The allocation limit bounds requested range lengths. Filesystems may 
merge or
+   * checksum-align ranges and allocate larger buffers; decoders may also 
require
+   * larger contiguous buffers. This is not a total memory limit.
    * @param allParts all parts to be read.
    * @param builder used to build chunk list to read the pages for the 
different columns.
-   * @throws IOException any IOE.
-   * @throws IllegalArgumentException arguments are invalid.
-   * @throws UnsupportedOperationException if the filesystem does not support 
vectored IO.
+   * @throws IOException if submitting or consuming the vectored reads fails.
+   * @throws IllegalArgumentException if range preparation fails before any 
reads are submitted.
    */
   private void readVectored(List<ConsecutivePartList> allParts, 
ChunkListBuilder builder) throws IOException {
-
+    final int maximumAllocation = options.getMaxAllocationSize();
+    Preconditions.checkArgument(maximumAllocation > 0, "Invalid maximum 
allocation size %s", maximumAllocation);
+    final long fileLength = vectoredReadFileLength;
     List<ParquetFileRange> ranges = new ArrayList<>(allParts.size());
+    List<Integer> partRangeCounts = new ArrayList<>(allParts.size());
     long totalSize = 0;
     for (ConsecutivePartList consecutiveChunks : allParts) {
       final long len = consecutiveChunks.length;
-      Preconditions.checkArgument(
-          len < Integer.MAX_VALUE,
-          "Invalid length %s for vectored read operation. It must be less than 
max integer value.",
-          len);
-      ranges.add(new ParquetFileRange(consecutiveChunks.offset, (int) len));
+      final long start = consecutiveChunks.offset;
+      if (start < 0 || len < 0 || start > fileLength || len > fileLength - 
start) {
+        throw new IOException(String.format(
+            "Invalid vectored read range (offset %d, length %d) for file 
length %d",
+            start, len, fileLength));
+      }
+      final int firstRange = ranges.size();
+      long remaining = len;
+      long offset = start;
+      do {
+        int rangeLength = (int) Math.min(remaining, maximumAllocation);
+        ranges.add(new ParquetFileRange(offset, rangeLength));
+        offset += rangeLength;
+        remaining -= rangeLength;
+      } while (remaining > 0);
+      partRangeCounts.add(ranges.size() - firstRange);
       totalSize += len;
     }
     LOG.debug("Reading {} bytes of data with vectored IO in {} ranges", 
totalSize, ranges.size());
-    // Request a vectored read;
-    f.readVectored(ranges, options.getAllocator());
-    int k = 0;
-    for (ConsecutivePartList consecutivePart : allParts) {
-      ParquetFileRange currRange = ranges.get(k++);
-      consecutivePart.readFromVectoredRange(currRange, builder);
+    if (vectoredReadExecutor == null) {
+      vectoredReadExecutor = VectoredReadOperation.newExecutor();

Review Comment:
   Agreed: idle expiry did not bound workers held by blocked submissions, and 
retries could accumulate them. Fixed in 
[8ea4302](https://github.com/apache/parquet-java/pull/3726/commits/8ea4302a61254de697f66ef20e5edc75729bbf69)
 with a shared bounded executor and deadline-aware admission. The default is 64 
workers, configurable before first use through the JVM system property 
`parquet.hadoop.vectored.io.threads`.
   
   A worker retains capacity until the backend call actually exits, the caller 
transfers or abandons buffer ownership, and failure cleanup finishes. Cleanup 
runs on that same worker after submission, so cancellation cannot release 
capacity early or let cleanup overtake the backend. Admission shares the 
existing 300-second deadline; if capacity is unavailable, the read fails before 
submission and its stream remains reader-owned. Readers no longer shut down the 
shared executor.
   
   Tests cover two blocked submissions followed by repeated admission timeouts 
without extra backend calls/workers, recovery after real completion, blocked 
stream close, interruption before admission with reader reuse, and abort before 
the worker starts. The 110-test Hadoop 3.4.1 run also passed with the 
production shared pool capped at two workers; full default-Hadoop module passed 
869 tests with 26 capability skips. The README documents saturation behavior 
and the limits: stuck calls can consume capacity indefinitely, and 
filesystem-owned threads/total memory are outside this cap. No live S3 or 
throughput validation was performed.



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