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

erose pushed a commit to branch ozone-1.2
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/ozone-1.2 by this push:
     new 8bd36cd  HDDS-5893. Potential data loss on multipart upload commit 
(#2763)
8bd36cd is described below

commit 8bd36cd7d06e9a9d34824a4465fe4e31263776d4
Author: UENISHI Kota <[email protected]>
AuthorDate: Wed Oct 27 06:50:08 2021 +0900

    HDDS-5893. Potential data loss on multipart upload commit (#2763)
---
 .../S3MultipartUploadCompleteResponse.java         |  4 +-
 ...stS3MultipartUploadCompleteResponseWithFSO.java | 73 ++++++++++++++++++----
 2 files changed, 63 insertions(+), 14 deletions(-)

diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/multipart/S3MultipartUploadCompleteResponse.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/multipart/S3MultipartUploadCompleteResponse.java
index b832813..ec47b30 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/multipart/S3MultipartUploadCompleteResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/s3/multipart/S3MultipartUploadCompleteResponse.java
@@ -97,7 +97,9 @@ public class S3MultipartUploadCompleteResponse extends 
OMClientResponse {
       if (repeatedOmKeyInfo == null) {
         repeatedOmKeyInfo = new RepeatedOmKeyInfo(partsUnusedList);
       } else {
-        repeatedOmKeyInfo.addOmKeyInfo(omKeyInfo);
+        for (OmKeyInfo unUsedPart : partsUnusedList) {
+          repeatedOmKeyInfo.addOmKeyInfo(unUsedPart);
+        }
       }
 
       omMetadataManager.getDeletedTable().putWithBatch(batchOperation,
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/s3/multipart/TestS3MultipartUploadCompleteResponseWithFSO.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/s3/multipart/TestS3MultipartUploadCompleteResponseWithFSO.java
index 5114c73..5fdd575 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/s3/multipart/TestS3MultipartUploadCompleteResponseWithFSO.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/s3/multipart/TestS3MultipartUploadCompleteResponseWithFSO.java
@@ -132,10 +132,57 @@ public class TestS3MultipartUploadCompleteResponseWithFSO
     TestOMRequestUtils.addVolumeAndBucketToDB(volumeName, bucketName,
             omMetadataManager);
     createParentPath(volumeName, bucketName);
+    runAddDBToBatchWithParts(volumeName, bucketName, keyName, 0);
 
-    String multipartUploadID = UUID.randomUUID().toString();
+    // As 1 unused parts exists, so 1 unused entry should be there in delete
+    // table.
+    Assert.assertEquals(2, omMetadataManager.countRowsInTable(
+            omMetadataManager.getDeletedTable()));
+  }
+
+  @Test
+  public void testAddDBToBatchWithPartsWithKeyInDeleteTable() throws Exception 
{
+
+    String volumeName = UUID.randomUUID().toString();
+    String bucketName = UUID.randomUUID().toString();
+    String keyName = getKeyName();
+
+    TestOMRequestUtils.addVolumeAndBucketToDB(volumeName, bucketName,
+            omMetadataManager);
+    createParentPath(volumeName, bucketName);
+
+    // Put an entry to delete table with the same key prior to multipart commit
+    OmKeyInfo prevKey = TestOMRequestUtils.createOmKeyInfo(volumeName,
+            bucketName, keyName,
+            HddsProtos.ReplicationType.RATIS,
+            HddsProtos.ReplicationFactor.ONE,
+            parentID + 8,
+            parentID, 8, Time.now());
+    RepeatedOmKeyInfo prevKeys = new RepeatedOmKeyInfo(prevKey);
+    String ozoneKey = omMetadataManager
+            .getOzoneKey(prevKey.getVolumeName(),
+                    prevKey.getBucketName(), prevKey.getFileName());
+    omMetadataManager.getDeletedTable().put(ozoneKey, prevKeys);
+
+    long oId = runAddDBToBatchWithParts(volumeName, bucketName, keyName, 1);
+
+    // Make sure new object isn't in delete table
+    RepeatedOmKeyInfo ds = omMetadataManager.getDeletedTable().get(ozoneKey);
+    for (OmKeyInfo omKeyInfo : ds.getOmKeyInfoList()) {
+      Assert.assertNotEquals(oId, omKeyInfo.getObjectID());
+    }
+
+    // As 1 unused parts and 1 previously put-and-deleted object exist,
+    // so 2 entries should be there in delete table.
+    Assert.assertEquals(2, omMetadataManager.countRowsInTable(
+            omMetadataManager.getDeletedTable()));
+  }
 
-    int deleteEntryCount = 0;
+  private long runAddDBToBatchWithParts(String volumeName,
+      String bucketName, String keyName, int deleteEntryCount)
+          throws Exception {
+
+    String multipartUploadID = UUID.randomUUID().toString();
 
     String fileName = OzoneFSUtils.getFileName(keyName);
     String dbMultipartKey = omMetadataManager.getMultipartKey(volumeName,
@@ -153,17 +200,19 @@ public class TestS3MultipartUploadCompleteResponseWithFSO
     OmMultipartKeyInfo omMultipartKeyInfo =
             s3InitiateMultipartUploadResponseFSO.getOmMultipartKeyInfo();
 
+    // After commits, it adds an entry to the deleted table. Incrementing the
+    // variable before the method call, because this method also has entry
+    // count check inside.
+    deleteEntryCount++;
     OmKeyInfo omKeyInfoFSO = commitS3MultipartUpload(volumeName, bucketName,
             keyName, multipartUploadID, fileName, dbMultipartKey,
-            omMultipartKeyInfo);
-    // After commits, it adds an entry to the deleted table.
-    deleteEntryCount++;
+            omMultipartKeyInfo, deleteEntryCount);
 
     OmKeyInfo omKeyInfo =
             TestOMRequestUtils.createOmKeyInfo(volumeName, bucketName, keyName,
                     HddsProtos.ReplicationType.RATIS,
                     HddsProtos.ReplicationFactor.ONE,
-                    parentID + 10,
+                    parentID + 9,
                     parentID, 100, Time.now());
     List<OmKeyInfo> unUsedParts = new ArrayList<>();
     unUsedParts.add(omKeyInfo);
@@ -184,17 +233,15 @@ public class TestS3MultipartUploadCompleteResponseWithFSO
     Assert.assertNull(omMetadataManager.getOpenKeyTable(getBucketLayout())
         .get(dbMultipartOpenKey));
 
-    // As 1 unused parts exists, so 1 unused entry should be there in delete
-    // table.
-    deleteEntryCount++;
-    Assert.assertEquals(deleteEntryCount, omMetadataManager.countRowsInTable(
-            omMetadataManager.getDeletedTable()));
+    return omKeyInfoFSO.getObjectID();
   }
 
+  @SuppressWarnings("parameterNumber")
   private OmKeyInfo commitS3MultipartUpload(String volumeName,
       String bucketName, String keyName, String multipartUploadID,
       String fileName, String multipartKey,
-      OmMultipartKeyInfo omMultipartKeyInfo) throws IOException {
+      OmMultipartKeyInfo omMultipartKeyInfo,
+      int deleteEntryCount) throws IOException {
 
     PartKeyInfo part1 = createPartKeyInfoFSO(volumeName, bucketName, parentID,
         fileName, 1);
@@ -223,7 +270,7 @@ public class TestS3MultipartUploadCompleteResponseWithFSO
     omMetadataManager.getStore().commitBatchOperation(batchOperation);
 
     // As 1 parts are created, so 1 entry should be there in delete table.
-    Assert.assertEquals(1, omMetadataManager.countRowsInTable(
+    Assert.assertEquals(deleteEntryCount, omMetadataManager.countRowsInTable(
         omMetadataManager.getDeletedTable()));
 
     String part1DeletedKeyName =

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

Reply via email to