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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7cc614c604 [HUDI-4412] Fix multi writer INSERT_OVERWRITE NPE bug 
(#6130)
7cc614c604 is described below

commit 7cc614c6049812202b7fc427e9f399d3a792f5bb
Author: liujinhui <[email protected]>
AuthorDate: Sat Sep 24 15:34:09 2022 +0800

    [HUDI-4412] Fix multi writer INSERT_OVERWRITE NPE bug (#6130)
    
    There are two minor issues fixed here:
    
    1. When the insert_overwrite operation is performed, the
        clusteringPlan in the requestedReplaceMetadata will be
        null. Calling getFileIdsFromRequestedReplaceMetadata will cause NPE.
    
    2. When insert_overwrite operation, inflightCommitMetadata!=null,
        getOperationType should be obtained from 
getHoodieInflightReplaceMetadata,
        the original code will have a null pointer.
---
 .../client/transaction/ConcurrentOperation.java    |  5 ++-
 ...urrentFileWritesConflictResolutionStrategy.java | 49 ++++++++++++++++++++++
 .../hudi/common/testutils/HoodieTestTable.java     |  6 +++
 3 files changed, 58 insertions(+), 2 deletions(-)

diff --git 
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/ConcurrentOperation.java
 
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/ConcurrentOperation.java
index 40da7dca7f..35580229e3 100644
--- 
a/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/ConcurrentOperation.java
+++ 
b/hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/ConcurrentOperation.java
@@ -124,14 +124,15 @@ public class ConcurrentOperation {
             HoodieRequestedReplaceMetadata requestedReplaceMetadata = 
this.metadataWrapper.getMetadataFromTimeline().getHoodieRequestedReplaceMetadata();
             org.apache.hudi.avro.model.HoodieCommitMetadata 
inflightCommitMetadata = 
this.metadataWrapper.getMetadataFromTimeline().getHoodieInflightReplaceMetadata();
             if (instant.isRequested()) {
-              if (requestedReplaceMetadata != null) {
+              // for insert_overwrite/insert_overwrite_table clusteringPlan 
will be empty
+              if (requestedReplaceMetadata != null && 
requestedReplaceMetadata.getClusteringPlan() != null) {
                 this.mutatedFileIds = 
getFileIdsFromRequestedReplaceMetadata(requestedReplaceMetadata);
                 this.operationType = WriteOperationType.CLUSTER;
               }
             } else {
               if (inflightCommitMetadata != null) {
                 this.mutatedFileIds = 
getFileIdWithoutSuffixAndRelativePathsFromSpecificRecord(inflightCommitMetadata.getPartitionToWriteStats()).keySet();
-                this.operationType = 
WriteOperationType.fromValue(this.metadataWrapper.getMetadataFromTimeline().getHoodieCommitMetadata().getOperationType());
+                this.operationType = 
WriteOperationType.fromValue(this.metadataWrapper.getMetadataFromTimeline().getHoodieInflightReplaceMetadata().getOperationType());
               } else if (requestedReplaceMetadata != null) {
                 // inflight replacecommit metadata is empty due to clustering, 
read fileIds from requested replacecommit
                 this.mutatedFileIds = 
getFileIdsFromRequestedReplaceMetadata(requestedReplaceMetadata);
diff --git 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestSimpleConcurrentFileWritesConflictResolutionStrategy.java
 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestSimpleConcurrentFileWritesConflictResolutionStrategy.java
index e7cc296ff6..39b9e1e6dc 100644
--- 
a/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestSimpleConcurrentFileWritesConflictResolutionStrategy.java
+++ 
b/hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/TestSimpleConcurrentFileWritesConflictResolutionStrategy.java
@@ -123,6 +123,41 @@ public class 
TestSimpleConcurrentFileWritesConflictResolutionStrategy extends Ho
     }
   }
 
+  @Test
+  public void testConcurrentWritesWithReplaceInflightCommit() throws Exception 
{
+    createReplaceInflight(HoodieActiveTimeline.createNewInstantTime());
+    HoodieActiveTimeline timeline = metaClient.getActiveTimeline();
+    Option<HoodieInstant> lastSuccessfulInstant = Option.empty();
+
+    // writer 1 starts
+    String currentWriterInstant = HoodieActiveTimeline.createNewInstantTime();
+    createInflightCommit(currentWriterInstant);
+    Option<HoodieInstant> currentInstant = Option.of(new 
HoodieInstant(State.INFLIGHT, HoodieTimeline.COMMIT_ACTION, 
currentWriterInstant));
+
+    // writer 2 starts and finishes
+    String newInstantTime = HoodieActiveTimeline.createNewInstantTime();
+    createReplaceInflight(newInstantTime);
+
+    SimpleConcurrentFileWritesConflictResolutionStrategy strategy = new 
SimpleConcurrentFileWritesConflictResolutionStrategy();
+    HoodieCommitMetadata currentMetadata = 
createCommitMetadata(currentWriterInstant);
+    timeline = timeline.reload();
+
+    List<HoodieInstant> candidateInstants = 
strategy.getCandidateInstants(timeline, currentInstant.get(), 
lastSuccessfulInstant).collect(
+        Collectors.toList());
+
+    // writer 1 conflicts with writer 2
+    Assertions.assertTrue(candidateInstants.size() == 1);
+    ConcurrentOperation thatCommitOperation = new 
ConcurrentOperation(candidateInstants.get(0), metaClient);
+    ConcurrentOperation thisCommitOperation = new 
ConcurrentOperation(currentInstant.get(), currentMetadata);
+    Assertions.assertTrue(strategy.hasConflict(thisCommitOperation, 
thatCommitOperation));
+    try {
+      strategy.resolveConflict(null, thisCommitOperation, thatCommitOperation);
+      Assertions.fail("Cannot reach here, writer 1 and writer 2 should have 
thrown a conflict");
+    } catch (HoodieWriteConflictException e) {
+      // expected
+    }
+  }
+
   @Test
   public void testConcurrentWritesWithInterleavingScheduledCompaction() throws 
Exception {
     createCommit(HoodieActiveTimeline.createNewInstantTime());
@@ -394,6 +429,20 @@ public class 
TestSimpleConcurrentFileWritesConflictResolutionStrategy extends Ho
         
.withBaseFilesInPartition(HoodieTestDataGenerator.DEFAULT_FIRST_PARTITION_PATH, 
fileId1, fileId2);
   }
 
+  private void createReplaceInflight(String instantTime) throws Exception {
+    String fileId1 = "file-1";
+    String fileId2 = "file-2";
+
+    HoodieCommitMetadata inflightReplaceMetadata = new HoodieCommitMetadata();
+    
inflightReplaceMetadata.setOperationType(WriteOperationType.INSERT_OVERWRITE);
+    HoodieWriteStat writeStat = new HoodieWriteStat();
+    writeStat.setFileId("file-1");
+    
inflightReplaceMetadata.addWriteStat(HoodieTestDataGenerator.DEFAULT_FIRST_PARTITION_PATH,
 writeStat);
+    HoodieTestTable.of(metaClient)
+        .addInflightReplace(instantTime, Option.of(inflightReplaceMetadata))
+        
.withBaseFilesInPartition(HoodieTestDataGenerator.DEFAULT_FIRST_PARTITION_PATH, 
fileId1, fileId2);
+  }
+
   private void createReplace(String instantTime, WriteOperationType 
writeOperationType) throws Exception {
     String fileId1 = "file-1";
     String fileId2 = "file-2";
diff --git 
a/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestTable.java
 
b/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestTable.java
index 31de82b12c..c2531d47c1 100644
--- 
a/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestTable.java
+++ 
b/hudi-common/src/test/java/org/apache/hudi/common/testutils/HoodieTestTable.java
@@ -284,6 +284,12 @@ public class HoodieTestTable {
     return this;
   }
 
+  public HoodieTestTable addInflightReplace(String instantTime, 
Option<HoodieCommitMetadata> inflightReplaceMetadata) throws Exception {
+    createInflightReplaceCommit(basePath, instantTime, 
inflightReplaceMetadata);
+    currentInstantTime = instantTime;
+    return this;
+  }
+
   public HoodieTestTable addInflightClean(String instantTime, 
HoodieCleanerPlan cleanerPlan) throws IOException {
     createRequestedCleanFile(basePath, instantTime, cleanerPlan);
     createInflightCleanFile(basePath, instantTime, cleanerPlan);

Reply via email to