zhongyujiang opened a new pull request, #8840:
URL: https://github.com/apache/paimon/pull/8840

   ## Problem
   
   Wide tables (thousands of columns) throw a cryptic 
`NegativeArraySizeException` during compaction / sort spill:
   
   ```
   java.lang.NegativeArraySizeException
     at 
org.apache.paimon.memory.MemorySegmentUtils.getBytes(MemorySegmentUtils.java:186)
     at org.apache.paimon.data.BinarySection.toBytes(BinarySection.java:101)
     at 
org.apache.paimon.format.parquet.writer.ParquetRowDataWriter$StringWriter.writeString(ParquetRowDataWriter.java:268)
   ```
   
   ## Root Cause
   
   `BinaryRow`'s fixed-length part (`nullBitsSizeInBytes + 8 * arity`) must 
reside within a single `MemorySegment`, because all field accessors 
(`getString`, `getLong`, `getArray`, etc.) read from `segments[0]` via 
`UNSAFE.getLong` **without bounds checking**.
   
   The default `page-size` is 64 KB, which supports up to **8064 columns** 
(fixed part = 65528 bytes). When a table exceeds this limit (e.g. 8347 columns 
→ fixed part = 67824 bytes), the row silently spans segments during paged 
sort/spill operations. `segments[0]` is only 64 KB, so field offsets beyond 64 
KB read out-of-bounds heap memory via `UNSAFE`. The garbage `long` is 
interpreted as a string length; when negative, `new byte[negative]` throws 
`NegativeArraySizeException`.
   
   `BinaryRowSerializer.checkSkipWriteForFixLengthPart` only `advance()`s to 
the next page when the current page lacks space — it never validates that the 
fixed part fits in a single page, and assumes `advance()` will always help 
(which it can't when `fixedPart > pageSize`).
   
   ## Fix
   
   Add a `checkArgument` guard at the top of `checkSkipWriteForFixLengthPart` — 
the choke point for all paged writes (`BinaryInMemorySortBuffer`, 
`InMemoryBuffer`, `BytesHashMap`, `SimpleObjectsCache`, 
`InternalRowSerializer`). When `getSerializedRowFixedPartLength() > 
segmentSize`, throw a clear `IllegalArgumentException` at the first write, 
advising to increase `page-size`.
   
   This converts the cryptic `NegativeArraySizeException` (which may surface 
far from the root cause, or not surface at all for INT-only schemas — silent 
data corruption) into an immediate, actionable error.
   
   ## Testing
   
   - `BinaryRowSerializerPageSizeGuardTest`: 200 INT columns (fixed part 1636 
bytes), verifies the guard throws with a clear message at `page-size=1024` and 
succeeds at `page-size=4096` with round-trip.
   - `WriterOperatorTest`: bumped `page-size` from 32 b to 64 b in 3 test 
methods whose KeyValue combined rows (52/60 bytes fixed part) previously 
exceeded 32 b and relied on silent corruption.
   
   ## Workaround (for existing tables)
   
   ```sql
   ALTER TABLE <table> SET TBLPROPERTIES ('page-size' = '256kb');
   ```
   
   `page-size` must be larger than the row's fixed-length part (`≈ 8.125 * 
num_columns` bytes).


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

Reply via email to