Neuw84 commented on PR #17296:
URL: https://github.com/apache/iceberg/pull/17296#issuecomment-5106399967

   Have been tinkering a little bit more on that **0**.
   
   I went looking for where a zero batch size can come from, because the answer 
decides whether the
   `== 0` guard should exist at all. What I found:
   
   **A zero batch size is reachable, and only ever from an explicit caller.** 
There is no validation
   anywhere on the path:
   
   - `Parquet.ReadBuilder.recordsPerBatch(int)` - no check, default 10000.
   - `new ArrowReader(TableScan, int batchSize, boolean)` - public API, no 
check, passes straight through
     to `recordsPerBatch`.
   - Spark: `read.parquet.vectorization.batch-size` / the matching SQL conf, 
default
     `PARQUET_BATCH_SIZE_DEFAULT = 5000`, flows through 
`SparkReadConf.parquetBatchSize()` ->
     `ParquetBatchReadConf` -> `recordsPerBatch`. A user setting `0` is 
accepted.
   
   `DEFAULT_BATCH_SIZE` is also 5000, the same value as 
`PARQUET_BATCH_SIZE_DEFAULT`, so the guard reads
   like "treat 0 as unset and fall back to the default".
   
   **But a zero batch size cannot actually work end to end.** In
   `VectorizedParquetReader.FileIterator.next()`:
   
   ```java
   int numValuesToRead = (int) Math.min(nextRowGroupStart - valuesRead, 
batchSize);
   ...
   valuesRead += numValuesToRead;
   ```
   
   with `batchSize == 0` this is always `0`, `valuesRead` never advances and 
`hasNext()` stays true, so
   the iterator returns empty batches forever. The iterator uses its own 
`batchSize` from `ReadConf`, so
   the readers' `== 0` guard cannot rescue that; it only prevents zero-capacity 
allocations inside the
   readers.
   
   **What the guard does buy is avoiding a much worse failure, and that is 
where the ordering matters.**
   `NullabilityHolder.isNullAt` indexes a raw array with no bounds check:
   
   ```java
   public byte isNullAt(int index) {
     return isNull[index];
   }
   ```
   
   and both `ColumnVector` (arrow) and `IcebergArrowColumnVector` (Spark) call 
it through
   `holder.nullabilityHolder()`. With the old ordering, a zero batch size 
produced a `DEFAULT_BATCH_SIZE`
   sized vector paired with a zero length holder, so the vector reports 5000 
values while any null check
   on it throws `ArrayIndexOutOfBoundsException`. Resolving the size first 
keeps the two consistent.
   
   So my read is: the guard is worth keeping as defence in depth, the 
inconsistency was the real
   problem, and the actual fix for a user passing 0 belongs at the entry 
points. Two follow-ups I would
   suggest, both out of scope of this pull request ( as I see it):
   
   1. Reject a non-positive batch size in `Parquet.ReadBuilder.recordsPerBatch` 
and the `ArrowReader`
      constructor, since it can only produce an infinite stream of empty 
batches. That would let the
      `== 0` guards go away entirely.
   
   2. `VectorizedArrowReader.setBatchSize` (the parent, line 136) resolves the 
value into its own field
      but passes the raw argument to 
`vectorizedColumnIterator.setBatchSize(batchSize)`. Same
      inconsistency, one level up. I left it alone because changing what the 
column iterator receives is
      a behaviour change unrelated to this leak.


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