comphead opened a new pull request, #6900:
URL: https://github.com/apache/datafusion-comet/pull/6900

   ## Which issue does this PR close?
   
   Closes #6779.
   
   ## Rationale for this change
   
   A Comet broadcast can return wrong values, or fail with 
`OversizedAllocationException`, when its
   build side emits sliced batches with string, binary, array or map columns, 
at any nesting level.
   
   arrow-rs slices these arrays by moving the start of their offsets, so the 
JVM imports a slice
   whose offsets start past zero and whose data buffer still begins with the 
rows before the slice.
   `zero_offsets` (#6339) only handles `ArrayData::offset`, which arrow-rs 
keeps for booleans.
   DataFusion produces such slices whenever an operator emits one batch in 
`batch_size` pieces.
   Grouped aggregates do this for every `GROUP BY` and `DISTINCT`, and so do 
`LIMIT ... OFFSET`,
   TopK and window aggregates.
   
   `Utils.serializeBatches` writes these vectors with Arrow Java's 
`VectorUnloader`, which keeps the
   offsets as they are and writes the data buffer from byte 0. 
`coalesceBroadcastBatches` (#3703)
   then appends the decoded batches with `VectorSchemaRootAppender`. In Arrow 
Java 18.3 that assumes
   offsets start at zero, so the first value of every slice absorbs all the 
bytes before it. The
   issue's query returns 199976 rows instead of 200000, and the appended data 
grows with every
   slice, which is where the `OversizedAllocationException` comes from. Every 
release since 0.15.0
   has this.
   
   Arrow Java fixed `VectorAppender` in apache/arrow-java#1180, but no release 
includes it yet.
   19.0.0 is the latest.
   
   ## What changes are included in this PR?
   
   - `serializeBatches` checks each column, children included, for an offset 
buffer that starts past
     zero. When one does, it writes `root.slice(0, rowCount)` instead of the 
root. The slice rebases
     those offsets to zero and drops the bytes before them, and shares every 
other buffer. Batches
     whose offsets already start at zero are written as before.
   - The bytes before a slice are no longer written. Before this change, each 
slice carried its
     buffer from the start up to its own end, so the bytes collected to the 
driver grew with the
     square of the number of slices per partition.
   - A comment at the `VectorSchemaRootAppender.append` call notes that 
coalescing relies on this.
   
   The in-memory cache serializer (`CachedBatchIpc.serialize`) also writes the 
bytes before the first
   offset. There they only inflate the payload and its size statistics, and the 
values read back
   correctly, so this PR leaves it unchanged.
   
   ## How are these changes tested?
   
   - `CometJoinSuite` "Broadcast coalescing keeps the values of sliced batches" 
broadcasts
     `SELECT DISTINCT *` over string, binary, `array<string>` and struct 
columns with nulls at batch
     size 100. It compares every value with Spark and checks that the broadcast 
still coalesced.
   - `UtilsSuite` "serializeBatches rebases offsets that start past zero" 
coalesces string vectors
     shaped like imported slices and checks the values. It also checks that 
each slice takes as many
     bytes as the same values in a vector of their own.
   
   Both tests fail without the fix. Locally, on Spark 4.1 against a build of 
recent `main`, the new
   tests, the issue's reproducer, the rest of `UtilsSuite` and 25 existing 
broadcast tests in
   `CometJoinSuite`, `CometExecSuite` and `CometInMemoryCacheSuite` pass. The 
Spark 3.x profiles were
   not run locally.
   


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