parthchandra commented on code in PR #6926:
URL: https://github.com/apache/datafusion-comet/pull/6926#discussion_r4542405988


##########
spark/src/main/scala/org/apache/spark/sql/comet/CometIcebergWriteExec.scala:
##########
@@ -427,3 +410,60 @@ object CometIcebergWriteExec {
     }
   }
 }
+
+/**
+ * Runs one native Iceberg write task per partition of `input`, the RDD that 
feeds the write
+ * plan's one leaf. Like `mapPartitions`, it has `input`'s partitions and a 
one-to-one dependency
+ * on it, but `compute` does not iterate `input`. It hands the partition to
+ * [[CometExecRDD.resolveInputObjects]], as every native block does, which 
gives a `ShuffleScan`
+ * leaf the raw shuffle blocks and a `Scan` leaf an Arrow stream. Iterating a 
shuffle input would
+ * fetch and decode its blocks in the JVM instead.
+ *
+ * Each task runs a copy of `nativeOp` stamped with the task's `partition_id` 
and
+ * `task_attempt_id`, which the native writer needs to name its files.
+ */
+private class CometIcebergWriteRDD(
+    var input: RDD[_],
+    shuffleScanIndices: Set[Int],
+    nativeOp: Operator,
+    nativeMetrics: CometMetricNode,
+    shuffleProbe: Option[IcebergSchedulerTestProbe])
+    extends RDD[ColumnarBatch](input) {
+
+  // Read here on the driver, where the input's partitions are known.
+  private val numPartitions = input.getNumPartitions
+
+  override protected def getPartitions: Array[Partition] = 
firstParent[Any].partitions
+
+  override def compute(split: Partition, context: TaskContext): 
Iterator[ColumnarBatch] = {
+    // Before the shuffle fetch that `resolveInputObjects` starts, as the 
probe's RDD does for a
+    // child that is not read natively.
+    shuffleProbe.foreach(_.beforeInput())

Review Comment:
   In `CometIcebergWriteRDD.compute`, the probe only fires `beforeInput()`. Can 
we add a one-line comment here (or in the class scaladoc stating the shuffle 
path intentionally skips the other probe callbacks because the probe is 
test-only



##########
spark/src/test/scala/org/apache/comet/CometIcebergWriteActionSuite.scala:
##########
@@ -1133,6 +1133,151 @@ class CometIcebergWriteActionSuite
     }
   }
 
+  // A Comet shuffle directly under the native write is decoded inside the 
write's native plan by
+  // a `ShuffleScan`, rather than decoded in the JVM and imported again over 
FFI. Under AQE the
+  // write's child is the shuffle stage or a coalesced read of it. Without 
AQE, Comet reads no
+  // shuffle directly, so the write keeps its FFI `Scan`. Each case is written 
twice, with direct
+  // read on and off, so the two input paths are checked against each other as 
well as the source.
+  test("native acceleration: a write over a shuffle decodes the shuffle blocks 
natively") {
+    assumeNativeAcceleration()
+    withIcebergCatalog { _ =>
+      val columns = "id BIGINT, s STRING, d DOUBLE, st STRUCT<a: INT, b: 
STRING>, " +
+        "arr ARRAY<INT>, m MAP<STRING, INT>, dt DATE, ts TIMESTAMP, ntz 
TIMESTAMP_NTZ, " +
+        "dec DECIMAL(12, 3), bin BINARY, b BOOLEAN, f FLOAT"
+      spark.sql(s"CREATE TABLE $catalog.$ns.shuffle_src ($columns) USING 
iceberg")
+      spark.sql(s"""
+        INSERT INTO $catalog.$ns.shuffle_src
+        SELECT id,
+          IF(id % 7 = 0, NULL, CONCAT('s', CAST(id AS STRING))),
+          CASE id % 11 WHEN 0 THEN NULL WHEN 1 THEN CAST('NaN' AS DOUBLE) WHEN 
2 THEN -0.0D
+            WHEN 3 THEN CAST('Infinity' AS DOUBLE) WHEN 4 THEN 
CAST('-Infinity' AS DOUBLE)
+            ELSE id / 3.0 END,
+          IF(id % 13 = 0, NULL,
+            NAMED_STRUCT('a', CAST(id AS INT), 'b', IF(id % 2 = 0, NULL, 
CAST(id AS STRING)))),

Review Comment:
   The st struct is flat (a INT, b STRING).  Maybe add a nested complex column 
— array-of-struct or struct-containing-map — so nested-type handling over the 
direct-read path is exercised, not just top-level struct/array/map.



##########
spark/src/test/scala/org/apache/comet/CometIcebergWriteActionSuite.scala:
##########
@@ -1133,6 +1133,151 @@ class CometIcebergWriteActionSuite
     }
   }
 
+  // A Comet shuffle directly under the native write is decoded inside the 
write's native plan by
+  // a `ShuffleScan`, rather than decoded in the JVM and imported again over 
FFI. Under AQE the
+  // write's child is the shuffle stage or a coalesced read of it. Without 
AQE, Comet reads no
+  // shuffle directly, so the write keeps its FFI `Scan`. Each case is written 
twice, with direct
+  // read on and off, so the two input paths are checked against each other as 
well as the source.
+  test("native acceleration: a write over a shuffle decodes the shuffle blocks 
natively") {
+    assumeNativeAcceleration()
+    withIcebergCatalog { _ =>
+      val columns = "id BIGINT, s STRING, d DOUBLE, st STRUCT<a: INT, b: 
STRING>, " +
+        "arr ARRAY<INT>, m MAP<STRING, INT>, dt DATE, ts TIMESTAMP, ntz 
TIMESTAMP_NTZ, " +
+        "dec DECIMAL(12, 3), bin BINARY, b BOOLEAN, f FLOAT"
+      spark.sql(s"CREATE TABLE $catalog.$ns.shuffle_src ($columns) USING 
iceberg")
+      spark.sql(s"""
+        INSERT INTO $catalog.$ns.shuffle_src
+        SELECT id,
+          IF(id % 7 = 0, NULL, CONCAT('s', CAST(id AS STRING))),

Review Comment:
   The s column is ASCII only (CONCAT('s', id)). Can we add at least one 
non-ASCII / multibyte string value (e.g. CJK characters) so string transport 
through the shuffle blocks is covered.



##########
spark/src/test/scala/org/apache/comet/CometIcebergWriteActionSuite.scala:
##########
@@ -1133,6 +1133,151 @@ class CometIcebergWriteActionSuite
     }
   }
 
+  // A Comet shuffle directly under the native write is decoded inside the 
write's native plan by
+  // a `ShuffleScan`, rather than decoded in the JVM and imported again over 
FFI. Under AQE the
+  // write's child is the shuffle stage or a coalesced read of it. Without 
AQE, Comet reads no
+  // shuffle directly, so the write keeps its FFI `Scan`. Each case is written 
twice, with direct
+  // read on and off, so the two input paths are checked against each other as 
well as the source.
+  test("native acceleration: a write over a shuffle decodes the shuffle blocks 
natively") {
+    assumeNativeAcceleration()
+    withIcebergCatalog { _ =>
+      val columns = "id BIGINT, s STRING, d DOUBLE, st STRUCT<a: INT, b: 
STRING>, " +

Review Comment:
   Dates/timestamps stay in a mid range. Should we add boundary values 
(pre-1970 date, far-future timestamp, and a non-UTC session timezone for the 
ts/ntz columns) to the generated data?



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