srielau commented on code in PR #58549:
URL: https://github.com/apache/spark/pull/58549#discussion_r4051344920


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/python/ColumnarArrowEvalPythonEvaluatorFactory.scala:
##########
@@ -190,10 +216,22 @@ private[python] class 
ColumnarArrowEvalPythonEvaluatorFactory(
 
       val passThruQueue =
         new ArrayDeque[(Array[ColumnVector], Int)]()

Review Comment:
   With pipelined execution enabled, `bufferedIter` adds entries from the 
writer thread while `resultIter` peeks and polls them from the task thread. 
`ArrayDeque` provides no synchronization or memory publication, unlike the 
`HybridRowQueue` used by paths 2 and 3, so this can observe an empty or stale 
queue. Please use a thread-safe SPSC/concurrent queue and add Arrow-columnar 
pipelined coverage.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/python/ColumnarArrowEvalPythonEvaluatorFactory.scala:
##########
@@ -190,10 +216,22 @@ private[python] class 
ColumnarArrowEvalPythonEvaluatorFactory(
 
       val passThruQueue =
         new ArrayDeque[(Array[ColumnVector], Int)]()
+      context.addTaskCompletionListener[Unit] { _ =>
+        while (!passThruQueue.isEmpty) {
+          passThruQueue.poll()._1.foreach(_.close())
+        }
+      }
 
       val bufferedIter = inputIter.map { batch =>
-        val passThruCols = childOutput.indices.map(
-          i => batch.column(i)).toArray
+        // The input reader owns and may close its vectors as soon as the 
Python runner consumes

Review Comment:
   This new lifetime explanation contradicts the existing Path 1 Scaladoc, 
which still says raw `ColumnVector` references are safe because batches 
allocate independently. The implementation now creates transferred views 
specifically because the reader can close the originals first. Please update 
both the class-level and method-level descriptions.



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/python/ColumnarArrowEvalPythonEvaluatorFactory.scala:
##########
@@ -190,10 +216,22 @@ private[python] class 
ColumnarArrowEvalPythonEvaluatorFactory(
 
       val passThruQueue =
         new ArrayDeque[(Array[ColumnVector], Int)]()
+      context.addTaskCompletionListener[Unit] { _ =>
+        while (!passThruQueue.isEmpty) {
+          passThruQueue.poll()._1.foreach(_.close())
+        }
+      }
 
       val bufferedIter = inputIter.map { batch =>
-        val passThruCols = childOutput.indices.map(
-          i => batch.column(i)).toArray
+        // The input reader owns and may close its vectors as soon as the 
Python runner consumes
+        // the input iterator. Create independent vector views whose buffers 
remain valid until
+        // the corresponding output batch is closed.
+        val passThruCols = childOutput.indices.map { i =>
+          val vector = 
batch.column(i).asInstanceOf[ArrowColumnVector].getValueVector

Review Comment:
   The optimized-path gate checks only that column 0 is Arrow-backed, but this 
loop now casts every child column. A legal mixed `ColumnarBatch`, such as an 
Arrow-backed scan with an appended constant or metadata vector, enters this 
path and then fails with `ClassCastException`. Require every pass-through 
column to be an `ArrowColumnVector` before choosing path 1; otherwise use the 
row-queue path.



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