You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ch...@apache.org on 2026/10/14 15:46:35 UTC
(ozone) branch master updated: HDDS-16998. Skip setting lastScannedKey during lifecycle cache scan (#11533)
This is an automated email from the ASF dual-hosted git repository.
chihsuan 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 2536fe9cbc3 HDDS-16998. Skip setting lastScannedKey during lifecycle cache scan (#11533)
2536fe9cbc3 is described below
commit 2536fe9cbc342edea19fe8d3e371659ac955cd51
Author: rajeshkumarchandolu <ra...@gmail.com>
AuthorDate: Wed Oct 14 21:16:28 2026 +0530
HDDS-16998. Skip setting lastScannedKey during lifecycle cache scan (#11533)
Co-authored-by: Chi-Hsuan Huang <ch...@gmail.com>
---
.../ozone/om/service/KeyLifecycleService.java | 3 +-
.../ozone/om/service/TestKeyLifecycleService.java | 60 ++++++++++++++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
index 565a2ec86cd..d74c220e8e4 100644
--- a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
+++ b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/service/KeyLifecycleService.java
@@ -825,6 +825,8 @@ private void evaluateKeyAndDirTable(OmBucketInfo bucket, long volumeObjId, Table
long numKeysExpired = 0;
HashSet<String> deletedKeySetInCache = new HashSet();
HashSet<String> keySetInCache = new HashSet();
+ // The cache is scanned separately before the table, so a cache key cannot represent table scan progress.
+ // Only advance lastScannedKey during table iteration to avoid skipping unprocessed table keys on resume.
Iterator<Map.Entry<CacheKey<String>, CacheValue<OmKeyInfo>>> cacheIter = keyTable.cacheIterator();
while (cacheIter.hasNext()) {
Map.Entry<CacheKey<String>, CacheValue<OmKeyInfo>> entry = cacheIter.next();
@@ -850,7 +852,6 @@ private void evaluateKeyAndDirTable(OmBucketInfo bucket, long volumeObjId, Table
break;
}
}
- lastScannedKey = entry.getKey().getCacheKey();
}
}
diff --git a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
index 456050c73cd..1da98e26775 100644
--- a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
+++ b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/service/TestKeyLifecycleService.java
@@ -62,6 +62,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -1154,6 +1155,65 @@ void testLastScannedKeySeek(boolean keyBelongToDir) throws Exception {
}
}
+ @Test
+ void testCacheOnlyKeyNotSavedAsLastScannedKey() throws Exception {
+ final String volumeName = getTestName();
+ final String bucketName = uniqueObjectName("bucket");
+ long initialDeletedKeyCount = getDeletedKeyCount();
+ long initialKeyCount = getKeyCount(FILE_SYSTEM_OPTIMIZED);
+
+ keyLifecycleService.setKeyDeleteBatchSize(1);
+ keyLifecycleService.suspend();
+
+ createVolumeAndBucket(volumeName, bucketName, FILE_SYSTEM_OPTIMIZED,
+ UserGroupInformation.getCurrentUser().getShortUserName());
+ createAndCommitKey(volumeName, bucketName, "dir1/key1", 1, null);
+ createAndCommitKey(volumeName, bucketName, "dir1/key2", 1, null);
+ GenericTestUtils.waitFor(() -> getKeyCount(FILE_SYSTEM_OPTIMIZED) - initialKeyCount == 2,
+ WAIT_CHECK_INTERVAL, 1000);
+ awaitKeyCacheDrained(FILE_SYSTEM_OPTIMIZED, volumeName, bucketName);
+
+ long volumeId = metadataManager.getVolumeTable().get(metadataManager.getVolumeKey(volumeName)).getObjectID();
+ long bucketId =
+ metadataManager.getBucketTable().get(metadataManager.getBucketKey(volumeName, bucketName)).getObjectID();
+ long dir1Id = metadataManager.getDirectoryTable().get(
+ metadataManager.getOzonePathKey(volumeId, bucketId, bucketId, "dir1")).getObjectID();
+
+ // key9 sorts after the table keys and only exists in the cache. The max epoch keeps the double buffer
+ // from evicting it before the scan.
+ Table<String, OmKeyInfo> keyTable = metadataManager.getKeyTable(FILE_SYSTEM_OPTIMIZED);
+ OmKeyInfo key1Info = keyTable.get(metadataManager.getOzonePathKey(volumeId, bucketId, dir1Id, "key1"));
+ OmKeyInfo cacheOnlyKey = key1Info.toBuilder().setKeyName("key9").build();
+ String cacheOnlyDbKey = metadataManager.getOzonePathKey(volumeId, bucketId, dir1Id, "key9");
+ keyTable.addCacheEntry(new CacheKey<>(cacheOnlyDbKey), CacheValue.get(Long.MAX_VALUE, cacheOnlyKey));
+
+ ZonedDateTime date = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(EXPIRE_SECONDS);
+ createLifecyclePolicy(volumeName, bucketName, FILE_SYSTEM_OPTIMIZED, "", null, date.toString(), true);
+
+ Field tableField = OmMetadataManagerImpl.class.getDeclaredField("lifecycleScanStateTable");
+ tableField.setAccessible(true);
+ Table<String, OmLifecycleScanState> originalTable =
+ (Table<String, OmLifecycleScanState>) tableField.get(metadataManager);
+ Table<String, OmLifecycleScanState> spyTable = spy(originalTable);
+ tableField.set(metadataManager, spyTable);
+ try {
+ runSingleLifecycleScan(volumeName, bucketName);
+
+ GenericTestUtils.waitFor(() ->
+ (getDeletedKeyCount() - initialDeletedKeyCount) == 3, WAIT_CHECK_INTERVAL, 10000);
+ assertEquals(0, getKeyCount(FILE_SYSTEM_OPTIMIZED) - initialKeyCount);
+
+ String bucketKey = metadataManager.getBucketKey(volumeName, bucketName);
+ // With a batch size of 1, the expired list fills right after the cache pass, so scan state is saved
+ // while the cache-only key is the last key evaluated.
+ verify(spyTable, atLeast(1)).addCacheEntry(argThat(k -> k.getCacheKey().equals(bucketKey)), any());
+ verify(spyTable, never()).addCacheEntry(argThat(k -> k.getCacheKey().equals(bucketKey)),
+ argThat(v -> v.getCacheValue() != null && cacheOnlyDbKey.equals(v.getCacheValue().getLastScannedKey())));
+ } finally {
+ tableField.set(metadataManager, originalTable);
+ }
+ }
+
@ParameterizedTest
@ValueSource(strings = {"FILE_SYSTEM_OPTIMIZED", "OBJECT_STORE"})
void testBucketRootScannedDirResume(BucketLayout layout) throws Exception {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org