Repository: apache/hudi
Pull request #13163: [HUDI-9319] Fix inconsistency of ordering value type between reader &…
Author: login: cshuo
Labels: name: size:M
description: PR with lines of changes in (100, 300]
Created: 2025-04-17T10:49:07Z | Merged: 2025-04-20T03:59:27Z

… writers

### Change Logs

Fix inconsistency of ordering value type between reader & writers

### Impact

none

### Risk level (write none, low medium or high below)

low

### Documentation Update

_Describe any necessary documentation update if there is any new feature, config, or user-facing change. If not, put "none"._

- _The config description must be updated if new configs are added or the default value of the configs are changed_
- _Any new feature or user-facing change requires updating the Hudi website. Please create a Jira ticket, attach the
  ticket number here and follow the [instruction](https://hudi.apache.org/contribute/developer-setup#website) to make
  changes to the website._

### Contributor's checklist

- [ ] Read through [contributor's guide](https://hudi.apache.org/contribute/how-to-contribute)
- [ ] Change Logs and Impact were stated clearly
- [ ] Adequate tests were added if applicable
- [ ] CI passed



Diff:
diff --git a/hudi-common/src/main/java/org/apache/hudi/common/util/SpillableMapUtils.java b/hudi-common/src/main/java/org/apache/hudi/common/util/SpillableMapUtils.java
index df788fa1741fb..c742b05aef160 100644
--- a/hudi-common/src/main/java/org/apache/hudi/common/util/SpillableMapUtils.java
+++ b/hudi-common/src/main/java/org/apache/hudi/common/util/SpillableMapUtils.java
@@ -18,6 +18,7 @@
 
 package org.apache.hudi.common.util;
 
+import org.apache.hudi.avro.HoodieAvroUtils;
 import org.apache.hudi.common.fs.SizeAwareDataOutputStream;
 import org.apache.hudi.common.model.HoodieAvroRecord;
 import org.apache.hudi.common.model.HoodieKey;
@@ -168,8 +169,9 @@ private static Comparable getPreCombineVal(GenericRecord rec, String preCombineF
     if (isNullOrEmpty(preCombineField)) {
       return DEFAULT_ORDERING_VALUE;
     }
-    Schema.Field field = rec.getSchema().getField(preCombineField);
-    return field == null ? DEFAULT_ORDERING_VALUE : (Comparable) rec.get(field.pos());
+    // keep consistent with writer side, using Java type for ordering value, see `DefaultHoodieRecordPayload`.
+    Object orderingValue = HoodieAvroUtils.getNestedFieldVal(rec, preCombineField, true, false);
+    return orderingValue == null ? DEFAULT_ORDERING_VALUE : (Comparable) orderingValue;
   }
 
   /**
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java
index 5cda4f25092bf..56fa44727b7ef 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java
@@ -56,9 +56,11 @@ public void openFunction() throws Exception {
   @Override
   public void invoke(I record) throws Exception {
     HoodieFlinkInternalRow hoodieRecord = toHoodieFunction.map((RowData) record);
-    ScalaCollector<HoodieFlinkInternalRow> collector = ScalaCollector.getInstance();
+    RecordsCollector<HoodieFlinkInternalRow> collector = RecordsCollector.getInstance();
     assignFunction.processElement(hoodieRecord, null, collector);
-    writeFunction.processElement(collector.getVal(), null, null);
+    for (HoodieFlinkInternalRow row: collector.getVal()) {
+      writeFunction.processElement(row, null, null);
+    }
   }
 
   @Override
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ScalaCollector.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/RecordsCollector.java
similarity index 75%
rename from hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ScalaCollector.java
rename to hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/RecordsCollector.java
index 681c6de2a89f5..4314181fd933a 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ScalaCollector.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/RecordsCollector.java
@@ -20,27 +20,35 @@
 
 import org.apache.flink.util.Collector;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A mock {@link Collector} that used in  {@link TestFunctionWrapper}.
  */
-class ScalaCollector<T> implements Collector<T> {
-  private T val;
+class RecordsCollector<T> implements Collector<T> {
+  private List<T> val;
+
+  public RecordsCollector() {
+    this.val = new ArrayList<>();
+  }
 
-  public static <T> ScalaCollector<T> getInstance() {
-    return new ScalaCollector<>();
+  public static <T> RecordsCollector<T> getInstance() {
+    return new RecordsCollector<>();
   }
 
   @Override
   public void collect(T t) {
-    this.val = t;
+    this.val.add(t);
   }
 
   @Override
   public void close() {
+    this.val.clear();
     this.val = null;
   }
 
-  public T getVal() {
+  public List<T> getVal() {
     return val;
   }
 }
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/StreamWriteFunctionWrapper.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/StreamWriteFunctionWrapper.java
index b87c55092a4f0..74d3f5202353f 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/StreamWriteFunctionWrapper.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/StreamWriteFunctionWrapper.java
@@ -156,7 +156,7 @@ public void openFunction() throws Exception {
       bootstrapOperator.setup(streamTask, streamConfig, output);
       bootstrapOperator.initializeState(this.stateInitializationContext);
 
-      Collector<HoodieFlinkInternalRow> collector = ScalaCollector.getInstance();
+      Collector<HoodieFlinkInternalRow> collector = RecordsCollector.getInstance();
       for (HoodieFlinkInternalRow bootstrapRecord : output.getRecords()) {
         stateInitializationContext.getKeyedStateStore().setCurrentKey(bootstrapRecord.getRecordKey());
         bucketAssignerFunction.processElement(bootstrapRecord, null, collector);
@@ -176,10 +176,12 @@ public void openFunction() throws Exception {
   public void invoke(I record) throws Exception {
     HoodieFlinkInternalRow hoodieRecord = toHoodieFunction.map((RowData) record);
     stateInitializationContext.getKeyedStateStore().setCurrentKey(hoodieRecord.getRecordKey());
-    ScalaCollector<HoodieFlinkInternalRow> collector = ScalaCollector.getInstance();
+    RecordsCollector<HoodieFlinkInternalRow> collector = RecordsCollector.getInstance();
     bucketAssignerFunction.processElement(hoodieRecord, null, collector);
     bucketAssignFunctionContext.setCurrentKey(hoodieRecord.getRecordKey());
-    writeFunction.processElement(collector.getVal(), null, null);
+    for (HoodieFlinkInternalRow row: collector.getVal()) {
+      writeFunction.processElement(row, null, null);
+    }
   }
 
   public WriteMetadataEvent[] getEventBuffer() {
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestInputFormat.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestInputFormat.java
index b4d0ca4b552a9..e5d94d52d3329 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestInputFormat.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/table/format/TestInputFormat.java
@@ -52,11 +52,13 @@
 import org.apache.flink.configuration.Configuration;
 import org.apache.flink.core.io.InputSplit;
 import org.apache.flink.table.api.DataTypes;
+import org.apache.flink.table.catalog.ResolvedSchema;
 import org.apache.flink.table.data.RowData;
 import org.apache.flink.table.expressions.CallExpression;
 import org.apache.flink.table.expressions.FieldReferenceExpression;
 import org.apache.flink.table.expressions.ValueLiteralExpression;
 import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
+import org.apache.flink.table.runtime.typeutils.RowDataSerializer;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -1152,6 +1154,30 @@ void testReadWithWiderSchema(HoodieTableType tableType) throws Exception {
     TestData.assertRowDataEquals(result, TestData.DATA_SET_INSERT);
   }
 
+  @Test
+  void testOrderingValueWithDecimalType() throws Exception {
+    conf = new Configuration();
+    conf.set(FlinkOptions.PATH, tempFile.getAbsolutePath());
+    conf.set(FlinkOptions.TABLE_NAME, "TestHoodieTable");
+    conf.set(FlinkOptions.TABLE_TYPE, HoodieTableType.MERGE_ON_READ.name());
+    conf.set(FlinkOptions.PARTITION_PATH_FIELD, "partition");
+    conf.setString(FlinkOptions.SOURCE_AVRO_SCHEMA.key(), AvroSchemaConverter.convertToSchema(TestConfigurations.ROW_TYPE_DECIMAL_ORDERING).toString());
+    conf.set(FlinkOptions.COMPACTION_ASYNC_ENABLED, false); // by default close the async compaction
+    StreamerUtil.initTableIfNotExists(conf);
+
+    tableSource = getTableSource(conf, TestConfigurations.TABLE_SCHEMA_DECIMAL_ORDERING);
+
+    TestData.writeData(TestData.DATA_SET_INSERT_DECIMAL_ORDERING, conf);
+    InputFormat<RowData, ?> inputFormat = this.tableSource.getInputFormat();
+    List<RowData> result = readData(inputFormat, TestConfigurations.SERIALIZER_DECIMAL_ORDERING);
+
+    // global index is enabled by default
+    // row1: <id1, Danny, 23, 10, par1>
+    // row2: <id1, Bob, 44, 1, par2>
+    // the ordering value of row2: 1 is smaller than that of row1: 10, so row1 in `par1` will not be deleted.
+    TestData.assertRowDataEquals(result, TestData.DATA_SET_INSERT_DECIMAL_ORDERING, TestConfigurations.ROW_DATA_TYPE_DECIMAL_ORDERING);
+  }
+
   /**
    * Test reading file groups with compaction plan scheduled and delta logs.
    * File-slice after pending compaction-requested instant-time should also be considered valid.
@@ -1241,28 +1267,40 @@ private static Stream<Arguments> preCombiningAndChangelogModeParams() {
   }
 
   private HoodieTableSource getTableSource(Configuration conf) {
+    return getTableSource(conf, TestConfigurations.TABLE_SCHEMA);
+  }
+
+  private HoodieTableSource getTableSource(Configuration conf, ResolvedSchema tableSchema) {
     return new HoodieTableSource(
-        SerializableSchema.create(TestConfigurations.TABLE_SCHEMA),
+        SerializableSchema.create(tableSchema),
         new StoragePath(tempFile.getAbsolutePath()),
         Collections.singletonList("partition"),
         "default",
         conf);
   }
 
-  @SuppressWarnings("rawtypes")
   private static List<RowData> readData(InputFormat inputFormat) throws IOException {
+    return readData(inputFormat, TestConfigurations.SERIALIZER);
+  }
+
+  @SuppressWarnings("rawtypes")
+  private static List<RowData> readData(InputFormat inputFormat, RowDataSerializer serializer) throws IOException {
     InputSplit[] inputSplits = inputFormat.createInputSplits(1);
-    return readData(inputFormat, inputSplits);
+    return readData(inputFormat, inputSplits, serializer);
   }
 
-  @SuppressWarnings("unchecked, rawtypes")
   private static List<RowData> readData(InputFormat inputFormat, InputSplit[] inputSplits) throws IOException {
+    return readData(inputFormat, inputSplits, TestConfigurations.SERIALIZER);
+  }
+
+  @SuppressWarnings("unchecked, rawtypes")
+  private static List<RowData> readData(InputFormat inputFormat, InputSplit[] inputSplits, RowDataSerializer serializer) throws IOException {
     List<RowData> result = new ArrayList<>();
 
     for (InputSplit inputSplit : inputSplits) {
       inputFormat.open(inputSplit);
       while (!inputFormat.reachedEnd()) {
-        result.add(TestConfigurations.SERIALIZER.copy((RowData) inputFormat.nextRecord(null))); // no reuse
+        result.add(serializer.copy((RowData) inputFormat.nextRecord(null))); // no reuse
       }
       inputFormat.close();
     }
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestConfigurations.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestConfigurations.java
index 16ab5fba7ee60..3acfbf057f58e 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestConfigurations.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestConfigurations.java
@@ -57,10 +57,25 @@ private TestConfigurations() {
 
   public static final RowType ROW_TYPE = (RowType) ROW_DATA_TYPE.getLogicalType();
 
+  public static final DataType ROW_DATA_TYPE_DECIMAL_ORDERING = DataTypes.ROW(
+          DataTypes.FIELD("uuid", DataTypes.VARCHAR(20)),// record key
+          DataTypes.FIELD("name", DataTypes.VARCHAR(10)),
+          DataTypes.FIELD("age", DataTypes.INT()),
+          DataTypes.FIELD("ts", DataTypes.DECIMAL(10, 0)), // precombine field
+          DataTypes.FIELD("partition", DataTypes.VARCHAR(10)))
+      .notNull();
+
+  public static final RowType ROW_TYPE_DECIMAL_ORDERING =
+      (RowType) ROW_DATA_TYPE_DECIMAL_ORDERING.getLogicalType();
+
   public static final ResolvedSchema TABLE_SCHEMA = SchemaBuilder.instance()
       .fields(ROW_TYPE.getFieldNames(), ROW_DATA_TYPE.getChildren())
       .build();
 
+  public static final ResolvedSchema TABLE_SCHEMA_DECIMAL_ORDERING = SchemaBuilder.instance()
+      .fields(ROW_TYPE_DECIMAL_ORDERING.getFieldNames(), ROW_DATA_TYPE_DECIMAL_ORDERING.getChildren())
+      .build();
+
   private static final List<String> FIELDS = ROW_TYPE.getFields().stream()
       .map(RowType.RowField::asSummaryString).collect(Collectors.toList());
 
@@ -306,6 +321,8 @@ public static String getCsvSourceDDL(String tableName, String fileName) {
 
   public static final RowDataSerializer SERIALIZER = new RowDataSerializer(ROW_TYPE);
 
+  public static final RowDataSerializer SERIALIZER_DECIMAL_ORDERING = new RowDataSerializer(ROW_TYPE_DECIMAL_ORDERING);
+
   public static Configuration getDefaultConf(String tablePath) {
     Configuration conf = new Configuration();
     conf.setString(FlinkOptions.PATH, tablePath);
diff --git a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestData.java b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestData.java
index 2888f82cde2f9..1c671e371d976 100644
--- a/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestData.java
+++ b/hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/utils/TestData.java
@@ -58,6 +58,7 @@
 import org.apache.flink.table.data.writer.BinaryRowWriter;
 import org.apache.flink.table.data.writer.BinaryWriter;
 import org.apache.flink.table.runtime.typeutils.InternalSerializers;
+import org.apache.flink.table.types.DataType;
 import org.apache.flink.table.types.logical.LogicalType;
 import org.apache.flink.table.types.logical.RowType;
 import org.apache.flink.types.Row;
@@ -119,6 +120,13 @@ public class TestData {
           TimestampData.fromEpochMillis(8), StringData.fromString("par4"))
   );
 
+  public static List<RowData> DATA_SET_INSERT_DECIMAL_ORDERING = Arrays.asList(
+      insertRow(TestConfigurations.ROW_TYPE_DECIMAL_ORDERING, StringData.fromString("id1"), StringData.fromString("Danny"),
+          23, DecimalData.fromBigDecimal(BigDecimal.TEN, 10, 0), StringData.fromString("par1")),
+      insertRow(TestConfigurations.ROW_TYPE_DECIMAL_ORDERING, StringData.fromString("id1"), StringData.fromString("Bob"),
+          44, DecimalData.fromBigDecimal(BigDecimal.ONE, 10, 0), StringData.fromString("par2"))
+  );
+
   public static List<RowData> DATA_SET_INSERT_PARTITION_IS_NULL = Arrays.asList(
       insertRow(StringData.fromString("idNull"), StringData.fromString("He"), 30,
           TimestampData.fromEpochMillis(9), null)
@@ -499,8 +507,15 @@ private static Integer toIdSafely(Object id) {
    * Returns string format of a list of RowData.
    */
   public static String rowDataToString(List<RowData> rows) {
+    return rowDataToString(rows, TestConfigurations.ROW_DATA_TYPE);
+  }
+
+  /**
+   * Returns string format of a list of RowData.
+   */
+  public static String rowDataToString(List<RowData> rows, DataType rowType) {
     DataStructureConverter<Object, Object> converter =
-        DataStructureConverters.getConverter(TestConfigurations.ROW_DATA_TYPE);
+        DataStructureConverters.getConverter(rowType);
     return rows.stream()
         .sorted(Comparator.comparing(o -> toIdSafely(o.getString(0))))
         .map(row -> converter.toExternal(row).toString())
@@ -671,8 +686,20 @@ public static void assertRowDataEquals(List<RowData> rows, String expected) {
    * @param expected Expected row data list
    */
   public static void assertRowDataEquals(List<RowData> rows, List<RowData> expected) {
-    String rowsString = rowDataToString(rows);
-    assertThat(rowsString, is(rowDataToString(expected)));
+    assertRowDataEquals(rows, expected, TestConfigurations.ROW_DATA_TYPE);
+  }
+
+  /**
+   * Sort the {@code rows} using field at index 0 and asserts
+   * it equals with the expected row data list {@code expected}.
+   *
+   * @param rows     Actual result rows
+   * @param expected Expected row data list
+   * @param rowType DataType for record
+   */
+  public static void assertRowDataEquals(List<RowData> rows, List<RowData> expected, DataType rowType) {
+    String rowsString = rowDataToString(rows, rowType);
+    assertThat(rowsString, is(rowDataToString(expected, rowType)));
   }
 
   /**


Reviews:
user: login: danny0405
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T01:02:39Z
user: login: danny0405
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T01:03:59Z
user: login: cshuo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T01:12:20Z
user: login: cshuo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T01:14:10Z
user: login: danny0405
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-20T03:58:57Z

Review comments:
diff_hunk: @@ -58,7 +58,9 @@ public void invoke(I record) throws Exception {
     HoodieFlinkInternalRow hoodieRecord = toHoodieFunction.map((RowData) record);
     ScalaCollector<HoodieFlinkInternalRow> collector = ScalaCollector.getInstance();
     assignFunction.processElement(hoodieRecord, null, collector);
-    writeFunction.processElement(collector.getVal(), null, null);
+    for (HoodieFlinkInternalRow row: collector.getVal()) {
path: hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java
user: login: danny0405
body: why change this line?
created_at: 2025-04-18T01:02:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -20,27 +20,35 @@
 
 import org.apache.flink.util.Collector;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A mock {@link Collector} that used in  {@link TestFunctionWrapper}.
  */
 class ScalaCollector<T> implements Collector<T> {
path: hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ScalaCollector.java
user: login: danny0405
body: The class name implies that the result should be singleton.
created_at: 2025-04-18T01:03:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -58,7 +58,9 @@ public void invoke(I record) throws Exception {
     HoodieFlinkInternalRow hoodieRecord = toHoodieFunction.map((RowData) record);
     ScalaCollector<HoodieFlinkInternalRow> collector = ScalaCollector.getInstance();
     assignFunction.processElement(hoodieRecord, null, collector);
-    writeFunction.processElement(collector.getVal(), null, null);
+    for (HoodieFlinkInternalRow row: collector.getVal()) {
path: hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ConsistentBucketStreamWriteFunctionWrapper.java
user: login: cshuo
body: `collector` is used to collect emitted records from `asisgnFunction`, it's possible `asisgnFunction` emits more than one records for one time processElement, e.g., for case global inedx, it may emits 1 DELETE + 1 INSERT.
created_at: 2025-04-18T01:12:20Z
author_association: COLLABORATOR
diff_hunk: @@ -20,27 +20,35 @@
 
 import org.apache.flink.util.Collector;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A mock {@link Collector} that used in  {@link TestFunctionWrapper}.
  */
 class ScalaCollector<T> implements Collector<T> {
path: hudi-flink-datasource/hudi-flink/src/test/java/org/apache/hudi/sink/utils/ScalaCollector.java
user: login: cshuo
body: `Scalar`? ok, then we should rename it if multiple emitted records is expected.
created_at: 2025-04-18T01:14:10Z
author_association: COLLABORATOR

PR comments:
user: login: hudi-bot
created_at: 2025-04-18T03:01:02Z
body: <!--
Meta data
{
  "version" : 1,
  "metaDataEntries" : [ {
    "hash" : "63aed2dc9fed8ec830144a4e3fc32fc9d52b2543",
    "status" : "DELETED",
    "url" : "https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=4924",
    "triggerID" : "63aed2dc9fed8ec830144a4e3fc32fc9d52b2543",
    "triggerType" : "PUSH"
  }, {
    "hash" : "4aaa9ccc9b29caa3148abdc1f3b84719a2b61942",
    "status" : "DELETED",
    "url" : "https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=4925",
    "triggerID" : "4aaa9ccc9b29caa3148abdc1f3b84719a2b61942",
    "triggerType" : "PUSH"
  }, {
    "hash" : "637dd78ba9b732e10ecec44fb172e3cfa8bc9f5a",
    "status" : "SUCCESS",
    "url" : "https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=4940",
    "triggerID" : "637dd78ba9b732e10ecec44fb172e3cfa8bc9f5a",
    "triggerType" : "PUSH"
  } ]
}-->
## CI report:

* 637dd78ba9b732e10ecec44fb172e3cfa8bc9f5a Azure: [SUCCESS](https://dev.azure.com/apachehudi/a1a51da7-8592-47d4-88dc-fd67bed336bb/_build/results?buildId=4940) 

<details>
<summary>Bot commands</summary>
  @hudi-bot supports the following commands:

 - `@hudi-bot run azure` re-run the last Azure build
</details>
author_association: COLLABORATOR