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

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


The following commit(s) were added to refs/heads/master by this push:
     new edffe1a832 Fix race condition when 2 segment upload occurred for the 
same segment (#9905)
edffe1a832 is described below

commit edffe1a832b7a534ebc40d0cd1ca1bbccc1eb02e
Author: Jialiang Li <[email protected]>
AuthorDate: Wed Dec 7 10:05:12 2022 -0800

    Fix race condition when 2 segment upload occurred for the same segment 
(#9905)
    
    Co-authored-by: Jack Li(Analytics Engineering) <[email protected]>
---
 .../pinot/controller/api/upload/ZKOperator.java    | 49 +++++++++++++++++-----
 .../helix/core/SegmentDeletionManager.java         |  1 -
 2 files changed, 39 insertions(+), 11 deletions(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/upload/ZKOperator.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/upload/ZKOperator.java
index 3d33a358f3..4229d6c433 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/upload/ZKOperator.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/upload/ZKOperator.java
@@ -297,8 +297,9 @@ public class ZKOperator {
     }
 
     // Lock if enableParallelPushProtection is true.
+    long segmentUploadStartTime = System.currentTimeMillis();
     if (enableParallelPushProtection) {
-      
newSegmentZKMetadata.setSegmentUploadStartTime(System.currentTimeMillis());
+      newSegmentZKMetadata.setSegmentUploadStartTime(segmentUploadStartTime);
     }
 
     // Update zk metadata customer map
@@ -323,8 +324,7 @@ public class ZKOperator {
         // Cleanup the Zk entry and the segment from the permanent directory 
if it exists.
         LOGGER.error("Could not move segment {} from table {} to permanent 
directory", segmentName, tableNameWithType,
             e);
-        _pinotHelixResourceManager.deleteSegment(tableNameWithType, 
segmentName);
-        LOGGER.info("Deleted zk entry and segment {} for table {}.", 
segmentName, tableNameWithType);
+        deleteSegmentIfNeeded(tableNameWithType, segmentName, 
segmentUploadStartTime, enableParallelPushProtection);
         throw e;
       }
     }
@@ -332,11 +332,11 @@ public class ZKOperator {
     try {
       _pinotHelixResourceManager.assignTableSegment(tableNameWithType, 
segmentMetadata.getName());
     } catch (Exception e) {
-      // assignTableSegment removes the zk entry. Call deleteSegment to remove 
the segment from permanent location.
+      // assignTableSegment removes the zk entry.
+      // Call deleteSegment to remove the segment from permanent location if 
needed.
       LOGGER.error("Caught exception while calling assignTableSegment for 
adding segment: {} to table: {}", segmentName,
           tableNameWithType, e);
-      _pinotHelixResourceManager.deleteSegment(tableNameWithType, segmentName);
-      LOGGER.info("Deleted zk entry and segment {} for table {}.", 
segmentName, tableNameWithType);
+      deleteSegmentIfNeeded(tableNameWithType, segmentName, 
segmentUploadStartTime, enableParallelPushProtection);
       throw e;
     }
 
@@ -344,14 +344,43 @@ public class ZKOperator {
       // Release lock. Expected version will be 0 as we hold a lock and no 
updates could take place meanwhile.
       newSegmentZKMetadata.setSegmentUploadStartTime(-1);
       if (!_pinotHelixResourceManager.updateZkMetadata(tableNameWithType, 
newSegmentZKMetadata, 0)) {
-        _pinotHelixResourceManager.deleteSegment(tableNameWithType, 
segmentName);
-        LOGGER.info("Deleted zk entry and segment {} for table {}.", 
segmentName, tableNameWithType);
-        throw new RuntimeException(
-            String.format("Failed to update ZK metadata for segment: %s of 
table: %s", segmentFile, tableNameWithType));
+        // There is a race condition when it took too much time for the 1st 
segment upload to process (due to slow
+        // PinotFS access), which leads to the 2nd attempt of segment upload, 
and the 2nd segment upload succeeded.
+        // In this case, when the 1st upload comes back, it shouldn't blindly 
delete the segment when it failed to
+        // update the zk metadata. Instead, the 1st attempt should validate 
the upload start time one more time. If the
+        // start time doesn't match with the one persisted in zk metadata, 
segment deletion should be skipped.
+        String errorMsg =
+            String.format("Failed to update ZK metadata for segment: %s of 
table: %s", segmentFile, tableNameWithType);
+        LOGGER.error(errorMsg);
+        deleteSegmentIfNeeded(tableNameWithType, segmentName, 
segmentUploadStartTime, true);
+        throw new RuntimeException(errorMsg);
       }
     }
   }
 
+  /**
+   * Deletes the segment to be uploaded if either one of the criteria is 
qualified:
+   * 1) the uploadStartTime matches with the one persisted in ZK metadata.
+   * 2) enableParallelPushProtection is not enabled.
+   */
+  private void deleteSegmentIfNeeded(String tableNameWithType, String 
segmentName, long currentSegmentUploadStartTime,
+      boolean enableParallelPushProtection) {
+    ZNRecord existingSegmentMetadataZNRecord =
+        
_pinotHelixResourceManager.getSegmentMetadataZnRecord(tableNameWithType, 
segmentName);
+    if (existingSegmentMetadataZNRecord == null) {
+      return;
+    }
+    // Check if the upload start time is set by this thread itself, if yes 
delete the segment.
+    SegmentZKMetadata segmentZKMetadata = new 
SegmentZKMetadata(existingSegmentMetadataZNRecord);
+    long existingSegmentUploadStartTime = 
segmentZKMetadata.getSegmentUploadStartTime();
+    LOGGER.info("Parallel push protection is {} for segment: {}.",
+        (enableParallelPushProtection ? "enabled" : "disabled"), segmentName);
+    if (!enableParallelPushProtection || currentSegmentUploadStartTime == 
existingSegmentUploadStartTime) {
+      _pinotHelixResourceManager.deleteSegment(tableNameWithType, segmentName);
+      LOGGER.info("Deleted zk entry and segment {} for table {}.", 
segmentName, tableNameWithType);
+    }
+  }
+
   private void copySegmentToDeepStore(String tableNameWithType, String 
segmentName, FileUploadType uploadType,
       File segmentFile, String sourceDownloadURIStr, URI 
finalSegmentLocationURI)
       throws Exception {
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/SegmentDeletionManager.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/SegmentDeletionManager.java
index 570a263ac8..ff74bea708 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/SegmentDeletionManager.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/helix/core/SegmentDeletionManager.java
@@ -193,7 +193,6 @@ public class SegmentDeletionManager {
       long effectiveDeletionDelay = Math.min(deletionDelay * 2, 
MAX_DELETION_DELAY_SECONDS);
       LOGGER.info("Postponing deletion of {} segments from table {}", 
segmentsToRetryLater.size(), tableName);
       deleteSegmentsWithDelay(tableName, segmentsToRetryLater, 
deletedSegmentsRetentionMs, effectiveDeletionDelay);
-      return;
     }
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to