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

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


The following commit(s) were added to refs/heads/master by this push:
     new 33140cc3da HDDS-9432. Perform individual delete key instead of delete 
range function (#5454)
33140cc3da is described below

commit 33140cc3dab284c7f830770c22b438d0dbb3a076
Author: Swaminathan Balachandran <[email protected]>
AuthorDate: Wed Oct 18 17:22:25 2023 -0700

    HDDS-9432. Perform individual delete key instead of delete range function 
(#5454)
    
    * HDDS-9432. Perform individual delete key instead of delete range function
    
    * HDDS-9432. Fix issue
    
    * HDDS-9432. Fix checkstyle issue
    
    * HDDS-9432. Address review comments
    
    * HDDS-9432. Add test cases
    
    * HDDS-9432. Fix test case
    
    * HDDS-9432. Address review comment
    
    * HDDS-9432. Address review comment
    
    * HDDS-9432. Fix checkstyle
    
    * HDDS-9432. Fix javadocs
---
 .../hadoop/hdds/utils/ThrowableFunction.java       |  27 ++++++
 .../apache/hadoop/ozone/om/OmSnapshotManager.java  | 100 +++++++++------------
 .../snapshot/TestOMSnapshotCreateResponse.java     |  80 +++++++++--------
 3 files changed, 113 insertions(+), 94 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/ThrowableFunction.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/ThrowableFunction.java
new file mode 100644
index 0000000000..d6dbb6871d
--- /dev/null
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/ThrowableFunction.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hdds.utils;
+
+
+/**
+ * Utility interface for function which throws exceptions.
+ * Similar to {@link java.util.function.Function}.
+ */
+public interface ThrowableFunction<T, R, E extends Throwable> {
+  R apply(T t) throws E;
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java
index 4ec14e1596..d88bd13c0d 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java
@@ -40,6 +40,7 @@ import com.google.common.cache.RemovalListener;
 import org.apache.hadoop.hdds.StringUtils;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.server.ServerUtils;
+import org.apache.hadoop.hdds.utils.ThrowableFunction;
 import org.apache.hadoop.hdds.utils.db.CodecRegistry;
 import org.apache.hadoop.hdds.utils.db.DBCheckpoint;
 import org.apache.hadoop.hdds.utils.db.RDBStore;
@@ -473,24 +474,24 @@ public final class OmSnapshotManager implements 
AutoCloseable {
    * @param bucketName bucket name
    */
   private static void deleteKeysFromDelDirTableInSnapshotScope(
-      OMMetadataManager omMetadataManager,
-      String volumeName,
+      OMMetadataManager omMetadataManager, String volumeName,
       String bucketName) throws IOException {
 
     // Range delete start key (inclusive)
-    final String beginKey = getOzonePathKeyWithVolumeBucketNames(
+    final String keyPrefix = getOzonePathKeyWithVolumeBucketNames(
         omMetadataManager, volumeName, bucketName);
-    // Range delete end key (exclusive). To be calculated
-    String endKey;
 
     try (TableIterator<String, ? extends Table.KeyValue<String, OmKeyInfo>>
-         iter = omMetadataManager.getDeletedDirTable().iterator()) {
-      endKey = findEndKeyGivenPrefix(iter, beginKey);
+         iter = omMetadataManager.getDeletedDirTable().iterator(keyPrefix)) {
+      performOperationOnKeys(iter,
+          entry -> {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Removing key {} from DeletedDirTable", 
entry.getKey());
+            }
+            omMetadataManager.getDeletedDirTable().delete(entry.getKey());
+            return null;
+          });
     }
-
-    // Clean up deletedDirectoryTable
-    deleteRangeInclusive(omMetadataManager.getDeletedDirTable(),
-        beginKey, endKey);
   }
 
   /**
@@ -530,52 +531,37 @@ public final class OmSnapshotManager implements 
AutoCloseable {
   }
 
   /**
-   * Helper method to locate the end key with the given prefix and iterator.
+   * Helper method to perform operation on keys with a given iterator.
    * @param keyIter TableIterator
-   * @param keyPrefix DB key prefix String
-   * @return endKey String, or null if no keys with such prefix is found
+   * @param operationFunction operation to be performed for each key.
    */
-  private static String findEndKeyGivenPrefix(
+  private static void performOperationOnKeys(
       TableIterator<String, ? extends Table.KeyValue<String, ?>> keyIter,
-      String keyPrefix) throws IOException {
-
-    String endKey;
-    keyIter.seek(keyPrefix);
+      ThrowableFunction<Table.KeyValue<String, ?>,
+      Void, IOException> operationFunction) throws IOException {
     // Continue only when there are entries of snapshot (bucket) scope
     // in deletedTable in the first place
-    if (!keyIter.hasNext()) {
-      // No key matching keyPrefix. No need to do delete or deleteRange at all.
-      endKey = null;
-    } else {
-      // Remember the last key with a matching prefix
-      endKey = keyIter.next().getKey();
-
-      // Loop until prefix mismatches.
-      // TODO: [SNAPSHOT] Try to seek to next predicted bucket name instead of
-      //  the while-loop for a potential speed up?
-      // Start performance tracking timer
-      long startTime = System.nanoTime();
-      while (keyIter.hasNext()) {
-        Table.KeyValue<String, ?> entry = keyIter.next();
-        String dbKey = entry.getKey();
-        if (dbKey.startsWith(keyPrefix)) {
-          endKey = dbKey;
-        }
-      }
-      // Time took for the iterator to finish (in ns)
-      long timeElapsed = System.nanoTime() - startTime;
-      if (timeElapsed >= DB_TABLE_ITER_LOOP_THRESHOLD_NS) {
-        // Print time elapsed
-        LOG.warn("Took {} ns to find endKey. Caller is {}", timeElapsed,
-            new Throwable().fillInStackTrace().getStackTrace()[1]
-                .getMethodName());
-      }
+    // Loop until prefix matches.
+    // Start performance tracking timer
+    long startTime = System.nanoTime();
+    while (keyIter.hasNext()) {
+      Table.KeyValue<String, ?> entry = keyIter.next();
+      operationFunction.apply(entry);
+    }
+    // Time took for the iterator to finish (in ns)
+    long timeElapsed = System.nanoTime() - startTime;
+    if (timeElapsed >= DB_TABLE_ITER_LOOP_THRESHOLD_NS) {
+      // Print time elapsed
+      LOG.warn("Took {} ns to find endKey. Caller is {}", timeElapsed,
+          new Throwable().fillInStackTrace().getStackTrace()[1]
+              .getMethodName());
     }
-    return endKey;
   }
 
   /**
    * Helper method to do deleteRange on a table, including endKey.
+   * TODO: Do remove this method, it is not used anywhere. Need to check if
+   *       deleteRange causes RocksDB corruption.
    * TODO: Move this into {@link Table} ?
    * @param table Table
    * @param beginKey begin key
@@ -600,25 +586,25 @@ public final class OmSnapshotManager implements 
AutoCloseable {
    * @param bucketName bucket name
    */
   private static void deleteKeysFromDelKeyTableInSnapshotScope(
-      OMMetadataManager omMetadataManager,
-      String volumeName,
+      OMMetadataManager omMetadataManager, String volumeName,
       String bucketName) throws IOException {
 
     // Range delete start key (inclusive)
-    final String beginKey =
+    final String keyPrefix =
         omMetadataManager.getOzoneKey(volumeName, bucketName, "");
-    // Range delete end key (exclusive). To be found
-    String endKey;
 
     try (TableIterator<String,
         ? extends Table.KeyValue<String, RepeatedOmKeyInfo>>
-             iter = omMetadataManager.getDeletedTable().iterator()) {
-      endKey = findEndKeyGivenPrefix(iter, beginKey);
+             iter = omMetadataManager.getDeletedTable().iterator(keyPrefix)) {
+      performOperationOnKeys(iter, entry -> {
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Removing key {} from DeletedTable", entry.getKey());
+        }
+        omMetadataManager.getDeletedTable().delete(entry.getKey());
+        return null;
+      });
     }
 
-    // Clean up deletedTable
-    deleteRangeInclusive(omMetadataManager.getDeletedTable(), beginKey, 
endKey);
-
     // No need to invalidate deletedTable (or deletedDirectoryTable) table
     // cache since entries are not added to its table cache in the first place.
     // See OMKeyDeleteRequest and OMKeyPurgeRequest#validateAndUpdateCache.
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotCreateResponse.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotCreateResponse.java
index bd551ab5c2..e837a17360 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotCreateResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotCreateResponse.java
@@ -34,12 +34,12 @@ import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.RepeatedOmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.util.Time;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.io.TempDir;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
@@ -60,32 +60,33 @@ import static 
org.apache.hadoop.ozone.om.OmSnapshotManager.getSnapshotPath;
  */
 public class TestOMSnapshotCreateResponse {
 
-  @Rule
-  public TemporaryFolder folder = new TemporaryFolder();
-  
+  @TempDir
+  private File folder;
+
   private OMMetadataManager omMetadataManager;
   private BatchOperation batchOperation;
   private OzoneConfiguration ozoneConfiguration;
 
-  @Before
+  @BeforeEach
   public void setup() throws Exception {
     ozoneConfiguration = new OzoneConfiguration();
-    String fsPath = folder.newFolder().getAbsolutePath();
+    String fsPath = folder.getAbsolutePath();
     ozoneConfiguration.set(OMConfigKeys.OZONE_OM_DB_DIRS,
         fsPath);
     omMetadataManager = new OmMetadataManagerImpl(ozoneConfiguration, null);
     batchOperation = omMetadataManager.getStore().initBatchOperation();
   }
 
-  @After
+  @AfterEach
   public void tearDown() {
     if (batchOperation != null) {
       batchOperation.close();
     }
   }
 
-  @Test
-  public void testAddToDBBatch() throws Exception {
+  @ParameterizedTest
+  @ValueSource(ints = {0, 1, 5, 10, 25})
+  public void testAddToDBBatch(int numberOfKeys) throws Exception {
     String volumeName = UUID.randomUUID().toString();
     String bucketName = UUID.randomUUID().toString();
     String snapshotName = UUID.randomUUID().toString();
@@ -97,15 +98,15 @@ public class TestOMSnapshotCreateResponse {
         Time.now());
 
     // confirm table is empty
-    Assert.assertEquals(0,
+    Assertions.assertEquals(0,
         omMetadataManager
-        .countRowsInTable(omMetadataManager.getSnapshotInfoTable()));
+            .countRowsInTable(omMetadataManager.getSnapshotInfoTable()));
 
     // Populate deletedTable and deletedDirectoryTable
     Set<String> dtSentinelKeys =
-        addTestKeysToDeletedTable(volumeName, bucketName);
+        addTestKeysToDeletedTable(volumeName, bucketName, numberOfKeys);
     Set<String> ddtSentinelKeys =
-        addTestKeysToDeletedDirTable(volumeName, bucketName);
+        addTestKeysToDeletedDirTable(volumeName, bucketName, numberOfKeys);
 
     // commit to table
     OMSnapshotCreateResponse omSnapshotCreateResponse =
@@ -114,17 +115,17 @@ public class TestOMSnapshotCreateResponse {
             .setStatus(OzoneManagerProtocolProtos.Status.OK)
             .setCreateSnapshotResponse(
                 CreateSnapshotResponse.newBuilder()
-                .setSnapshotInfo(snapshotInfo.getProtobuf())
-                .build()).build(), snapshotInfo);
+                    .setSnapshotInfo(snapshotInfo.getProtobuf())
+                    .build()).build(), snapshotInfo);
     omSnapshotCreateResponse.addToDBBatch(omMetadataManager, batchOperation);
     omMetadataManager.getStore().commitBatchOperation(batchOperation);
 
     // Confirm snapshot directory was created
     String snapshotDir = getSnapshotPath(ozoneConfiguration, snapshotInfo);
-    Assert.assertTrue((new File(snapshotDir)).exists());
+    Assertions.assertTrue((new File(snapshotDir)).exists());
 
     // Confirm table has 1 entry
-    Assert.assertEquals(1, omMetadataManager
+    Assertions.assertEquals(1, omMetadataManager
         .countRowsInTable(omMetadataManager.getSnapshotInfoTable()));
 
     // Check contents of entry
@@ -133,17 +134,19 @@ public class TestOMSnapshotCreateResponse {
              it = omMetadataManager.getSnapshotInfoTable().iterator()) {
       Table.KeyValue<String, SnapshotInfo> keyValue = it.next();
       storedInfo = keyValue.getValue();
-      Assert.assertEquals(snapshotInfo.getTableKey(), keyValue.getKey());
+      Assertions.assertEquals(snapshotInfo.getTableKey(), keyValue.getKey());
     }
-    Assert.assertEquals(snapshotInfo, storedInfo);
+    Assertions.assertEquals(snapshotInfo, storedInfo);
 
     // Check deletedTable and deletedDirectoryTable clean up work as expected
     verifyEntriesLeftInDeletedTable(dtSentinelKeys);
     verifyEntriesLeftInDeletedDirTable(ddtSentinelKeys);
   }
 
-  private Set<String> addTestKeysToDeletedTable(
-      String volumeName, String bucketName) throws IOException {
+  private Set<String> addTestKeysToDeletedTable(String volumeName,
+                                                String bucketName,
+                                                int numberOfKeys)
+      throws IOException {
 
     RepeatedOmKeyInfo dummyRepeatedKeyInfo = new RepeatedOmKeyInfo.Builder()
         .setOmKeyInfos(new ArrayList<>()).build();
@@ -176,7 +179,7 @@ public class TestOMSnapshotCreateResponse {
     }
 
     // Add deletedTable key entries in the snapshot (bucket) scope
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < numberOfKeys; i++) {
       String dtKey = omMetadataManager.getOzoneKey(volumeName, bucketName,
           "dtkey" + i);
       omMetadataManager.getDeletedTable().put(dtKey, dummyRepeatedKeyInfo);
@@ -189,12 +192,15 @@ public class TestOMSnapshotCreateResponse {
 
   /**
    * Populates deletedDirectoryTable for the test.
+   *
    * @param volumeName volume name
    * @param bucketName bucket name
    * @return A set of DB keys
    */
-  private Set<String> addTestKeysToDeletedDirTable(
-      String volumeName, String bucketName) throws IOException {
+  private Set<String> addTestKeysToDeletedDirTable(String volumeName,
+                                                   String bucketName,
+                                                   int numberOfKeys)
+      throws IOException {
 
     OMSnapshotResponseTestUtil.addVolumeBucketInfoToTable(
         omMetadataManager, volumeName, bucketName);
@@ -218,7 +224,7 @@ public class TestOMSnapshotCreateResponse {
 
     char bucketIdLastChar = dbKeyPfx.charAt(offset);
 
-    String dbKeyPfxBefore =  dbKeyPfx.substring(0, offset) +
+    String dbKeyPfxBefore = dbKeyPfx.substring(0, offset) +
         (char) (bucketIdLastChar - 1) + dbKeyPfx.substring(offset);
     for (int i = 0; i < 3; i++) {
       String dtKey = dbKeyPfxBefore + "dir" + i;
@@ -226,7 +232,7 @@ public class TestOMSnapshotCreateResponse {
       sentinelKeys.add(dtKey);
     }
 
-    String dbKeyPfxAfter =  dbKeyPfx.substring(0, offset) +
+    String dbKeyPfxAfter = dbKeyPfx.substring(0, offset) +
         (char) (bucketIdLastChar + 1) + dbKeyPfx.substring(offset);
     for (int i = 0; i < 3; i++) {
       String dtKey = dbKeyPfxAfter + "dir" + i;
@@ -235,7 +241,7 @@ public class TestOMSnapshotCreateResponse {
     }
 
     // Add key entries in the snapshot (bucket) scope
-    for (int i = 0; i < 10; i++) {
+    for (int i = 0; i < numberOfKeys; i++) {
       String dtKey = dbKeyPfx + "dir" + i;
       omMetadataManager.getDeletedDirTable().put(dtKey, dummyOmKeyInfo);
       // These are the keys that should be deleted.
@@ -261,18 +267,18 @@ public class TestOMSnapshotCreateResponse {
       Table<String, ?> table, Set<String> expectedKeys) throws IOException {
 
     try (TableIterator<String, ? extends Table.KeyValue<String, ?>>
-        keyIter = table.iterator()) {
+             keyIter = table.iterator()) {
       keyIter.seekToFirst();
       while (keyIter.hasNext()) {
         Table.KeyValue<String, ?> entry = keyIter.next();
         String dbKey = entry.getKey();
-        Assert.assertTrue(table.getName() + " should contain key",
-            expectedKeys.contains(dbKey));
+        Assertions.assertTrue(expectedKeys.contains(dbKey),
+            table.getName() + " should contain key");
         expectedKeys.remove(dbKey);
       }
     }
 
-    Assert.assertTrue(table.getName() + " is missing keys that should be 
there",
-        expectedKeys.isEmpty());
+    Assertions.assertTrue(expectedKeys.isEmpty(),
+        table.getName() + " is missing keys that should be there");
   }
 }


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

Reply via email to