wombatu-kun opened a new issue, #20367:
URL: https://github.com/apache/hudi/issues/20367

   ### Bug Description
   
   **What happened:**
   
   This affects the Spark record type 
(`hoodie.write.record.merge.custom.implementation.classes=org.apache.hudi.DefaultSparkRecordMerger`)
 on a MERGE_ON_READ table at table version 8 or 9 whose ordering field is 
nested, for example `meta.ts`. Every delete is written into the delete block 
with the default ordering value instead of its own.
   
   `HoodieInlineLogAppendHandle.writeDelete` takes the value from 
`HoodieSparkRecord.getOrderingValueAsJava`. That method checks the field with 
the top-level-only `HoodieSchema.getField`, finds no `meta.ts` and falls back 
to the default. Readers treat a delete with the default ordering value as a 
commit-time delete. So under `EVENT_TIME_ORDERING`, a delete that is older than 
the stored row still removes it.
   
   `HoodieSparkRecord.doGetOrderingValue` resolves the same field by path, and 
the Avro record type keeps the value.
   
   **What you expected:**
   
   The stale delete is ignored and the newer row stays. That is what happens 
with the Avro record type or a top-level ordering field.
   
   **Steps to reproduce:**
   
   1. Create a MERGE_ON_READ table at table version 9 (the default of 1.2.x) 
with the Spark record merger, a nested ordering field `meta.ts` and 
`EVENT_TIME_ORDERING`.
   2. Insert `id1` with `meta.ts = 100`.
   3. Upsert `id1` with `meta.ts = 50` and `_hoodie_is_deleted = true`.
   4. A snapshot read no longer returns `id1`; it should still return it with 
`meta.ts = 100`.
   
   The reproducing test in `TestMORDataSource` fails on master for the Spark 
rows. The Avro row shows the expected result:
   
   ```scala
     @ParameterizedTest
     @CsvSource(value = Array("AVRO;bigint;8", "SPARK;bigint;9", 
"SPARK;string;8", "SPARK;decimal(10,2);9"), delimiter = ';')
     def testDeleteWithNestedOrderingField(recordType: HoodieRecordType, 
orderingType: String, tableVersion: Int): Unit = {
       val (writeOpts, readOpts) = getWriterReaderOpts(recordType)
       val opts = writeOpts ++ Map(
         TABLE_TYPE.key -> MOR_TABLE_TYPE_OPT_VAL,
         RECORDKEY_FIELD.key -> "id",
         PARTITIONPATH_FIELD.key -> "",
         HoodieTableConfig.ORDERING_FIELDS.key -> "meta.ts",
         HoodieWriteConfig.RECORD_MERGE_MODE.key -> 
RecordMergeMode.EVENT_TIME_ORDERING.name(),
         HoodieWriteConfig.WRITE_TABLE_VERSION.key -> tableVersion.toString,
         HoodieTableConfig.VERSION.key -> tableVersion.toString,
         HoodieCompactionConfig.INLINE_COMPACT.key -> "false")
   
       def write(rows: Seq[(String, String, Boolean)], mode: SaveMode): Unit = {
         val localSpark = spark
         import localSpark.implicits._
         rows.toDF("id", "ts", "_hoodie_is_deleted")
           .select(col("id"), 
struct(col("ts").cast(orderingType).as("ts")).as("meta"), 
col("_hoodie_is_deleted"))
           
.coalesce(1).write.format("hudi").options(opts).mode(mode).save(basePath)
       }
   
       write(Seq(("id1", "100", false), ("id2", "100", false)), 
SaveMode.Overwrite)
       // The delete of id1 is older than the stored row and must not remove 
it; the delete of id2 is newer.
       write(Seq(("id1", "050", true), ("id2", "150", true)), SaveMode.Append)
   
       val rows = 
spark.read.format("hudi").options(readOpts).load(basePath).select("id", 
"meta.ts").collect()
       assertEquals(Seq("id1"), rows.map(_.getString(0)).toSeq)
       assertEquals(100, BigDecimal(rows.head.get(1).toString).toInt)
     }
   ```
   
   ### Environment
   
   **Hudi version:** 1.2.0 and 1.2.1 (table version 9 by default), and master 
with `hoodie.write.table.version` 8 or 9
   **Query engine:** Spark, with the Spark record merger
   **Relevant configs:** a nested ordering field, `EVENT_TIME_ORDERING`, 
`hoodie.write.record.merge.custom.implementation.classes=org.apache.hudi.DefaultSparkRecordMerger`
   
   ### Logs and Stack Trace
   
   The reproducing test on master:
   
   ```
   [ERROR] 
org.apache.hudi.functional.TestMORDataSource.testDeleteWithNestedOrderingField(HoodieRecordType,
 String, int)[2] -- Time elapsed: 5.606 s <<< FAILURE!
   org.opentest4j.AssertionFailedError: expected: <List(id1)> but was: 
<WrappedArray()>
   [ERROR] 
org.apache.hudi.functional.TestMORDataSource.testDeleteWithNestedOrderingField(HoodieRecordType,
 String, int)[3] -- Time elapsed: 4.644 s <<< FAILURE!
   org.opentest4j.AssertionFailedError: expected: <List(id1)> but was: 
<WrappedArray()>
   [ERROR] 
org.apache.hudi.functional.TestMORDataSource.testDeleteWithNestedOrderingField(HoodieRecordType,
 String, int)[4] -- Time elapsed: 4.556 s <<< FAILURE!
   org.opentest4j.AssertionFailedError: expected: <List(id1)> but was: 
<WrappedArray()>
   ```
   


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

Reply via email to