-
Notifications
You must be signed in to change notification settings - Fork 588
HDDS-12560. Reclaimable Filter for Snaphost Garbage Collections #8053
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
7327b47
bd5b0c6
f1c85fd
43ab7b7
51c88f1
b901166
690eae9
865f3a5
5743edb
abcfaff
a2127a4
9f6d2a0
9d6bae3
7d785ab
e1d2317
93ba939
3935bf8
6d638a0
681bb3d
8fd746c
b69182e
d32bcf0
f8a2b6e
f323e3e
29a26d2
1ebba73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| /* | ||
| * 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.util; | ||
|
|
||
| /** | ||
| * | ||
| * Represents a function that accepts one argument and produces a result. | ||
| * This is a functional interface whose functional method is apply(Object). | ||
| * Type parameters: | ||
| * <T> – the type of the input to the function | ||
| * <R> – the type of the result of the function | ||
| * <E> - the type of exception thrown. | ||
| */ | ||
| public interface CheckedFunction<T, R, E extends Exception> { | ||
|
||
| R apply(T t) throws E; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,235 @@ | ||||||||||
| /* | ||||||||||
| * 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.snapshot.filter; | ||||||||||
|
|
||||||||||
| import com.google.common.annotations.VisibleForTesting; | ||||||||||
| import com.google.common.collect.Lists; | ||||||||||
| import java.io.Closeable; | ||||||||||
| import java.io.IOException; | ||||||||||
| import java.util.ArrayList; | ||||||||||
| import java.util.Collections; | ||||||||||
| import java.util.List; | ||||||||||
| import java.util.UUID; | ||||||||||
| import java.util.stream.Collectors; | ||||||||||
| import org.apache.hadoop.hdds.utils.IOUtils; | ||||||||||
| import org.apache.hadoop.hdds.utils.db.Table; | ||||||||||
| import org.apache.hadoop.ozone.om.KeyManager; | ||||||||||
| import org.apache.hadoop.ozone.om.OmSnapshot; | ||||||||||
| import org.apache.hadoop.ozone.om.OmSnapshotManager; | ||||||||||
| import org.apache.hadoop.ozone.om.OzoneManager; | ||||||||||
| import org.apache.hadoop.ozone.om.SnapshotChainManager; | ||||||||||
| import org.apache.hadoop.ozone.om.helpers.OmBucketInfo; | ||||||||||
| import org.apache.hadoop.ozone.om.helpers.SnapshotInfo; | ||||||||||
| import org.apache.hadoop.ozone.om.lock.IOzoneManagerLock; | ||||||||||
| import org.apache.hadoop.ozone.om.lock.OzoneManagerLock; | ||||||||||
| import org.apache.hadoop.ozone.om.snapshot.MultiSnapshotLocks; | ||||||||||
| import org.apache.hadoop.ozone.om.snapshot.ReferenceCounted; | ||||||||||
| import org.apache.hadoop.ozone.om.snapshot.SnapshotUtils; | ||||||||||
| import org.apache.hadoop.ozone.util.CheckedFunction; | ||||||||||
| import org.slf4j.Logger; | ||||||||||
| import org.slf4j.LoggerFactory; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * This class is responsible for opening last N snapshot given a snapshot metadata manager or AOS metadata manager by | ||||||||||
hemantk-12 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
| * acquiring a lock. | ||||||||||
| */ | ||||||||||
| public abstract class ReclaimableFilter<V> implements CheckedFunction<Table.KeyValue<String, V>, | ||||||||||
| Boolean, IOException>, Closeable { | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| private static final Logger LOG = LoggerFactory.getLogger(ReclaimableFilter.class); | ||||||||||
|
|
||||||||||
| private final OzoneManager ozoneManager; | ||||||||||
| private final SnapshotInfo currentSnapshotInfo; | ||||||||||
| private final OmSnapshotManager omSnapshotManager; | ||||||||||
| private final SnapshotChainManager snapshotChainManager; | ||||||||||
|
|
||||||||||
| private final List<SnapshotInfo> previousSnapshotInfos; | ||||||||||
| private final List<ReferenceCounted<OmSnapshot>> previousOmSnapshots; | ||||||||||
| private final MultiSnapshotLocks snapshotIdLocks; | ||||||||||
| private Long volumeId; | ||||||||||
| private OmBucketInfo bucketInfo; | ||||||||||
| private final KeyManager keyManager; | ||||||||||
| private final int numberOfPreviousSnapshotsFromChain; | ||||||||||
|
|
||||||||||
| /** | ||||||||||
| * Filter to return deleted keys/directories which are reclaimable based on their presence in previous snapshot in | ||||||||||
| * the snapshot chain. | ||||||||||
| * | ||||||||||
| * @param currentSnapshotInfo : If null the deleted keys in AOS needs to be processed, hence the latest snapshot | ||||||||||
| * in the snapshot chain corresponding to bucket key needs to be processed. | ||||||||||
| * @param keyManager : KeyManager corresponding to snapshot or AOS. | ||||||||||
| * @param lock : Lock for Active OM. | ||||||||||
|
||||||||||
| */ | ||||||||||
| public ReclaimableFilter(OzoneManager ozoneManager, OmSnapshotManager omSnapshotManager, | ||||||||||
| SnapshotChainManager snapshotChainManager, | ||||||||||
|
||||||||||
| SnapshotInfo currentSnapshotInfo, KeyManager keyManager, | ||||||||||
| IOzoneManagerLock lock, | ||||||||||
| int numberOfPreviousSnapshotsFromChain) { | ||||||||||
| this.ozoneManager = ozoneManager; | ||||||||||
| this.omSnapshotManager = omSnapshotManager; | ||||||||||
| this.currentSnapshotInfo = currentSnapshotInfo; | ||||||||||
| this.snapshotChainManager = snapshotChainManager; | ||||||||||
| this.snapshotIdLocks = new MultiSnapshotLocks(lock, OzoneManagerLock.Resource.SNAPSHOT_GC_LOCK, false); | ||||||||||
| this.keyManager = keyManager; | ||||||||||
| this.numberOfPreviousSnapshotsFromChain = numberOfPreviousSnapshotsFromChain; | ||||||||||
| this.previousOmSnapshots = new ArrayList<>(numberOfPreviousSnapshotsFromChain); | ||||||||||
| this.previousSnapshotInfos = new ArrayList<>(numberOfPreviousSnapshotsFromChain); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private List<SnapshotInfo> getLastNSnapshotInChain(String volume, String bucket) throws IOException { | ||||||||||
| if (currentSnapshotInfo != null && | ||||||||||
| (!currentSnapshotInfo.getVolumeName().equals(volume) || !currentSnapshotInfo.getBucketName().equals(bucket))) { | ||||||||||
| throw new IOException("Volume & Bucket name for snapshot : " + currentSnapshotInfo + " not matching for " + | ||||||||||
| "key in volume: " + volume + " bucket: " + bucket); | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| } | ||||||||||
| SnapshotInfo expectedPreviousSnapshotInfo = currentSnapshotInfo == null | ||||||||||
| ? SnapshotUtils.getLatestSnapshotInfo(volume, bucket, ozoneManager, snapshotChainManager) | ||||||||||
| : SnapshotUtils.getPreviousSnapshot(ozoneManager, snapshotChainManager, currentSnapshotInfo); | ||||||||||
| List<SnapshotInfo> snapshotInfos = Lists.newArrayList(); | ||||||||||
| SnapshotInfo snapshotInfo = expectedPreviousSnapshotInfo; | ||||||||||
| while (snapshotInfos.size() < numberOfPreviousSnapshotsFromChain) { | ||||||||||
| // If changes made to the snapshot have not been flushed to disk, throw exception immediately, next run of | ||||||||||
| // garbage collection would process the snapshot. | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| if (!OmSnapshotManager.areSnapshotChangesFlushedToDB(ozoneManager.getMetadataManager(), snapshotInfo)) { | ||||||||||
| throw new IOException("Changes made to the snapshot " + snapshotInfo + " have not been flushed to the disk "); | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| } | ||||||||||
| snapshotInfos.add(snapshotInfo); | ||||||||||
| snapshotInfo = snapshotInfo == null ? null | ||||||||||
| : SnapshotUtils.getPreviousSnapshot(ozoneManager, snapshotChainManager, snapshotInfo); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Reversing list to get the correct order in chain. To ensure locking order is as per the chain ordering. | ||||||||||
| Collections.reverse(snapshotInfos); | ||||||||||
|
||||||||||
| return snapshotInfos; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private boolean validateExistingLastNSnapshotsInChain(String volume, String bucket) throws IOException { | ||||||||||
| List<SnapshotInfo> expectedLastNSnapshotsInChain = getLastNSnapshotInChain(volume, bucket); | ||||||||||
| List<UUID> expectedSnapshotIds = expectedLastNSnapshotsInChain.stream() | ||||||||||
| .map(snapshotInfo -> snapshotInfo == null ? null : snapshotInfo.getSnapshotId()) | ||||||||||
| .collect(Collectors.toList()); | ||||||||||
| List<UUID> existingSnapshotIds = previousOmSnapshots.stream() | ||||||||||
| .map(omSnapshotReferenceCounted -> omSnapshotReferenceCounted == null ? null : | ||||||||||
| omSnapshotReferenceCounted.get().getSnapshotID()).collect(Collectors.toList()); | ||||||||||
| return expectedSnapshotIds.equals(existingSnapshotIds); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Initialize the last N snapshots in the chain by acquiring locks. Throw IOException if it fails. | ||||||||||
| private void initializePreviousSnapshotsFromChain(String volume, String bucket) throws IOException { | ||||||||||
| if (validateExistingLastNSnapshotsInChain(volume, bucket) && snapshotIdLocks.isLockAcquired()) { | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| return; | ||||||||||
| } | ||||||||||
| // If existing snapshotIds don't match then close all snapshots and reopen the previous N snapshots. | ||||||||||
| close(); | ||||||||||
| try { | ||||||||||
| // Acquire lock on last N snapshot & current snapshot(AOS if it is null). | ||||||||||
| List<SnapshotInfo> expectedLastNSnapshotsInChain = getLastNSnapshotInChain(volume, bucket); | ||||||||||
| List<UUID> lockIds = expectedLastNSnapshotsInChain.stream() | ||||||||||
| .map(snapshotInfo -> snapshotInfo == null ? null : snapshotInfo.getSnapshotId()) | ||||||||||
| .collect(Collectors.toList()); | ||||||||||
| //currentSnapshotInfo for AOS will be null. | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| lockIds.add(currentSnapshotInfo == null ? null : currentSnapshotInfo.getSnapshotId()); | ||||||||||
|
|
||||||||||
| if (snapshotIdLocks.acquireLock(lockIds).isLockAcquired()) { | ||||||||||
| for (SnapshotInfo snapshotInfo : expectedLastNSnapshotsInChain) { | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| if (snapshotInfo != null) { | ||||||||||
| // Fail operation if any of the previous snapshots are not active. | ||||||||||
| previousOmSnapshots.add(omSnapshotManager.getActiveSnapshot(snapshotInfo.getVolumeName(), | ||||||||||
| snapshotInfo.getBucketName(), snapshotInfo.getName())); | ||||||||||
| previousSnapshotInfos.add(snapshotInfo); | ||||||||||
| } else { | ||||||||||
| previousOmSnapshots.add(null); | ||||||||||
| previousSnapshotInfos.add(null); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // TODO: Getting volumeId and bucket from active OM. This would be wrong on volume & bucket renames | ||||||||||
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| // support. | ||||||||||
| bucketInfo = ozoneManager.getBucketInfo(volume, bucket); | ||||||||||
| volumeId = ozoneManager.getMetadataManager().getVolumeId(volume); | ||||||||||
| } | ||||||||||
| } else { | ||||||||||
| throw new IOException("Lock acquisition failed for last N snapshots : " + | ||||||||||
| expectedLastNSnapshotsInChain + " " + currentSnapshotInfo); | ||||||||||
|
||||||||||
| throw new IOException("Lock acquisition failed for last N snapshots : " + | |
| expectedLastNSnapshotsInChain + " " + currentSnapshotInfo); | |
| throw new IOException("Lock acquisition failed for last N snapshots: " + | |
| expectedLastNSnapshotsInChain + ", " + currentSnapshotInfo); |
qq: Is adding expectedLastNSnapshotsInChain to the log message useful?
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need to be closed explicitly here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We release the locks earlier the better right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReclaimableFilter is closeable. IMO, the caller should decide when to release the resource.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validateExistingLastNSnapshotsInChain is an expensive operation to run for every key in terms of Java GC. If I understand it correctly, it is to short-circuit the BG process, but at the cost of GC.
I think performing this check at both the beginning and the end of the iterator should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it shouldn't be it is just checking the snapshot chain. In memory operation should not be expensive.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| for (ReferenceCounted<OmSnapshot> previousOmSnapshot : previousOmSnapshots) { | |
| IOUtils.close(LOG, previousOmSnapshot); | |
| } | |
| IOUtils.close(LOG, previousOmSnapshots); |
IOUtils.close() has another implementation that takes a collection.
swamirishi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /* | ||
| * 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 containing filter to perform reclaimable check on snapshots. | ||
| */ | ||
| package org.apache.hadoop.ozone.om.snapshot.filter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: please remove this unnecessary line.