This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 2b46053b5a [spark] Broadcast data evolution firstRowIdToPartitionMap 
(#8051)
2b46053b5a is described below

commit 2b46053b5acc4bcd7cb845a6bb1a5e18429c52b5
Author: Liurnly <[email protected]>
AuthorDate: Sun May 31 21:46:15 2026 +0800

    [spark] Broadcast data evolution firstRowIdToPartitionMap (#8051)
    
    `firstRowIdToPartitionMap` is used by data evolution MERGE INTO writes
    to locate the original partition and row count for each data file from
    its `firstRowId`. This mapping is built from scanned `DataSplit`s and
    then used by each Spark task while writing evolved rows.
    
    For merge plans that affect many files across many partitions, the map
    can become large. Capturing it directly in every task closure duplicates
    the serialized map for each task and can make Spark task serialization
    very large.
---
 .../spark/commands/DataEvolutionPaimonWriter.scala | 42 ++++++++++++----------
 .../spark/write/DataEvolutionTableDataWrite.scala  |  6 ++--
 2 files changed, 28 insertions(+), 20 deletions(-)

diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/DataEvolutionPaimonWriter.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/DataEvolutionPaimonWriter.scala
index 2b78333223..c49368d1bf 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/DataEvolutionPaimonWriter.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/DataEvolutionPaimonWriter.scala
@@ -19,7 +19,6 @@
 package org.apache.paimon.spark.commands
 
 import org.apache.paimon.CoreOptions
-import org.apache.paimon.data.BinaryRow
 import org.apache.paimon.format.blob.BlobFileFormat.isBlobFile
 import org.apache.paimon.spark.write.{DataEvolutionTableDataWrite, 
WriteHelper, WriteTaskResult}
 import org.apache.paimon.table.FileStoreTable
@@ -28,6 +27,7 @@ import org.apache.paimon.table.source.DataSplit
 import org.apache.paimon.types.DataType
 import org.apache.paimon.types.DataTypeRoot.BLOB
 import org.apache.paimon.types.VectorType.isVectorStoreFile
+import org.apache.paimon.utils.SerializationUtils
 
 import org.apache.spark.sql._
 
@@ -39,21 +39,6 @@ import scala.collection.mutable
 case class DataEvolutionPaimonWriter(paimonTable: FileStoreTable, dataSplits: 
Seq[DataSplit])
   extends WriteHelper {
 
-  private lazy val firstRowIdToPartitionMap: mutable.HashMap[Long, (BinaryRow, 
Long)] = {
-    val firstRowIdToPartitionMap = new mutable.HashMap[Long, (BinaryRow, Long)]
-    dataSplits.foreach(
-      split =>
-        split
-          .dataFiles()
-          .asScala
-          .filter(file => !isBlobFile(file.fileName()) && 
!isVectorStoreFile(file.fileName()))
-          .foreach(
-            file =>
-              firstRowIdToPartitionMap
-                .put(file.firstRowId(), (split.partition(), file.rowCount()))))
-    firstRowIdToPartitionMap
-  }
-
   // File rolling will never be performed
   override val table: FileStoreTable =
     
paimonTable.copy(Collections.singletonMap(CoreOptions.TARGET_FILE_SIZE.key(), 
"99999 G"))
@@ -77,14 +62,35 @@ case class DataEvolutionPaimonWriter(paimonTable: 
FileStoreTable, dataSplits: Se
           CoreOptions.BLOB_EXTERNAL_STORAGE_FIELD.key() + "') can be updated.")
     }
 
+    val firstRowIdToPartitionMap = new mutable.HashMap[Long, (Array[Byte], 
Long)]
+    dataSplits.foreach(
+      split =>
+        split
+          .dataFiles()
+          .asScala
+          .filter(file => !isBlobFile(file.fileName()) && 
!isVectorStoreFile(file.fileName()))
+          .foreach(
+            file =>
+              firstRowIdToPartitionMap
+                .put(
+                  file.firstRowId(),
+                  // BinaryRow stores data in transient memory segments and 
relies on Java
+                  // serialization hooks to restore them. Store bytes in Spark 
closures and
+                  // broadcasts so Kryo does not serialize BinaryRow internals 
directly.
+                  (SerializationUtils.serializeBinaryRow(split.partition()), 
file.rowCount())
+                )))
+    val firstRowIdToPartitionMapBroadcast =
+      sparkSession.sparkContext.broadcast(firstRowIdToPartitionMap)
+    val writeBuilder = table.newBatchWriteBuilder()
+
     val written =
       data.mapPartitions {
         iter =>
           {
             val write = DataEvolutionTableDataWrite(
-              table.newBatchWriteBuilder(),
+              writeBuilder,
               writeType,
-              firstRowIdToPartitionMap,
+              firstRowIdToPartitionMapBroadcast.value,
               catalogContextForBlobDescriptor)
             try {
               iter.foreach(row => write.write(row))
diff --git 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/write/DataEvolutionTableDataWrite.scala
 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/write/DataEvolutionTableDataWrite.scala
index c0e5190d5d..ba2e84ef8a 100644
--- 
a/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/write/DataEvolutionTableDataWrite.scala
+++ 
b/paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/write/DataEvolutionTableDataWrite.scala
@@ -28,6 +28,7 @@ import org.apache.paimon.spark.util.SparkRowUtils
 import org.apache.paimon.table.sink.{BatchWriteBuilder, CommitMessage, 
CommitMessageImpl, TableWriteImpl}
 import org.apache.paimon.types.RowType
 import org.apache.paimon.utils.RecordWriter
+import org.apache.paimon.utils.SerializationUtils
 
 import org.apache.spark.sql.Row
 
@@ -39,7 +40,7 @@ import scala.collection.mutable.ListBuffer
 case class DataEvolutionTableDataWrite(
     writeBuilder: BatchWriteBuilder,
     writeType: RowType,
-    firstRowIdToPartitionMap: mutable.HashMap[Long, (BinaryRow, Long)],
+    firstRowIdToPartitionMap: mutable.HashMap[Long, (Array[Byte], Long)],
     catalogContext: CatalogContext)
   extends InnerTableV1DataWrite {
 
@@ -73,7 +74,8 @@ case class DataEvolutionTableDataWrite(
           s"Available first row IDs: 
${firstRowIdToPartitionMap.keys.mkString(", ")}")
     }
 
-    val (partition, numRecords) = pair
+    val (partitionBytes, numRecords) = pair
+    val partition = SerializationUtils.deserializeBinaryRow(partitionBytes)
 
     val writer = writeBuilder
       .newWrite()

Reply via email to