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

jojochuang 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 d4c06b2492e HDDS-15650. Fix snapshotUsedNamespace underflow when FSO 
directory is deleted and purged (#10545)
d4c06b2492e is described below

commit d4c06b2492ee74d22e157dbddb7dd476ef411879
Author: Wei-Chiu Chuang <[email protected]>
AuthorDate: Tue Jun 23 11:04:29 2026 -0700

    HDDS-15650. Fix snapshotUsedNamespace underflow when FSO directory is 
deleted and purged (#10545)
    
    Generated-by: Gemini 3.5 Flash
---
 .../om/request/key/OMKeyDeleteRequestWithFSO.java  |  2 +-
 .../ozone/om/request/OMRequestTestUtils.java       |  7 +++
 .../request/key/TestOMKeyDeleteRequestWithFSO.java | 69 ++++++++++++++++++++++
 3 files changed, 77 insertions(+), 1 deletion(-)

diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyDeleteRequestWithFSO.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyDeleteRequestWithFSO.java
index 4737b85373d..efe04a804b9 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyDeleteRequestWithFSO.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/request/key/OMKeyDeleteRequestWithFSO.java
@@ -162,7 +162,7 @@ public OMClientResponse validateAndUpdateCache(OzoneManager 
ozoneManager, Execut
       // Empty entries won't be added to deleted table so this key shouldn't 
get added to snapshotUsed space.
       boolean isKeyNonEmpty = !OmKeyInfo.isKeyEmpty(omKeyInfo);
       omBucketInfo.decrUsedBytes(quotaReleased, isKeyNonEmpty);
-      omBucketInfo.decrUsedNamespace(1L, isKeyNonEmpty);
+      omBucketInfo.decrUsedNamespace(1L, isKeyNonEmpty || 
keyStatus.isDirectory());
 
       // If omKeyInfo has hsync metadata, delete its corresponding open key as 
well
       String dbOpenKey = null;
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 c7e80f166ae..0a7ad486235 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
@@ -1030,6 +1030,13 @@ public static String deleteDir(String ozoneKey, String 
volume, String bucket,
         omDirectoryInfo.getName());
     omMetadataManager.getDeletedDirTable().put(ozoneKey, omKeyInfo);
     omMetadataManager.getDirectoryTable().delete(ozoneKey);
+
+    String bucketKey = omMetadataManager.getBucketKey(volume, bucket);
+    OmBucketInfo omBucketInfo = 
omMetadataManager.getBucketTable().get(bucketKey);
+    if (omBucketInfo != null) {
+      omBucketInfo.decrUsedNamespace(1L, true);
+      omMetadataManager.getBucketTable().put(bucketKey, omBucketInfo);
+    }
     return ozoneKey;
   }
 
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyDeleteRequestWithFSO.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyDeleteRequestWithFSO.java
index c537b09c85b..cbaa6344699 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyDeleteRequestWithFSO.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/request/key/TestOMKeyDeleteRequestWithFSO.java
@@ -34,6 +34,7 @@
 import org.apache.hadoop.ozone.om.OzonePrefixPathImpl;
 import org.apache.hadoop.ozone.om.exceptions.OMException;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
+import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
 import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.OzoneFileStatus;
@@ -333,4 +334,72 @@ public void testDeleteParentAfterChildDeleted() throws 
Exception {
     assertEquals(OzoneManagerProtocolProtos.Status.OK, 
response.getOMResponse().getStatus(),
         "Parent delete should succeed after children deleted");
   }
+
+  @Test
+  public void testSnapshotUsedNamespaceAfterDirectoryDeleteAndPurge() throws 
Exception {
+    OMRequestTestUtils.addVolumeAndBucketToDB(volumeName, bucketName, 
omMetadataManager, getBucketLayout());
+
+    String dirName = "dir1";
+    String dirKeyPath = addKeyToDirTable(volumeName, bucketName, dirName);
+
+    long parentObjectID = 0L;
+    long dirObjectID = 12345L;
+    OmDirectoryInfo omDirectoryInfo = 
OMRequestTestUtils.createOmDirectoryInfo(dirName, dirObjectID, parentObjectID);
+    omMetadataManager.getDirectoryTable().put(dirKeyPath, omDirectoryInfo);
+
+    String bucketKey = omMetadataManager.getBucketKey(volumeName, bucketName);
+    OmBucketInfo omBucketInfo = 
omMetadataManager.getBucketTable().get(bucketKey);
+    assertNotNull(omBucketInfo);
+    // Initialize used namespace and snapshot used namespace for test 
predictability
+    omBucketInfo.incrUsedNamespace(1);
+    omMetadataManager.getBucketTable().put(bucketKey, omBucketInfo);
+
+    // Delete the directory
+    long txnId = 100L;
+    OMRequest deleteRequest = doPreExecute(createDeleteKeyRequest(dirName, 
false));
+    OMKeyDeleteRequest omKeyDeleteRequest = 
getOmKeyDeleteRequest(deleteRequest);
+    OMClientResponse deleteResponse = 
omKeyDeleteRequest.validateAndUpdateCache(ozoneManager, txnId++);
+    assertEquals(OzoneManagerProtocolProtos.Status.OK, 
deleteResponse.getOMResponse().getStatus());
+
+    OmBucketInfo bucketInfoAfterDelete = 
omMetadataManager.getBucketTable().get(bucketKey);
+
+    // Perform purge
+    OzoneManagerProtocolProtos.PurgeDirectoriesRequest.Builder purgeDirRequest 
=
+        OzoneManagerProtocolProtos.PurgeDirectoriesRequest.newBuilder();
+
+    long volumeId = omMetadataManager.getVolumeId(volumeName);
+    long bucketId = bucketInfoAfterDelete.getObjectID();
+
+    OzoneManagerProtocolProtos.PurgePathRequest purgePathRequest =
+        OzoneManagerProtocolProtos.PurgePathRequest.newBuilder()
+            .setVolumeId(volumeId)
+            .setBucketId(bucketId)
+            .setDeletedDir(dirKeyPath)
+            .build();
+
+    purgeDirRequest.addDeletedPath(purgePathRequest);
+    purgeDirRequest.addBucketNameInfos(
+        OzoneManagerProtocolProtos.BucketNameInfo.newBuilder()
+            .setVolumeName(volumeName)
+            .setBucketName(bucketName)
+            .setBucketId(bucketId)
+            .setVolumeId(volumeId)
+            .build());
+
+    OMRequest purgeRequest = OMRequest.newBuilder()
+        .setCmdType(OzoneManagerProtocolProtos.Type.PurgeDirectories)
+        .setPurgeDirectoriesRequest(purgeDirRequest)
+        .setClientId(UUID.randomUUID().toString())
+        .build();
+
+    OMDirectoriesPurgeRequestWithFSO omPurgeRequest = new 
OMDirectoriesPurgeRequestWithFSO(purgeRequest);
+    OMClientResponse purgeResponse = 
omPurgeRequest.validateAndUpdateCache(ozoneManager, txnId);
+    assertEquals(OzoneManagerProtocolProtos.Status.OK, 
purgeResponse.getOMResponse().getStatus());
+
+    OmBucketInfo bucketInfoAfterPurge = 
omMetadataManager.getBucketTable().get(bucketKey);
+
+    // We expect snapshotUsedNamespace to not go negative
+    assertTrue(bucketInfoAfterPurge.getSnapshotUsedNamespace() >= 0,
+        "SnapshotUsedNamespace went negative (" + 
bucketInfoAfterPurge.getSnapshotUsedNamespace() + ") due to bug.");
+  }
 }


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

Reply via email to