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

siyao 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 10aaa57472 HDDS-8530. [Snapshot] Fix for OM crash on restart due to 
snapshot chain manager corruption (#4677)
10aaa57472 is described below

commit 10aaa57472ed328334311049094470c4d657d857
Author: Hemant Kumar <[email protected]>
AuthorDate: Wed May 17 18:28:02 2023 -0700

    HDDS-8530. [Snapshot] Fix for OM crash on restart due to snapshot chain 
manager corruption (#4677)
---
 .../hadoop/ozone/om/helpers/SnapshotInfo.java      |  10 +-
 .../ozone/om/TestOzoneManagerHASnapshot.java       | 121 ++++++++
 .../src/main/proto/OmClientProtocol.proto          |   1 +
 .../apache/hadoop/ozone/om/OmSnapshotManager.java  |   6 +-
 .../hadoop/ozone/om/SnapshotChainManager.java      | 318 ++++++++++-----------
 .../request/snapshot/OMSnapshotCreateRequest.java  |   5 +-
 .../request/snapshot/OMSnapshotDeleteRequest.java  |   2 +-
 .../hadoop/ozone/om/TestOmSnapshotManager.java     |   9 +-
 .../ozone/om/request/OMRequestTestUtils.java       |   4 +-
 .../snapshot/TestOMSnapshotDeleteRequest.java      |   3 +-
 .../snapshot/TestOMSnapshotCreateResponse.java     |   4 +-
 .../snapshot/TestOMSnapshotDeleteResponse.java     |   4 +-
 12 files changed, 295 insertions(+), 192 deletions(-)

diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
index bc297a92fd..31f67fbdba 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/helpers/SnapshotInfo.java
@@ -27,8 +27,6 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Snapsho
 
 import com.google.common.base.Preconditions;
 
-import org.apache.hadoop.util.Time;
-
 import java.time.format.DateTimeFormatter;
 import java.time.Instant;
 import java.time.ZonedDateTime;
@@ -479,15 +477,15 @@ public final class SnapshotInfo implements Auditable {
   public static SnapshotInfo newInstance(String volumeName,
                                          String bucketName,
                                          String snapshotName,
-                                         String snapshotId) {
+                                         String snapshotId,
+                                         long creationTime) {
     SnapshotInfo.Builder builder = new SnapshotInfo.Builder();
-    long initialTime = Time.now();
     if (StringUtils.isBlank(snapshotName)) {
-      snapshotName = generateName(initialTime);
+      snapshotName = generateName(creationTime);
     }
     builder.setSnapshotID(snapshotId)
         .setName(snapshotName)
-        .setCreationTime(initialTime)
+        .setCreationTime(creationTime)
         .setDeletionTime(INVALID_TIMESTAMP)
         .setPathPreviousSnapshotID(INITIAL_SNAPSHOT_ID)
         .setGlobalPreviousSnapshotID(INITIAL_SNAPSHOT_ID)
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHASnapshot.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHASnapshot.java
new file mode 100644
index 0000000000..3f0267e845
--- /dev/null
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHASnapshot.java
@@ -0,0 +1,121 @@
+/*
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.ozone.om;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
+import org.apache.hadoop.ozone.client.OzoneBucket;
+import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests snapshot in OM HA setup.
+ */
+@Timeout(300)
+public class TestOzoneManagerHASnapshot extends TestOzoneManagerHA {
+
+  /**
+   * Test snapshotNames are unique among OM nodes when snapshotName is not
+   * passed or empty.
+   */
+  @Test
+  public void testSnapshotNameConsistency() throws Exception {
+    OzoneBucket ozoneBucket = setupBucket();
+    String volumeName = ozoneBucket.getVolumeName();
+    String bucketName = ozoneBucket.getName();
+
+    createKey(ozoneBucket);
+
+    getObjectStore().createSnapshot(volumeName, bucketName, "");
+    List<OzoneManager> ozoneManagers = getCluster().getOzoneManagersList();
+    List<String> snapshotNames = new ArrayList<>();
+
+    for (OzoneManager ozoneManager : ozoneManagers) {
+      await().atMost(Duration.ofSeconds(120))
+          .until(() -> {
+            String snapshotPrefix = OM_KEY_PREFIX + volumeName +
+                OM_KEY_PREFIX + bucketName;
+            SnapshotInfo snapshotInfo = null;
+            try (TableIterator<String, ?
+                extends Table.KeyValue<String, SnapshotInfo>>
+                     iterator = ozoneManager.getMetadataManager()
+                .getSnapshotInfoTable().iterator(snapshotPrefix)) {
+              while (iterator.hasNext()) {
+                snapshotInfo = iterator.next().getValue();
+              }
+            } catch (IOException e) {
+              throw new RuntimeException(e);
+            }
+
+            if (snapshotInfo != null) {
+              snapshotNames.add(snapshotInfo.getName());
+            }
+            return snapshotInfo != null;
+          });
+    }
+
+    assertEquals(1, snapshotNames.stream().distinct().count());
+    assertNotNull(snapshotNames.get(0));
+    assertTrue(snapshotNames.get(0).startsWith("s"));
+  }
+
+  @Test
+  public void testSnapshotChainManagerRestore() throws Exception {
+    List<OzoneBucket> ozoneBuckets = new ArrayList<>();
+    List<String> volumeNames = new ArrayList<>();
+    List<String> bucketNames = new ArrayList<>();
+
+    for (int i = 0; i < 10; i++) {
+      OzoneBucket ozoneBucket = setupBucket();
+      ozoneBuckets.add(ozoneBucket);
+      volumeNames.add(ozoneBucket.getVolumeName());
+      bucketNames.add(ozoneBucket.getName());
+    }
+
+    for (int i = 0; i < 100; i++) {
+      int index = i % 10;
+      createKey(ozoneBuckets.get(index));
+      String snapshot1 = "snapshot-" + RandomStringUtils.randomNumeric(5);
+      getObjectStore().createSnapshot(volumeNames.get(index),
+          bucketNames.get(index), snapshot1);
+    }
+
+    // Restart leader OM
+    OzoneManager omLeader = getCluster().getOMLeader();
+    getCluster().shutdownOzoneManager(omLeader);
+    getCluster().restartOzoneManager(omLeader, true);
+
+    await().atMost(Duration.ofSeconds(180))
+        .until(() -> getCluster().getOMLeader() != null);
+    assertNotNull(getCluster().getOMLeader());
+  }
+}
diff --git 
a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto 
b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
index 2cced1d0af..e115319e9d 100644
--- a/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
+++ b/hadoop-ozone/interface-client/src/main/proto/OmClientProtocol.proto
@@ -1687,6 +1687,7 @@ message CreateSnapshotRequest {
   optional string bucketName = 2;
   optional string snapshotName = 3;
   optional string snapshotId = 4;
+  optional uint64 creationTime = 5;
 }
 
 message ListSnapshotRequest {
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 3a870d1f07..03daef3dd1 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
@@ -401,8 +401,10 @@ public final class OmSnapshotManager implements 
AutoCloseable {
           .writeLock().unlock();
     }
 
-    LOG.info("Created checkpoint : {} for snapshot {}",
-        dbCheckpoint.getCheckpointLocation(), snapshotInfo.getName());
+    if (dbCheckpoint != null) {
+      LOG.info("Created checkpoint : {} for snapshot {}",
+          dbCheckpoint.getCheckpointLocation(), snapshotInfo.getName());
+    }
 
     final RocksDBCheckpointDiffer dbCpDiffer =
         store.getRocksDBCheckpointDiffer();
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
index 8dc6c703e0..c3ec6d1d61 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/SnapshotChainManager.java
@@ -33,26 +33,27 @@ import org.slf4j.LoggerFactory;
 
 /**
  * This class is used for creating and accessing Snapshot Chains.
- *
+ * <p>
  * The snapshot chain maintains the in-memory sequence of snapshots
  * created in chronological order.  There are two such snapshots maintained
  * i.) Path based snapshot chain, sequence of snapshots created for a
  * given /volume/bucket
  * ii.) Global snapshot chain, sequence of all snapshots created in order
- *
+ * <p>
  * On start, the snapshot chains are initialized from the on disk
  * SnapshotInfoTable from the om RocksDB.
  */
 public class SnapshotChainManager {
-  private LinkedHashMap<String, SnapshotChainInfo>  snapshotChainGlobal;
-  private Map<String, LinkedHashMap<String, SnapshotChainInfo>>
-      snapshotChainPath;
-  private Map<String, String> latestPathSnapshotID;
-  private String latestGlobalSnapshotID;
-  private Map<String, String> snapshotIdToTableKey;
   private static final Logger LOG =
       LoggerFactory.getLogger(SnapshotChainManager.class);
 
+  private final LinkedHashMap<String, SnapshotChainInfo>  snapshotChainGlobal;
+  private final Map<String, LinkedHashMap<String, SnapshotChainInfo>>
+      snapshotChainPath;
+  private String latestGlobalSnapshotID;
+  private final Map<String, String> latestPathSnapshotID;
+  private final Map<String, String> snapshotIdToTableKey;
+
   public SnapshotChainManager(OMMetadataManager metadataManager)
       throws IOException {
     snapshotChainGlobal = new LinkedHashMap<>();
@@ -83,10 +84,10 @@ public class SnapshotChainManager {
     }
     if (prevGlobalID != null &&
         !snapshotChainGlobal.containsKey(prevGlobalID)) {
-      throw new IOException("Snapshot Chain corruption: "
-          + "previous snapshotID given but no associated snapshot "
-          + "found in snapshot chain: SnapshotID "
-          + prevGlobalID);
+      throw new IOException(String.format("Snapshot chain corruption. " +
+          "Previous snapshotId: %s is set for snapshotId: %s but no " +
+          "associated snapshot found in snapshot chain.", prevGlobalID,
+          snapshotID));
     }
     snapshotChainGlobal.put(snapshotID,
         new SnapshotChainInfo(snapshotID, prevGlobalID, null));
@@ -115,10 +116,10 @@ public class SnapshotChainManager {
         (!snapshotChainPath
             .get(snapshotPath)
             .containsKey(prevPathID)))) {
-      throw new IOException("Snapshot Chain corruption: "
-          + "previous snapshotID given but no associated snapshot "
-          + "found in snapshot chain: SnapshotID "
-          + prevPathID);
+      throw new IOException(String.format("Snapshot chain corruption. " +
+              "Previous snapshotId: %s is set for snapshotId: %s but no " +
+              "associated snapshot found in snapshot chain.", prevPathID,
+          snapshotID));
     }
 
     if (prevPathID != null &&
@@ -151,14 +152,16 @@ public class SnapshotChainManager {
       String prev = 
snapshotChainGlobal.get(snapshotID).getPreviousSnapshotID();
 
       if (prev != null && !snapshotChainGlobal.containsKey(prev)) {
-        throw new IOException("Snapshot chain corruption: snapshot node to be "
-            + "deleted has prev node element not found in snapshot chain: "
-            + "SnapshotID " + prev);
+        throw new IOException(String.format("Snapshot chain corruption. " +
+                "SnapshotId: %s to be deleted has previous snapshotId: %s " +
+                "but associated snapshot is not found in snapshot chain.",
+            snapshotID, prev));
       }
       if (next != null && !snapshotChainGlobal.containsKey(next)) {
-        throw new IOException("Snapshot chain corruption: snapshot node to be "
-            + "deleted has next node element not found in snapshot chain: "
-            + "SnapshotID " + next);
+        throw new IOException(String.format("Snapshot chain corruption. " +
+                "SnapshotId: {%s} to be deleted has next snapshotId: %s " +
+                "but associated snapshot is not found in snapshot chain.",
+            snapshotID, next));
       }
       snapshotChainGlobal.remove(snapshotID);
       if (next != null) {
@@ -173,8 +176,8 @@ public class SnapshotChainManager {
       }
     } else {
       // snapshotID not found in snapshot chain, log warning and return
-      LOG.warn("Snapshot chain: snapshotID not found: SnapshotID {}",
-          snapshotID);
+      LOG.warn("Snapshot chain corruption. SnapshotID: {} is not found in " +
+          "snapshot chain.", snapshotID);
       status = false;
     }
 
@@ -183,53 +186,47 @@ public class SnapshotChainManager {
 
   private boolean deleteSnapshotPath(String snapshotPath,
                                      String snapshotID) throws IOException {
-    boolean status = true;
     if (snapshotChainPath.containsKey(snapshotPath) &&
-        snapshotChainPath
-            .get(snapshotPath)
-            .containsKey(snapshotID)) {
+        snapshotChainPath.get(snapshotPath).containsKey(snapshotID)) {
       // reset prev and next snapshot entries in chain ordered list
       // for node removal
-      String next = snapshotChainPath
+      String nextSnapshotID = snapshotChainPath
           .get(snapshotPath)
           .get(snapshotID)
           .getNextSnapshotID();
-      String prev = snapshotChainPath
+      String previousSnapshotID = snapshotChainPath
           .get(snapshotPath)
           .get(snapshotID)
           .getPreviousSnapshotID();
 
-      if (prev != null &&
-          !snapshotChainPath
-              .get(snapshotPath)
-              .containsKey(prev)) {
-        throw new IOException("Snapshot chain corruption: snapshot node to "
-            + "be deleted has prev node element not found in snapshot "
-            + "chain: Snapshot path " + snapshotPath + ", SnapshotID "
-            + prev);
+      if (previousSnapshotID != null &&
+          !snapshotChainPath.get(snapshotPath)
+              .containsKey(previousSnapshotID)) {
+        throw new IOException(String.format("Snapshot chain corruption. " +
+                "SnapshotId: %s at snapshotPath: %s to be deleted has " +
+                "previous snapshotId: %s but associated snapshot is not " +
+                "found in snapshot chain.", snapshotID, snapshotPath,
+            previousSnapshotID));
       }
-      if (next != null && !snapshotChainPath
-          .get(snapshotPath)
-          .containsKey(next)) {
-        throw new IOException("Snapshot chain corruption: snapshot node to "
-            + "be deleted has next node element not found in snapshot "
-            + "chain:  Snapshot path " + snapshotPath + ", SnapshotID "
-            + next);
+      if (nextSnapshotID != null && !snapshotChainPath.get(snapshotPath)
+          .containsKey(nextSnapshotID)) {
+        throw new IOException(String.format("Snapshot chain corruption. " +
+                "SnapshotId: %s at snapshotPath: %s to be deleted has next " +
+                "snapshotId: %s but associated snapshot is not found in " +
+                "snapshot chain.", snapshotID, snapshotPath,
+            nextSnapshotID));
       }
-      snapshotChainPath
-          .get(snapshotPath)
-          .remove(snapshotID);
-      if (next != null) {
-        snapshotChainPath
-            .get(snapshotPath)
-            .get(next)
-            .setPreviousSnapshotID(prev);
+
+      snapshotChainPath.get(snapshotPath).remove(snapshotID);
+      if (nextSnapshotID != null) {
+        snapshotChainPath.get(snapshotPath)
+            .get(nextSnapshotID)
+            .setPreviousSnapshotID(previousSnapshotID);
       }
-      if (prev != null) {
-        snapshotChainPath
-            .get(snapshotPath)
-            .get(prev)
-            .setNextSnapshotID(next);
+      if (previousSnapshotID != null) {
+        snapshotChainPath.get(snapshotPath)
+            .get(previousSnapshotID)
+            .setNextSnapshotID(nextSnapshotID);
       }
       // remove path if no entries
       if (snapshotChainPath.get(snapshotPath).isEmpty()) {
@@ -238,20 +235,17 @@ public class SnapshotChainManager {
       // remove from latest list if necessary
       if (latestPathSnapshotID.get(snapshotPath).equals(snapshotID)) {
         latestPathSnapshotID.remove(snapshotPath);
-        if (prev != null) {
-          latestPathSnapshotID.put(snapshotPath, prev);
+        if (previousSnapshotID != null) {
+          latestPathSnapshotID.put(snapshotPath, previousSnapshotID);
         }
       }
-
+      return true;
     } else {
       // snapshotID not found in snapshot chain, log warning and return
-      LOG.warn("Snapshot chain: snapshotID not found: Snapshot path {}," +
-              " SnapshotID {}",
-          snapshotPath, snapshotID);
-      status = false;
+      LOG.warn("Snapshot chain corruption. SnapshotId: {} is not in chain " +
+          "found for snapshot path {}.", snapshotID, snapshotPath);
+      return false;
     }
-
-    return status;
   }
 
   /**
@@ -259,7 +253,7 @@ public class SnapshotChainManager {
    * @param metadataManager OMMetadataManager
    */
   private void loadFromSnapshotInfoTable(OMMetadataManager metadataManager)
-          throws IOException {
+      throws IOException {
     // read from snapshotInfo table to populate
     // snapshot chains - both global and local path
     try (TableIterator<String, ? extends Table.KeyValue<String, SnapshotInfo>>
@@ -275,38 +269,37 @@ public class SnapshotChainManager {
         kv = keyIter.next();
         snaps.put(kv.getValue().getCreationTime(), kv.getValue());
       }
-      for (SnapshotInfo sinfo : snaps.values()) {
-        addSnapshot(sinfo);
+      for (SnapshotInfo snapshotInfo : snaps.values()) {
+        addSnapshot(snapshotInfo);
       }
     }
   }
 
   /**
    * Add snapshot to snapshot chain.
-   * @param sinfo SnapshotInfo of snapshot to add to chain.
+   * @param snapshotInfo SnapshotInfo of snapshot to add to chain.
    */
-  public void addSnapshot(SnapshotInfo sinfo) throws IOException {
-    addSnapshotGlobal(sinfo.getSnapshotID(),
-        sinfo.getGlobalPreviousSnapshotID());
-    addSnapshotPath(sinfo.getSnapshotPath(),
-        sinfo.getSnapshotID(),
-        sinfo.getPathPreviousSnapshotID());
+  public void addSnapshot(SnapshotInfo snapshotInfo) throws IOException {
+    addSnapshotGlobal(snapshotInfo.getSnapshotID(),
+        snapshotInfo.getGlobalPreviousSnapshotID());
+    addSnapshotPath(snapshotInfo.getSnapshotPath(),
+        snapshotInfo.getSnapshotID(), 
snapshotInfo.getPathPreviousSnapshotID());
     // store snapshot ID to snapshot DB table key in the map
-    snapshotIdToTableKey.put(sinfo.getSnapshotID(), sinfo.getTableKey());
+    snapshotIdToTableKey.put(snapshotInfo.getSnapshotID(),
+        snapshotInfo.getTableKey());
   }
 
   /**
    * Delete snapshot from snapshot chain.
-   * @param sinfo SnapshotInfo of snapshot to remove from chain.
+   * @param snapshotInfo SnapshotInfo of snapshot to remove from chain.
    * @return boolean
    */
-  public boolean deleteSnapshot(SnapshotInfo sinfo) throws IOException {
-    boolean status;
-
-    status = deleteSnapshotGlobal(sinfo.getSnapshotID()) &&
-        deleteSnapshotPath(sinfo.getSnapshotPath(), sinfo.getSnapshotID());
+  public boolean deleteSnapshot(SnapshotInfo snapshotInfo) throws IOException {
+    boolean status = deleteSnapshotGlobal(snapshotInfo.getSnapshotID()) &&
+        deleteSnapshotPath(snapshotInfo.getSnapshotPath(),
+            snapshotInfo.getSnapshotID());
     if (status) {
-      snapshotIdToTableKey.remove(sinfo.getSnapshotID());
+      snapshotIdToTableKey.remove(snapshotInfo.getSnapshotID());
     }
     return status;
   }
@@ -336,19 +329,13 @@ public class SnapshotChainManager {
    * @param snapshotID String, snapshot UUID
    * @return boolean
    */
-  public boolean hasNextGlobalSnapshot(String snapshotID)
-          throws NoSuchElementException {
-    boolean hasNext = false;
+  public boolean hasNextGlobalSnapshot(String snapshotID) {
     if (!snapshotChainGlobal.containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotID {}", snapshotID);
-      throw new NoSuchElementException("No snapshot: " + snapshotID);
-    }
-    if (snapshotChainGlobal
-        .get(snapshotID)
-        .getNextSnapshotID() != null) {
-      hasNext = true;
+      LOG.error("No snapshot for provided snapshotId: {}", snapshotID);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+          "found in snapshot chain.", snapshotID));
     }
-    return hasNext;
+    return snapshotChainGlobal.get(snapshotID).getNextSnapshotID() != null;
   }
 
   /**
@@ -357,16 +344,14 @@ public class SnapshotChainManager {
    * @return String, snapshot UUID of next snapshot in chain from
    * snapshotID
    */
-  public String nextGlobalSnapshot(String snapshotID)
-          throws NoSuchElementException {
+  public String nextGlobalSnapshot(String snapshotID) {
     if (!hasNextGlobalSnapshot(snapshotID)) {
-      LOG.error("no following snapshot for provided snapshotID {}", 
snapshotID);
-      throw new NoSuchElementException("no following snapshot from: "
-          + snapshotID);
+      LOG.error("No following snapshot found in snapshot chain for provided " +
+              "snapshotId: {}.", snapshotID);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+          "found in snapshot chain.", snapshotID));
     }
-    return snapshotChainGlobal
-        .get(snapshotID)
-        .getNextSnapshotID();
+    return snapshotChainGlobal.get(snapshotID).getNextSnapshotID();
   }
 
   /**
@@ -375,19 +360,15 @@ public class SnapshotChainManager {
    * @param snapshotID String, snapshot UUID
    * @return boolean
    */
-  public boolean hasPreviousGlobalSnapshot(String snapshotID)
-          throws NoSuchElementException {
-    boolean hasPrevious = false;
+  public boolean hasPreviousGlobalSnapshot(String snapshotID) {
     if (!snapshotChainGlobal.containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotID {}", snapshotID);
-      throw new NoSuchElementException("No snapshot: " + snapshotID);
+      LOG.error("No snapshot found in snapshot chain for provided " +
+          "snapshotId: {}.", snapshotID);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+          "found in snapshot chain.", snapshotID));
     }
-    if (snapshotChainGlobal
-        .get(snapshotID)
-        .getPreviousSnapshotID() != null) {
-      hasPrevious = true;
-    }
-    return hasPrevious;
+
+    return snapshotChainGlobal.get(snapshotID).getPreviousSnapshotID() != null;
   }
 
   /**
@@ -396,17 +377,14 @@ public class SnapshotChainManager {
    * @return String, snapshot UUID of previous snapshot in chain from
    * snapshotID
    */
-  public String previousGlobalSnapshot(String snapshotID)
-      throws NoSuchElementException {
+  public String previousGlobalSnapshot(String snapshotID) {
     if (!hasPreviousGlobalSnapshot(snapshotID)) {
-      LOG.error("no preceeding snapshot for provided snapshotID {}",
-          snapshotID);
-      throw new NoSuchElementException("No preceeding snapshot from: "
-          + snapshotID);
+      LOG.error("No preceding snapshot found in snapshot chain for provided " +
+          "snapshotId: {}.", snapshotID);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+          "found in snapshot chain.", snapshotID));
     }
-    return snapshotChainGlobal
-        .get(snapshotID)
-        .getPreviousSnapshotID();
+    return snapshotChainGlobal.get(snapshotID).getPreviousSnapshotID();
   }
 
   /**
@@ -416,23 +394,20 @@ public class SnapshotChainManager {
    * @param snapshotID String, snapshot UUID
    * @return boolean
    */
-  public boolean hasNextPathSnapshot(String snapshotPath, String snapshotID)
-          throws NoSuchElementException {
-    boolean hasNext = false;
+  public boolean hasNextPathSnapshot(String snapshotPath, String snapshotID) {
     if (!snapshotChainPath.containsKey(snapshotPath) ||
         !snapshotChainPath.get(snapshotPath).containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotPath {} and "
-          + " snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("No such snapshot: "
-          + snapshotID + "for path: " + snapshotPath);
+      LOG.error("No snapshot found for provided snapshotId: {} and " +
+          "snapshotPath: {}", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+              "found in snapshot chain for snapshotPath: %s.", snapshotID,
+          snapshotPath));
     }
-    if (snapshotChainPath
+
+    return snapshotChainPath
         .get(snapshotPath)
         .get(snapshotID)
-        .getNextSnapshotID() != null) {
-      hasNext = true;
-    }
-    return hasNext;
+        .getNextSnapshotID() != null;
   }
 
   /**
@@ -442,23 +417,19 @@ public class SnapshotChainManager {
    * @param snapshotID String, snapshot UUID
    * @return boolean
    */
-  public boolean hasPreviousPathSnapshot(String snapshotPath, String 
snapshotID)
-      throws NoSuchElementException {
-    boolean hasPrevious = false;
+  public boolean hasPreviousPathSnapshot(String snapshotPath,
+                                         String snapshotID) {
     if (!snapshotChainPath.containsKey(snapshotPath) ||
         !snapshotChainPath.get(snapshotPath).containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotPath {} and "
-          + " snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("No snapshot: " + snapshotID
-          + " for snapshot path: " + snapshotPath);
+      LOG.error("No snapshot found for provided snapshotId: {} and " +
+          "snapshotPath: {}", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+              "found in snapshot chain for snapshotPath: %s.", snapshotID,
+          snapshotPath));
     }
-    if (snapshotChainPath
-        .get(snapshotPath)
+    return snapshotChainPath.get(snapshotPath)
         .get(snapshotID)
-        .getPreviousSnapshotID() != null) {
-      hasPrevious = true;
-    }
-    return hasPrevious;
+        .getPreviousSnapshotID() != null;
   }
 
   /**
@@ -468,26 +439,24 @@ public class SnapshotChainManager {
    * @return String, snapshot UUID of next snapshot in chain from
    * snapshotID
    */
-  public String nextPathSnapshot(String snapshotPath, String snapshotID)
-      throws NoSuchElementException {
+  public String nextPathSnapshot(String snapshotPath, String snapshotID) {
     if (!snapshotChainPath.containsKey(snapshotPath) ||
         !snapshotChainPath.get(snapshotPath).containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotPath {} and "
-          + " snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("No snapshot: " + snapshotID
-          + " for snapshot path:" + snapshotPath);
+      LOG.error("No snapshot found for provided snapshotId: {} and " +
+          "snapshotPath: {}", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+              "found in snapshot chain for snapshotPath: %s.", snapshotID,
+          snapshotPath));
     }
-    if (snapshotChainPath
-        .get(snapshotPath)
-        .get(snapshotID)
+    if (snapshotChainPath.get(snapshotPath).get(snapshotID)
         .getNextSnapshotID() == null) {
-      LOG.error("no following snapshot for provided snapshotPath {}, "
-          + "snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("no following snapshot from: "
-          + snapshotID + "for snapshot path:" + snapshotPath);
+      LOG.error("No following snapshot for provided snapshotId {} and " +
+          "snapshotPath {}.", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("No following snapshot " +
+          "found in snapshot chain for snapshotId: %s and snapshotPath: " +
+          "%s.", snapshotID, snapshotPath));
     }
-    return snapshotChainPath
-        .get(snapshotPath)
+    return snapshotChainPath.get(snapshotPath)
         .get(snapshotID)
         .getNextSnapshotID();
   }
@@ -499,23 +468,24 @@ public class SnapshotChainManager {
    * @return String, snapshot UUID of previous snapshot in chain from
    * snapshotID
    */
-  public String previousPathSnapshot(String snapshotPath, String snapshotID)
-      throws NoSuchElementException {
+  public String previousPathSnapshot(String snapshotPath, String snapshotID) {
     if (!snapshotChainPath.containsKey(snapshotPath) ||
         !snapshotChainPath.get(snapshotPath).containsKey(snapshotID)) {
-      LOG.error("no snapshot for provided snapshotPath {} and "
-          + " snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("No snapshot: " + snapshotID
-          + " for snapshot path:" + snapshotPath);
+      LOG.error("No snapshot found for provided snapshotId: {} and " +
+          "snapshotPath: {}", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("SnapshotId: %s is not " +
+              "found in snapshot chain for snapshotPath: %s.", snapshotID,
+          snapshotPath));
     }
     if (snapshotChainPath
         .get(snapshotPath)
         .get(snapshotID)
         .getPreviousSnapshotID() == null) {
-      LOG.error("no preceeding snapshot for provided snapshotPath {}, "
-          + "snapshotID {}", snapshotPath, snapshotID);
-      throw new NoSuchElementException("no preceeding snapshot from: "
-          + snapshotID + "for snapshot path:" + snapshotPath);
+      LOG.error("No preceding snapshot for provided snapshotId: {} and " +
+          "snapshotPath: {}", snapshotID, snapshotPath);
+      throw new NoSuchElementException(String.format("No preceding snapshot " +
+              "found in snapshot chain for snapshotId: %s and snapshotPath: " +
+              "%s.", snapshotID, snapshotPath));
     }
     return snapshotChainPath
         .get(snapshotPath)
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java
index 0bed12118d..c92fa8f939 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotCreateRequest.java
@@ -45,6 +45,7 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRespo
 import org.apache.hadoop.ozone.security.acl.IAccessAuthorizer;
 import org.apache.hadoop.ozone.security.acl.OzoneObj;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.util.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -82,7 +83,8 @@ public class OMSnapshotCreateRequest extends OMClientRequest {
     snapshotInfo = SnapshotInfo.newInstance(volumeName,
         bucketName,
         possibleName,
-        snapshotId);
+        snapshotId,
+        createSnapshotRequest.getCreationTime());
     snapshotName = snapshotInfo.getName();
     snapshotPath = snapshotInfo.getSnapshotPath();
   }
@@ -107,6 +109,7 @@ public class OMSnapshotCreateRequest extends 
OMClientRequest {
     return omRequest.toBuilder().setCreateSnapshotRequest(
         omRequest.getCreateSnapshotRequest().toBuilder()
             .setSnapshotId(UUID.randomUUID().toString())
+            .setCreationTime(Time.now())
             .build()).build();
   }
   
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java
index 33ec087617..cf2481d2c2 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotDeleteRequest.java
@@ -208,7 +208,7 @@ public class OMSnapshotDeleteRequest extends 
OMClientRequest {
     if (snapshotInfo == null) {
       // Dummy SnapshotInfo for logging and audit logging when erred
       snapshotInfo = SnapshotInfo.newInstance(volumeName, bucketName,
-          snapshotName, null);
+          snapshotName, null, Time.now());
     }
 
     // Perform audit logging outside the lock
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshotManager.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshotManager.java
index cd1589ee63..bbc2dd6b5a 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshotManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshotManager.java
@@ -30,6 +30,7 @@ import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
 import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
 import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.ozone.om.snapshot.OmSnapshotUtils;
+import org.apache.hadoop.util.Time;
 import org.apache.ozone.test.GenericTestUtils;
 import org.junit.After;
 import org.junit.Assert;
@@ -222,8 +223,10 @@ public class TestOmSnapshotManager {
       String volumeName, String bucketName) {
     String snapshotName = UUID.randomUUID().toString();
     String snapshotId = UUID.randomUUID().toString();
-    return SnapshotInfo.newInstance(
-        volumeName, bucketName, snapshotName, snapshotId);
+    return SnapshotInfo.newInstance(volumeName,
+        bucketName,
+        snapshotName,
+        snapshotId,
+        Time.now());
   }
-
 }
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
index e5b0271a4b..1dac4ac364 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/OMRequestTestUtils.java
@@ -365,7 +365,7 @@ public final class OMRequestTestUtils {
       String volumeName, String bucketName, String snapshotName,
       OMMetadataManager omMetadataManager) throws IOException {
     SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(volumeName,
-        bucketName, snapshotName, UUID.randomUUID().toString());
+        bucketName, snapshotName, UUID.randomUUID().toString(), Time.now());
     addSnapshotToTable(false, 0L, snapshotInfo, omMetadataManager);
   }
 
@@ -376,7 +376,7 @@ public final class OMRequestTestUtils {
       String volumeName, String bucketName, String snapshotName,
       OMMetadataManager omMetadataManager) throws IOException {
     SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(volumeName, 
bucketName,
-        snapshotName, UUID.randomUUID().toString());
+        snapshotName, UUID.randomUUID().toString(), Time.now());
     addSnapshotToTable(true, 0L, snapshotInfo, omMetadataManager);
   }
 
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java
index ae28a9cd31..e09eb26806 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotDeleteRequest.java
@@ -41,6 +41,7 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMReque
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.apache.hadoop.util.Time;
 import org.apache.ozone.test.LambdaTestUtils;
 import org.junit.After;
 import org.junit.Assert;
@@ -206,7 +207,7 @@ public class TestOMSnapshotDeleteRequest {
 
     // add key to cache
     SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(
-        volumeName, bucketName, snapshotName, null);
+        volumeName, bucketName, snapshotName, null, Time.now());
     Assert.assertEquals(SnapshotInfo.SnapshotStatus.SNAPSHOT_ACTIVE,
         snapshotInfo.getSnapshotStatus());
     omMetadataManager.getSnapshotInfoTable().addCacheEntry(
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 8ec9e2713c..d50a1539e1 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
@@ -33,6 +33,7 @@ import org.apache.hadoop.ozone.om.OmSnapshotManager;
 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;
@@ -92,7 +93,8 @@ public class TestOMSnapshotCreateResponse {
     SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(volumeName,
         bucketName,
         snapshotName,
-        snapshotId);
+        snapshotId,
+        Time.now());
 
     // confirm table is empty
     Assert.assertEquals(0,
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotDeleteResponse.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotDeleteResponse.java
index 296636ed51..a568bf6db6 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotDeleteResponse.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/response/snapshot/TestOMSnapshotDeleteResponse.java
@@ -34,6 +34,7 @@ import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DeleteS
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Status;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
+import org.apache.hadoop.util.Time;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -85,7 +86,8 @@ public class TestOMSnapshotDeleteResponse {
     SnapshotInfo snapshotInfo = SnapshotInfo.newInstance(volumeName,
         bucketName,
         snapshotName,
-        snapshotId);
+        snapshotId,
+        Time.now());
 
     // confirm table is empty
     Assert.assertEquals(0,


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


Reply via email to