viirya commented on code in PR #5560:
URL: https://github.com/apache/datafusion-comet/pull/5560#discussion_r3991579134


##########
.github/workflows/pyarrow_udf_test.yml:
##########
@@ -28,23 +28,29 @@ on:
     paths: &feature-paths
       - "pom.xml"
       - "common/pom.xml"
-      - "common/src/main/scala/org/apache/comet/CometConf.scala"
+      - "native/shuffle/src/spark_unsafe/row.rs"
       - "spark/pom.xml"
+      - "spark/src/main/java/org/apache/comet/vector/**"
+      - 
"spark/src/main/java/org/apache/spark/sql/comet/execution/shuffle/SpillWriter.java"
+      - "spark/src/main/scala/org/apache/comet/CometConf.scala"
       - 
"spark/src/main/scala/org/apache/comet/rules/EliminateRedundantTransitions.scala"
+      - "spark/src/main/scala/org/apache/comet/vector/NativeUtil.scala"
+      - "spark/src/main/scala/org/apache/comet/vector/StreamReader.scala"

Review Comment:
   **[P3] Include the new shared decoding helper in the workflow paths**
   
   Now that the runner delegates decoding and cleanup to 
`spark/src/main/scala/org/apache/comet/vector/CometVectorUtils.scala`, could we 
add that file to this list, or cover the Scala vector directory?
   
   The Java vector glob above does not match it, and the Scala entries 
currently list only `NativeUtil.scala` and `StreamReader.scala`. A future 
change confined to the shared decoding helper would therefore skip these Python 
worker regressions. This PR itself still triggers them through its other 
changed files.



##########
spark/src/main/spark-4.x/org/apache/spark/sql/execution/python/CometArrowPythonRunnerBase.scala:
##########
@@ -178,44 +182,49 @@ private[python] trait CometArrowPythonRunnerBase
 
         val cometBatch = currentGroup.next()
         val startData = dataOut.size()
-        val sourceVectors = (0 until cometBatch.numCols()).map { i =>
-          cometBatch
-            .column(i)
-            .asInstanceOf[CometDecodedVector]
-            .getValueVector
-            .asInstanceOf[FieldVector]
+        val columns = (0 until cometBatch.numCols()).map { i =>
+          cometBatch.column(i).asInstanceOf[CometDecodedVector]
         }
-        val batchFields = sourceVectors.map(_.getField)
-
-        if (arrowWriter == null) {
-          // Build the schema-only struct root once from the first batch's 
child fields.
-          // mapInArrow/mapInPandas exchange the columns under a single 
non-nullable struct.
-          // Comet's FFI-imported vectors leave the Arrow Field name null, so 
restore the real
-          // column names from the input schema (the worker reads columns by 
name, and shaded
-          // Arrow rejects a null field name). Keep the field types and child 
structure as-is so
-          // the advertised schema matches the source buffers. Keeping the 
type as-is also means
-          // a TimestampType reaches the worker with Comet's UTC time zone
-          // rather than the session zone vanilla Spark would label it with; 
this is a documented
-          // limitation (see pyarrow-udfs.md), not a value difference, since 
the stored instant is
-          // identical.
-          val childNames = inputStructType.fieldNames
-          streamFields = batchFields.zipWithIndex.map { case (field, i) =>
-            renamed(field, childNames(i), forceNullable = true)
+        CometArrowPythonRunnerBase.foreachInputBatch(
+          columns,
+          cometBatch.numRows(),
+          arrowMaxRecordsPerBatch,
+          arrowMaxBytesPerBatch,
+          allocator) { (sourceVectors, numRows) =>

Review Comment:
   **[P1] Return to Spark between slices so the transport buffer can drain**
   
   `foreachInputBatch` serializes every slice before this 
`writeNextInputToStream` call returns. In Spark 4.1, `dataOut` wraps a 
`DirectByteBufferOutputStream`; 
`ReaderInputStream.writeAdditionalInputToPythonWorker` only writes that buffer 
to the worker socket after this call returns ([Spark 
source](https://github.com/apache/spark/blob/v4.1.3/core/src/main/scala/org/apache/spark/api/python/PythonRunner.scala#L861)).
 Consequently, closing each decoded vector does not release the accumulated IPC 
bytes.
   
   I reproduced this with 256 rows referencing one 64 KiB dictionary value and 
`maxRecordsPerBatch=1`. The source buffers occupied 83,968 bytes and the 
decoding allocator peaked at 114,688 bytes, but the transport buffer 
accumulated 16,832,512 bytes. With `-XX:MaxDirectMemorySize=8m`, this failed in 
`DirectByteBufferOutputStream.grow`; a control that drained/reset the buffer 
between slices passed, with at most 65,752 pending IPC bytes.
   
   Could we retain the current source batch and remaining ranges in the writer, 
emit one slice per `writeNextInputToStream` call, and return so Spark can drain 
the transport buffer? The source must remain alive until all its slices have 
been consumed. Calling `flush()` on this stream would not drain it to the 
socket.
   
   Please also add a regression covering transport buffering. The existing 
allocator-capped test proves that decoding is bounded, but does not detect 
accumulation outside the Arrow allocator.



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