Skip to content

Commit c35e99f

Browse files
authored
HDDS-10250. Use SnapshotId as key in SnapshotCache (apache#6139)
1 parent 7c79246 commit c35e99f

6 files changed

Lines changed: 113 additions & 114 deletions

File tree

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/OmSnapshotManager.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Set;
3636
import java.util.concurrent.TimeUnit;
3737
import java.util.stream.Collectors;
38+
import java.util.UUID;
3839

3940
import com.google.common.cache.RemovalListener;
4041
import org.apache.hadoop.hdds.StringUtils;
@@ -244,7 +245,7 @@ public OmSnapshotManager(OzoneManager ozoneManager) {
244245
OZONE_OM_SNAPSHOT_CACHE_MAX_SIZE,
245246
OZONE_OM_SNAPSHOT_CACHE_MAX_SIZE_DEFAULT);
246247

247-
CacheLoader<String, OmSnapshot> loader = createCacheLoader();
248+
CacheLoader<UUID, OmSnapshot> loader = createCacheLoader();
248249

249250
// TODO: [SNAPSHOT] Remove this if not going to make SnapshotCache impl
250251
// pluggable.
@@ -325,19 +326,25 @@ public boolean canDisableFsSnapshot(OMMetadataManager ommm) {
325326
return isSnapshotInfoTableEmpty;
326327
}
327328

328-
private CacheLoader<String, OmSnapshot> createCacheLoader() {
329-
return new CacheLoader<String, OmSnapshot>() {
329+
private CacheLoader<UUID, OmSnapshot> createCacheLoader() {
330+
return new CacheLoader<UUID, OmSnapshot>() {
330331

331332
@Nonnull
332333
@Override
333-
public OmSnapshot load(@Nonnull String snapshotTableKey)
334-
throws IOException {
335-
// Check if the snapshot exists
336-
final SnapshotInfo snapshotInfo = getSnapshotInfo(snapshotTableKey);
334+
public OmSnapshot load(@Nonnull UUID snapshotId) throws IOException {
335+
String snapshotTableKey = ((OmMetadataManagerImpl) ozoneManager.getMetadataManager())
336+
.getSnapshotChainManager()
337+
.getTableKey(snapshotId);
338+
339+
// SnapshotChain maintains in-memory reverse mapping of snapshotId to snapshotName based on snapshotInfoTable.
340+
// So it should not happen ideally.
341+
// If it happens, then either snapshot has been purged in between or SnapshotChain is corrupted
342+
// and missing some entries which needs investigation.
343+
if (snapshotTableKey == null) {
344+
throw new IOException("No snapshot exist with snapshotId: " + snapshotId);
345+
}
337346

338-
// Block snapshot from loading when it is no longer active e.g. DELETED,
339-
// unless this is called from SnapshotDeletingService.
340-
checkSnapshotActive(snapshotInfo, true);
347+
final SnapshotInfo snapshotInfo = getSnapshotInfo(snapshotTableKey);
341348

342349
CacheValue<SnapshotInfo> cacheValue = ozoneManager.getMetadataManager()
343350
.getSnapshotInfoTable()
@@ -417,9 +424,9 @@ public void invalidateCache() {
417424
/**
418425
* Immediately invalidate an entry.
419426
*
420-
* @param key DB snapshot table key
427+
* @param key SnapshotId.
421428
*/
422-
public void invalidateCacheEntry(String key) throws IOException {
429+
public void invalidateCacheEntry(UUID key) throws IOException {
423430
if (snapshotCache != null) {
424431
snapshotCache.invalidate(key);
425432
}
@@ -663,17 +670,16 @@ private ReferenceCounted<OmSnapshot> getSnapshot(
663670
return getSnapshot(snapshotTableKey, skipActiveCheck);
664671
}
665672

666-
private ReferenceCounted<OmSnapshot> getSnapshot(
667-
String snapshotTableKey,
668-
boolean skipActiveCheck) throws IOException {
669-
673+
private ReferenceCounted<OmSnapshot> getSnapshot(String snapshotTableKey, boolean skipActiveCheck)
674+
throws IOException {
675+
SnapshotInfo snapshotInfo = SnapshotUtils.getSnapshotInfo(ozoneManager, snapshotTableKey);
670676
// Block FS API reads when snapshot is not active.
671677
if (!skipActiveCheck) {
672-
checkSnapshotActive(ozoneManager, snapshotTableKey);
678+
checkSnapshotActive(snapshotInfo, false);
673679
}
674680

675681
// retrieve the snapshot from the cache
676-
return snapshotCache.get(snapshotTableKey);
682+
return snapshotCache.get(snapshotInfo.getSnapshotId());
677683
}
678684

679685
/**

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/snapshot/OMSnapshotPurgeRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager ozoneManager, TermIn
9191
trxnLogIndex, updatedSnapInfos);
9292
updateSnapshotChainAndCache(omMetadataManager, fromSnapshot,
9393
trxnLogIndex, updatedPathPreviousAndGlobalSnapshots);
94-
ozoneManager.getOmSnapshotManager().invalidateCacheEntry(snapTableKey);
94+
ozoneManager.getOmSnapshotManager().invalidateCacheEntry(fromSnapshot.getSnapshotId());
9595
}
9696

9797
omClientResponse = new OMSnapshotPurgeResponse(omResponse.build(),

hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotCache.java

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.io.IOException;
2828
import java.util.Iterator;
2929
import java.util.Map;
30+
import java.util.UUID;
3031
import java.util.concurrent.ConcurrentHashMap;
3132

3233
import static org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_NOT_FOUND;
@@ -39,26 +40,25 @@ public class SnapshotCache {
3940
static final Logger LOG = LoggerFactory.getLogger(SnapshotCache.class);
4041

4142
// Snapshot cache internal hash map.
42-
// Key: DB snapshot table key
43+
// Key: SnapshotId
4344
// Value: OmSnapshot instance, each holds a DB instance handle inside
4445
// TODO: [SNAPSHOT] Consider wrapping SoftReference<> around IOmMetadataReader
45-
private final ConcurrentHashMap<String, ReferenceCounted<OmSnapshot>> dbMap;
46+
private final ConcurrentHashMap<UUID, ReferenceCounted<OmSnapshot>> dbMap;
47+
48+
private final CacheLoader<UUID, OmSnapshot> cacheLoader;
4649

47-
private final CacheLoader<String, OmSnapshot> cacheLoader;
4850
// Soft-limit of the total number of snapshot DB instances allowed to be
4951
// opened on the OM.
5052
private final int cacheSizeLimit;
5153

52-
public SnapshotCache(
53-
CacheLoader<String, OmSnapshot> cacheLoader,
54-
int cacheSizeLimit) {
54+
public SnapshotCache(CacheLoader<UUID, OmSnapshot> cacheLoader, int cacheSizeLimit) {
5555
this.dbMap = new ConcurrentHashMap<>();
5656
this.cacheLoader = cacheLoader;
5757
this.cacheSizeLimit = cacheSizeLimit;
5858
}
5959

6060
@VisibleForTesting
61-
ConcurrentHashMap<String, ReferenceCounted<OmSnapshot>> getDbMap() {
61+
ConcurrentHashMap<UUID, ReferenceCounted<OmSnapshot>> getDbMap() {
6262
return dbMap;
6363
}
6464

@@ -71,17 +71,17 @@ public int size() {
7171

7272
/**
7373
* Immediately invalidate an entry.
74-
* @param key DB snapshot table key
74+
* @param key SnapshotId
7575
*/
76-
public void invalidate(String key) throws IOException {
76+
public void invalidate(UUID key) throws IOException {
7777
dbMap.compute(key, (k, v) -> {
7878
if (v == null) {
79-
LOG.warn("Key: '{}' does not exist in cache.", k);
79+
LOG.warn("SnapshotId: '{}' does not exist in snapshot cache.", k);
8080
} else {
8181
try {
8282
v.get().close();
8383
} catch (IOException e) {
84-
throw new IllegalStateException("Failed to close snapshot: " + key, e);
84+
throw new IllegalStateException("Failed to close snapshotId: " + key, e);
8585
}
8686
}
8787
return null;
@@ -92,11 +92,10 @@ public void invalidate(String key) throws IOException {
9292
* Immediately invalidate all entries and close their DB instances in cache.
9393
*/
9494
public void invalidateAll() {
95-
Iterator<Map.Entry<String, ReferenceCounted<OmSnapshot>>>
96-
it = dbMap.entrySet().iterator();
95+
Iterator<Map.Entry<UUID, ReferenceCounted<OmSnapshot>>> it = dbMap.entrySet().iterator();
9796

9897
while (it.hasNext()) {
99-
Map.Entry<String, ReferenceCounted<OmSnapshot>> entry = it.next();
98+
Map.Entry<UUID, ReferenceCounted<OmSnapshot>> entry = it.next();
10099
OmSnapshot omSnapshot = entry.getValue().get();
101100
try {
102101
// TODO: If wrapped with SoftReference<>, omSnapshot could be null?
@@ -114,19 +113,18 @@ public void invalidateAll() {
114113
*/
115114
public enum Reason {
116115
FS_API_READ,
117-
SNAPDIFF_READ,
116+
SNAP_DIFF_READ,
118117
DEEP_CLEAN_WRITE,
119118
GARBAGE_COLLECTION_WRITE
120119
}
121120

122121
/**
123122
* Get or load OmSnapshot. Shall be close()d after use.
124123
* TODO: [SNAPSHOT] Can add reason enum to param list later.
125-
* @param key snapshot table key
124+
* @param key SnapshotId
126125
* @return an OmSnapshot instance, or null on error
127126
*/
128-
public ReferenceCounted<OmSnapshot> get(String key)
129-
throws IOException {
127+
public ReferenceCounted<OmSnapshot> get(UUID key) throws IOException {
130128
// Warn if actual cache size exceeds the soft limit already.
131129
if (size() > cacheSizeLimit) {
132130
LOG.warn("Snapshot cache size ({}) exceeds configured soft-limit ({}).",
@@ -137,9 +135,9 @@ public ReferenceCounted<OmSnapshot> get(String key)
137135
ReferenceCounted<OmSnapshot> rcOmSnapshot =
138136
dbMap.compute(key, (k, v) -> {
139137
if (v == null) {
140-
LOG.info("Loading snapshot. Table key: {}", k);
138+
LOG.info("Loading SnapshotId: '{}'", k);
141139
try {
142-
v = new ReferenceCounted<>(cacheLoader.load(k), false, this);
140+
v = new ReferenceCounted<>(cacheLoader.load(key), false, this);
143141
} catch (OMException omEx) {
144142
// Return null if the snapshot is no longer active
145143
if (!omEx.getResult().equals(FILE_NOT_FOUND)) {
@@ -163,8 +161,7 @@ public ReferenceCounted<OmSnapshot> get(String key)
163161
if (rcOmSnapshot == null) {
164162
// The only exception that would fall through the loader logic above
165163
// is OMException with FILE_NOT_FOUND.
166-
throw new OMException("Snapshot table key '" + key + "' not found, "
167-
+ "or the snapshot is no longer active",
164+
throw new OMException("SnapshotId: '" + key + "' not found, or the snapshot is no longer active.",
168165
OMException.ResultCodes.FILE_NOT_FOUND);
169166
}
170167

@@ -179,12 +176,12 @@ public ReferenceCounted<OmSnapshot> get(String key)
179176

180177
/**
181178
* Release the reference count on the OmSnapshot instance.
182-
* @param key snapshot table key
179+
* @param key SnapshotId
183180
*/
184-
public void release(String key) {
181+
public void release(UUID key) {
185182
dbMap.compute(key, (k, v) -> {
186183
if (v == null) {
187-
throw new IllegalArgumentException("Key '" + key + "' does not exist in cache.");
184+
throw new IllegalArgumentException("SnapshotId '" + key + "' does not exist in cache.");
188185
} else {
189186
v.decrementRefCount();
190187
}
@@ -196,15 +193,6 @@ public void release(String key) {
196193
cleanup();
197194
}
198195

199-
/**
200-
* Alternatively, can release with OmSnapshot instance directly.
201-
* @param omSnapshot OmSnapshot
202-
*/
203-
public void release(OmSnapshot omSnapshot) {
204-
final String snapshotTableKey = omSnapshot.getSnapshotTableKey();
205-
release(snapshotTableKey);
206-
}
207-
208196
/**
209197
* Wrapper for cleanupInternal() that is synchronized to prevent multiple
210198
* threads from interleaving into the cleanup method.
@@ -221,24 +209,23 @@ private synchronized void cleanup() {
221209
* TODO: [SNAPSHOT] Add new ozone debug CLI command to trigger this directly.
222210
*/
223211
private void cleanupInternal() {
224-
for (Map.Entry<String, ReferenceCounted<OmSnapshot>> entry : dbMap.entrySet()) {
212+
for (Map.Entry<UUID, ReferenceCounted<OmSnapshot>> entry : dbMap.entrySet()) {
225213
dbMap.compute(entry.getKey(), (k, v) -> {
226214
if (v == null) {
227-
throw new IllegalStateException("Key '" + k + "' does not exist in cache. The RocksDB " +
215+
throw new IllegalStateException("SnapshotId '" + k + "' does not exist in cache. The RocksDB " +
228216
"instance of the Snapshot may not be closed properly.");
229217
}
230218

231219
if (v.getTotalRefCount() > 0) {
232-
LOG.debug("Snapshot {} is still being referenced ({}), skipping its clean up",
233-
k, v.getTotalRefCount());
220+
LOG.debug("SnapshotId {} is still being referenced ({}), skipping its clean up.", k, v.getTotalRefCount());
234221
return v;
235222
} else {
236-
LOG.debug("Closing Snapshot {}. It is not being referenced anymore.", k);
223+
LOG.debug("Closing SnapshotId {}. It is not being referenced anymore.", k);
237224
// Close the instance, which also closes its DB handle.
238225
try {
239226
v.get().close();
240227
} catch (IOException ex) {
241-
throw new IllegalStateException("Error while closing snapshot DB", ex);
228+
throw new IllegalStateException("Error while closing snapshot DB.", ex);
242229
}
243230
return null;
244231
}

hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshotManager.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,16 @@ public void testCloseOnEviction() throws IOException {
166166

167167
SnapshotInfo first = createSnapshotInfo(volumeName, bucketName);
168168
SnapshotInfo second = createSnapshotInfo(volumeName, bucketName);
169+
first.setGlobalPreviousSnapshotId(null);
170+
first.setPathPreviousSnapshotId(null);
171+
second.setGlobalPreviousSnapshotId(first.getSnapshotId());
172+
second.setPathPreviousSnapshotId(first.getSnapshotId());
173+
169174
when(snapshotInfoTable.get(first.getTableKey())).thenReturn(first);
170175
when(snapshotInfoTable.get(second.getTableKey())).thenReturn(second);
171176

177+
((OmMetadataManagerImpl) om.getMetadataManager()).getSnapshotChainManager().addSnapshot(first);
178+
((OmMetadataManagerImpl) om.getMetadataManager()).getSnapshotChainManager().addSnapshot(second);
172179
// create the first snapshot checkpoint
173180
OmSnapshotManager.createOmSnapshotCheckpoint(om.getMetadataManager(),
174181
first);

0 commit comments

Comments
 (0)