From f4678a62e17b401a69b69cb7b55cb45acf0c9c93 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Fri, 18 Aug 2023 11:53:23 -0700 Subject: [PATCH 1/2] Moved SnapshotChain update out of OMSnapshotPurgeResponse to OMSnapshotPurgeRequest. --- .../hadoop/ozone/om/helpers/SnapshotInfo.java | 32 ++++-- .../snapshot/OMSnapshotPurgeRequest.java | 101 +++++++++++++++++- .../snapshot/OMSnapshotPurgeResponse.java | 101 +++--------------- 3 files changed, 141 insertions(+), 93 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 fcd28ce32949..bdb642d8fbae 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 @@ -21,6 +21,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.hdds.utils.db.Codec; +import org.apache.hadoop.hdds.utils.db.CopyObject; import org.apache.hadoop.hdds.utils.db.DelegatedCodec; import org.apache.hadoop.hdds.utils.db.Proto2Codec; import org.apache.hadoop.ozone.OzoneConsts; @@ -52,15 +53,11 @@ * snapshot checkpoint directory, previous snapshotid * for the snapshot path & global amongst other necessary fields. */ -public final class SnapshotInfo implements Auditable { +public final class SnapshotInfo implements Auditable, CopyObject { private static final Codec CODEC = new DelegatedCodec<>( Proto2Codec.get(OzoneManagerProtocolProtos.SnapshotInfo.class), SnapshotInfo::getFromProtobuf, - SnapshotInfo::getProtobuf, - // FIXME: HDDS-8665 Deep copy will cause failures - // - TestOMSnapshotDeleteRequest NullPointerException - // - TestOMSnapshotPurgeRequestAndResponse AssertionFailedError - DelegatedCodec.CopyType.SHALLOW); + SnapshotInfo::getProtobuf); public static Codec getCodec() { return CODEC; @@ -599,4 +596,27 @@ public int hashCode() { creationTime, deletionTime, pathPreviousSnapshotId, globalPreviousSnapshotId, snapshotPath, checkpointDir); } + + /** + * Return a new copy of the object. + */ + @Override + public SnapshotInfo copyObject() { + return new Builder() + .setSnapshotId(snapshotId) + .setName(name) + .setVolumeName(volumeName) + .setBucketName(bucketName) + .setSnapshotStatus(snapshotStatus) + .setCreationTime(creationTime) + .setDeletionTime(deletionTime) + .setPathPreviousSnapshotId(pathPreviousSnapshotId) + .setGlobalPreviousSnapshotId(globalPreviousSnapshotId) + .setSnapshotPath(snapshotPath) + .setCheckpointDir(checkpointDir) + .setDbTxSequenceNumber(dbTxSequenceNumber) + .setDeepClean(deepClean) + .setSstFiltered(sstFiltered) + .build(); + } } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotPurgeRequest.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotPurgeRequest.java index 414ec0f67b96..43943d353db7 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotPurgeRequest.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotPurgeRequest.java @@ -40,6 +40,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; +import java.util.UUID; /** * Handles OMSnapshotPurge Request. @@ -73,6 +75,8 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, List snapInfosToUpdate = snapshotPurgeRequest .getUpdatedSnapshotDBKeyList(); Map updatedSnapInfos = new HashMap<>(); + Map updatedPathPreviousAndGlobalSnapshots = + new HashMap<>(); // Snapshots that are already deepCleaned by the KeyDeletingService // can be marked as deepCleaned. @@ -94,12 +98,16 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, SnapshotInfo nextSnapshot = SnapshotUtils .getNextActiveSnapshot(fromSnapshot, snapshotChainManager, omSnapshotManager); + updateSnapshotInfoAndCache(nextSnapshot, omMetadataManager, trxnLogIndex, updatedSnapInfos, true); + updateSnapshotChainAndCache(omMetadataManager, fromSnapshot, + trxnLogIndex, updatedPathPreviousAndGlobalSnapshots); } omClientResponse = new OMSnapshotPurgeResponse(omResponse.build(), - snapshotDbKeys, updatedSnapInfos); + snapshotDbKeys, updatedSnapInfos, + updatedPathPreviousAndGlobalSnapshots); } catch (IOException ex) { omClientResponse = new OMSnapshotPurgeResponse( createErrorOMResponse(omResponse, ex)); @@ -124,4 +132,95 @@ private void updateSnapshotInfoAndCache(SnapshotInfo snapInfo, updatedSnapInfos.put(snapInfo.getTableKey(), snapInfo); } } + + /** + * Removes the snapshot from the chain and updates the next snapshot's + * previousPath and previousGlobal IDs in DB cache. + * It also returns the pair of updated next path and global snapshots to + * update in DB. + */ + private void updateSnapshotChainAndCache( + OmMetadataManagerImpl metadataManager, + SnapshotInfo snapInfo, + long trxnLogIndex, + Map updatedPathPreviousAndGlobalSnapshots + ) throws IOException { + if (snapInfo == null) { + return; + } + + SnapshotChainManager snapshotChainManager = metadataManager + .getSnapshotChainManager(); + SnapshotInfo nextPathSnapInfo = null; + + // If the snapshot is deleted in the previous run, then the in-memory + // SnapshotChainManager might throw NoSuchElementException as the snapshot + // is removed in-memory but OMDoubleBuffer has not flushed yet. + boolean hasNextPathSnapshot; + boolean hasNextGlobalSnapshot; + try { + hasNextPathSnapshot = snapshotChainManager.hasNextPathSnapshot( + snapInfo.getSnapshotPath(), snapInfo.getSnapshotId()); + hasNextGlobalSnapshot = snapshotChainManager.hasNextGlobalSnapshot( + snapInfo.getSnapshotId()); + } catch (NoSuchElementException ex) { + return; + } + + // Updates next path snapshot's previous snapshot ID + if (hasNextPathSnapshot) { + UUID nextPathSnapshotId = snapshotChainManager.nextPathSnapshot( + snapInfo.getSnapshotPath(), snapInfo.getSnapshotId()); + + String snapshotTableKey = snapshotChainManager + .getTableKey(nextPathSnapshotId); + nextPathSnapInfo = metadataManager.getSnapshotInfoTable() + .get(snapshotTableKey); + if (nextPathSnapInfo != null) { + nextPathSnapInfo.setPathPreviousSnapshotId( + snapInfo.getPathPreviousSnapshotId()); + metadataManager.getSnapshotInfoTable().addCacheEntry( + new CacheKey<>(nextPathSnapInfo.getTableKey()), + CacheValue.get(trxnLogIndex, nextPathSnapInfo)); + updatedPathPreviousAndGlobalSnapshots + .put(nextPathSnapInfo.getTableKey(), nextPathSnapInfo); + } + } + + // Updates next global snapshot's previous snapshot ID + if (hasNextGlobalSnapshot) { + UUID nextGlobalSnapshotId = + snapshotChainManager.nextGlobalSnapshot(snapInfo.getSnapshotId()); + + String snapshotTableKey = snapshotChainManager + .getTableKey(nextGlobalSnapshotId); + + SnapshotInfo nextGlobalSnapInfo = metadataManager.getSnapshotInfoTable() + .get(snapshotTableKey); + // If both next global and path snapshot are same, it may overwrite + // nextPathSnapInfo.setPathPreviousSnapshotID(), adding this check + // will prevent it. + if (nextGlobalSnapInfo != null && nextPathSnapInfo != null && + nextGlobalSnapInfo.getSnapshotId().equals( + nextPathSnapInfo.getSnapshotId())) { + nextPathSnapInfo.setGlobalPreviousSnapshotId( + snapInfo.getGlobalPreviousSnapshotId()); + metadataManager.getSnapshotInfoTable().addCacheEntry( + new CacheKey<>(nextPathSnapInfo.getTableKey()), + CacheValue.get(trxnLogIndex, nextPathSnapInfo)); + updatedPathPreviousAndGlobalSnapshots + .put(nextPathSnapInfo.getTableKey(), nextPathSnapInfo); + } else if (nextGlobalSnapInfo != null) { + nextGlobalSnapInfo.setGlobalPreviousSnapshotId( + snapInfo.getGlobalPreviousSnapshotId()); + metadataManager.getSnapshotInfoTable().addCacheEntry( + new CacheKey<>(nextGlobalSnapInfo.getTableKey()), + CacheValue.get(trxnLogIndex, nextGlobalSnapInfo)); + updatedPathPreviousAndGlobalSnapshots + .put(nextGlobalSnapInfo.getTableKey(), nextGlobalSnapInfo); + } + } + + snapshotChainManager.deleteSnapshot(snapInfo); + } } diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotPurgeResponse.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotPurgeResponse.java index abc68a4cfc95..863bf5f62b8b 100644 --- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotPurgeResponse.java +++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/response/snapshot/OMSnapshotPurgeResponse.java @@ -23,7 +23,6 @@ import org.apache.hadoop.hdds.utils.db.RDBStore; import org.apache.hadoop.ozone.om.OMMetadataManager; import org.apache.hadoop.ozone.om.OmMetadataManagerImpl; -import org.apache.hadoop.ozone.om.SnapshotChainManager; import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; import org.apache.hadoop.ozone.om.response.CleanupTableInfo; import org.apache.hadoop.ozone.om.response.OMClientResponse; @@ -37,8 +36,6 @@ import java.nio.file.Paths; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; -import java.util.UUID; import static org.apache.hadoop.ozone.om.OmMetadataManagerImpl.SNAPSHOT_INFO_TABLE; @@ -51,13 +48,18 @@ public class OMSnapshotPurgeResponse extends OMClientResponse { LoggerFactory.getLogger(OMSnapshotPurgeResponse.class); private final List snapshotDbKeys; private final Map updatedSnapInfos; + private final Map updatedPreviousAndGlobalSnapInfos; - public OMSnapshotPurgeResponse(@Nonnull OMResponse omResponse, + public OMSnapshotPurgeResponse( + @Nonnull OMResponse omResponse, @Nonnull List snapshotDbKeys, - Map updatedSnapInfos) { + Map updatedSnapInfos, + Map updatedPreviousAndGlobalSnapInfos + ) { super(omResponse); this.snapshotDbKeys = snapshotDbKeys; this.updatedSnapInfos = updatedSnapInfos; + this.updatedPreviousAndGlobalSnapInfos = updatedPreviousAndGlobalSnapInfos; } /** @@ -69,6 +71,7 @@ public OMSnapshotPurgeResponse(@Nonnull OMResponse omResponse) { checkStatusNotOK(); this.snapshotDbKeys = null; this.updatedSnapInfos = null; + this.updatedPreviousAndGlobalSnapInfos = null; } @Override @@ -77,7 +80,9 @@ protected void addToDBBatch(OMMetadataManager omMetadataManager, OmMetadataManagerImpl metadataManager = (OmMetadataManagerImpl) omMetadataManager; - updateSnapInfo(metadataManager, batchOperation); + updateSnapInfo(metadataManager, batchOperation, updatedSnapInfos); + updateSnapInfo(metadataManager, batchOperation, + updatedPreviousAndGlobalSnapInfos); for (String dbKey: snapshotDbKeys) { SnapshotInfo snapshotInfo = omMetadataManager .getSnapshotInfoTable().get(dbKey); @@ -88,7 +93,7 @@ protected void addToDBBatch(OMMetadataManager omMetadataManager, if (snapshotInfo == null) { continue; } - cleanupSnapshotChain(metadataManager, snapshotInfo, batchOperation); + // Delete Snapshot checkpoint directory. deleteCheckpointDirectory(omMetadataManager, snapshotInfo); omMetadataManager.getSnapshotInfoTable().deleteWithBatch(batchOperation, @@ -97,91 +102,15 @@ protected void addToDBBatch(OMMetadataManager omMetadataManager, } private void updateSnapInfo(OmMetadataManagerImpl metadataManager, - BatchOperation batchOp) + BatchOperation batchOp, + Map snapshotInfos) throws IOException { - for (Map.Entry entry : updatedSnapInfos.entrySet()) { + for (Map.Entry entry : snapshotInfos.entrySet()) { metadataManager.getSnapshotInfoTable().putWithBatch(batchOp, entry.getKey(), entry.getValue()); } } - /** - * Cleans up the snapshot chain and updates next snapshot's - * previousPath and previousGlobal IDs. - */ - private void cleanupSnapshotChain(OmMetadataManagerImpl metadataManager, - SnapshotInfo snapInfo, - BatchOperation batchOperation) - throws IOException { - SnapshotChainManager snapshotChainManager = metadataManager - .getSnapshotChainManager(); - SnapshotInfo nextPathSnapInfo = null; - SnapshotInfo nextGlobalSnapInfo; - - // If the snapshot is deleted in the previous run, then the in-memory - // SnapshotChainManager might throw NoSuchElementException as the snapshot - // is removed in-memory but OMDoubleBuffer has not flushed yet. - boolean hasNextPathSnapshot; - boolean hasNextGlobalSnapshot; - try { - hasNextPathSnapshot = snapshotChainManager.hasNextPathSnapshot( - snapInfo.getSnapshotPath(), snapInfo.getSnapshotId()); - hasNextGlobalSnapshot = snapshotChainManager.hasNextGlobalSnapshot( - snapInfo.getSnapshotId()); - } catch (NoSuchElementException ex) { - LOG.warn("The Snapshot {} could have been deleted in the previous run.", - snapInfo.getSnapshotId(), ex); - return; - } - - // Updates next path snapshot's previous snapshot ID - if (hasNextPathSnapshot) { - UUID nextPathSnapshotId = snapshotChainManager.nextPathSnapshot( - snapInfo.getSnapshotPath(), snapInfo.getSnapshotId()); - - String snapshotTableKey = snapshotChainManager - .getTableKey(nextPathSnapshotId); - nextPathSnapInfo = metadataManager.getSnapshotInfoTable() - .get(snapshotTableKey); - if (nextPathSnapInfo != null) { - nextPathSnapInfo.setPathPreviousSnapshotId( - snapInfo.getPathPreviousSnapshotId()); - metadataManager.getSnapshotInfoTable().putWithBatch(batchOperation, - nextPathSnapInfo.getTableKey(), nextPathSnapInfo); - } - } - - // Updates next global snapshot's previous snapshot ID - if (hasNextGlobalSnapshot) { - UUID nextGlobalSnapshotId = - snapshotChainManager.nextGlobalSnapshot(snapInfo.getSnapshotId()); - - String snapshotTableKey = snapshotChainManager - .getTableKey(nextGlobalSnapshotId); - nextGlobalSnapInfo = metadataManager.getSnapshotInfoTable() - .get(snapshotTableKey); - // If both next global and path snapshot are same, it may overwrite - // nextPathSnapInfo.setPathPreviousSnapshotID(), adding this check - // will prevent it. - if (nextGlobalSnapInfo != null && nextPathSnapInfo != null && - nextGlobalSnapInfo.getSnapshotId().equals( - nextPathSnapInfo.getSnapshotId())) { - nextPathSnapInfo.setGlobalPreviousSnapshotId( - snapInfo.getGlobalPreviousSnapshotId()); - metadataManager.getSnapshotInfoTable().putWithBatch(batchOperation, - nextPathSnapInfo.getTableKey(), nextPathSnapInfo); - } else if (nextGlobalSnapInfo != null) { - nextGlobalSnapInfo.setGlobalPreviousSnapshotId( - snapInfo.getGlobalPreviousSnapshotId()); - metadataManager.getSnapshotInfoTable().putWithBatch(batchOperation, - nextGlobalSnapInfo.getTableKey(), nextGlobalSnapInfo); - } - } - - // Removes current snapshot from the snapshot chain. - snapshotChainManager.deleteSnapshot(snapInfo); - } - /** * Deletes the checkpoint directory for a snapshot. */ From 1c7b2199d5bd9fdfa497956f7cb44dc1737099a6 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Fri, 18 Aug 2023 16:55:23 -0700 Subject: [PATCH 2/2] Updated snapshot chain test by purging multiple snapshots --- .../hadoop/ozone/om/SnapshotChainManager.java | 12 ++ ...TestOMSnapshotPurgeRequestAndResponse.java | 137 +++++++++++------- 2 files changed, 98 insertions(+), 51 deletions(-) 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 e4f66b6512f3..b47d851519f4 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 @@ -16,6 +16,7 @@ */ package org.apache.hadoop.ozone.om; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.hdds.utils.db.Table; import org.apache.hadoop.hdds.utils.db.TableIterator; import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; @@ -430,4 +431,15 @@ public LinkedHashMap getSnapshotChainPath( String path) { return snapshotChainByPath.get(path); } + + @VisibleForTesting + public Map getGlobalSnapshotChain() { + return globalSnapshotChain; + } + + @VisibleForTesting + public Map> getSnapshotChainByPath() { + return snapshotChainByPath; + } } diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotPurgeRequestAndResponse.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotPurgeRequestAndResponse.java index fc54bc0d89d3..06a30e1469b5 100644 --- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotPurgeRequestAndResponse.java +++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/snapshot/TestOMSnapshotPurgeRequestAndResponse.java @@ -58,7 +58,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -259,6 +258,8 @@ public void testValidateAndUpdateCache() throws Exception { } } + // TODO: clean up: Do we this test after + // testSnapshotChainInSnapshotInfoTableAfterSnapshotPurge? @ParameterizedTest @ValueSource(ints = {0, 1, 2, 3, 4}) public void testSnapshotChainCleanup(int index) throws Exception { @@ -331,49 +332,72 @@ public void testSnapshotChainCleanup(int index) throws Exception { private static Stream snapshotPurgeCases() { return Stream.of( - Arguments.of(0, true), - Arguments.of(1, true), - Arguments.of(2, true), - Arguments.of(3, true), - Arguments.of(4, true), - Arguments.of(5, true), - Arguments.of(6, true), - Arguments.of(7, true), - Arguments.of(8, true), - Arguments.of(0, false), - Arguments.of(1, false), - Arguments.of(2, false), - Arguments.of(3, false), - Arguments.of(4, false), - Arguments.of(5, false), - Arguments.of(6, false), - Arguments.of(7, false), - Arguments.of(8, false) + Arguments.of("Single bucket: purge first snapshot.", + 1, 5, 0, 0, true), + Arguments.of("Single bucket: purge snapshot at index 2.", + 1, 5, 2, 2, true), + Arguments.of("Single bucket: purge snapshots from index 1 to 3.", + 1, 5, 1, 3, true), + Arguments.of("Single bucket: purge last snapshot.", + 1, 5, 4, 4, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge first snapshot.", 3, 5, 0, 0, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge first 5 snapshots.", 3, 5, 0, 4, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge snapshot at index 7.", 3, 5, 7, 7, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge snapshots from index 5 to 9.", 3, 5, 5, 9, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge snapshots from index 3 to 12.", 3, 5, 3, 12, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge last 5 snapshots.", 3, 5, 10, 14, true), + Arguments.of("Multiple buckets (keys are created in bucket order): " + + "purge last snapshot.", 3, 5, 14, 14, true), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge first snapshot.", 3, 5, 0, 0, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge first 5 snapshots.", 3, 5, 0, 5, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge snapshot at index 7.", 3, 5, 7, 7, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge snapshots from index 5 to 9.", 3, 5, 5, 9, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge snapshots from index 3 to 12.", 3, 5, 3, 12, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge last 5 snapshots.", 3, 5, 10, 14, false), + Arguments.of("Multiple buckets (keys are not created in bucket " + + "order): purge last snapshot.", 3, 5, 14, 14, false) ); } - @ParameterizedTest + @ParameterizedTest(name = "{0}") @MethodSource("snapshotPurgeCases") public void testSnapshotChainInSnapshotInfoTableAfterSnapshotPurge( - int purgeIndex, + String description, + int numberOfBuckets, + int numberOfKeysPerBucket, + int fromIndex, + int toIndex, boolean createInBucketOrder) throws Exception { - List buckets = Arrays.asList( - "buck-1-" + UUID.randomUUID(), - "buck-2-" + UUID.randomUUID(), - "buck-3-" + UUID.randomUUID() - ); - - for (String bucket : buckets) { - OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucket, + SnapshotChainManager chainManager = + ((OmMetadataManagerImpl) omMetadataManager).getSnapshotChainManager(); + int totalKeys = numberOfBuckets * numberOfKeysPerBucket; + + List buckets = new ArrayList<>(); + for (int i = 0; i < numberOfBuckets; i++) { + String bucketNameLocal = "bucket-" + UUID.randomUUID(); + OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucketNameLocal, omMetadataManager); + buckets.add(bucketNameLocal); } List snapshotInfoList = new ArrayList<>(); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < numberOfBuckets; i++) { + for (int j = 0; j < numberOfKeysPerBucket; j++) { int bucketIndex = createInBucketOrder ? i : j; - String bucket = buckets.get(bucketIndex % 3); + String bucket = buckets.get(bucketIndex % numberOfBuckets); String snapshotName = UUID.randomUUID().toString(); createSnapshotCheckpoint(volumeName, bucket, snapshotName); String snapshotTableKey = @@ -384,33 +408,44 @@ public void testSnapshotChainInSnapshotInfoTableAfterSnapshotPurge( } } - validateSnapshotOrderInSnapshotInfoTableAndSnapshotChain(snapshotInfoList); + long numberOfSnapshotBeforePurge = omMetadataManager + .countRowsInTable(omMetadataManager.getSnapshotInfoTable()); + assertEquals(totalKeys, numberOfSnapshotBeforePurge); + assertEquals(totalKeys, chainManager.getGlobalSnapshotChain().size()); - SnapshotInfo purgeSnapshotInfo = snapshotInfoList.get(purgeIndex); + validateSnapshotOrderInSnapshotInfoTableAndSnapshotChain(snapshotInfoList); - String purgeSnapshotKey = SnapshotInfo.getTableKey(volumeName, - purgeSnapshotInfo.getBucketName(), - purgeSnapshotInfo.getName()); + List purgeSnapshotKeys = new ArrayList<>(); + for (int i = fromIndex; i <= toIndex; i++) { + SnapshotInfo purgeSnapshotInfo = snapshotInfoList.get(i); + String purgeSnapshotKey = SnapshotInfo.getTableKey(volumeName, + purgeSnapshotInfo.getBucketName(), + purgeSnapshotInfo.getName()); + purgeSnapshotKeys.add(purgeSnapshotKey); + } - OMRequest snapshotPurgeRequest = createPurgeKeysRequest( - Collections.singletonList(purgeSnapshotKey)); + OMRequest snapshotPurgeRequest = createPurgeKeysRequest(purgeSnapshotKeys); purgeSnapshots(snapshotPurgeRequest); List snapshotInfoListAfterPurge = new ArrayList<>(); - for (int i = 0; i < 9; i++) { - if (i == purgeIndex) { - // Ignoring purgeIndex because snapshot at purgeIndex has been purged. - continue; + for (int i = 0; i < totalKeys; i++) { + if (i < fromIndex || i > toIndex) { + SnapshotInfo info = snapshotInfoList.get(i); + String snapshotKey = SnapshotInfo.getTableKey(volumeName, + info.getBucketName(), info.getName()); + snapshotInfoListAfterPurge.add( + omMetadataManager.getSnapshotInfoTable().get(snapshotKey)); } - - SnapshotInfo info = snapshotInfoList.get(i); - String snapshotKey = SnapshotInfo.getTableKey(volumeName, - info.getBucketName(), - info.getName()); - - snapshotInfoListAfterPurge.add( - omMetadataManager.getSnapshotInfoTable().get(snapshotKey)); } + + long expectNumberOfSnapshotAfterPurge = totalKeys - + (toIndex - fromIndex + 1); + long actualNumberOfSnapshotAfterPurge = omMetadataManager + .countRowsInTable(omMetadataManager.getSnapshotInfoTable()); + assertEquals(expectNumberOfSnapshotAfterPurge, + actualNumberOfSnapshotAfterPurge); + assertEquals(expectNumberOfSnapshotAfterPurge, chainManager + .getGlobalSnapshotChain().size()); validateSnapshotOrderInSnapshotInfoTableAndSnapshotChain( snapshotInfoListAfterPurge); }