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

sumitagrawal 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 693d0f483e HDDS-12589. Fix Incorrect FSO Key Listing for 
Container-to-Key Mapping. (#8109)
693d0f483e is described below

commit 693d0f483e07a06c75115d0155a71955bf977b88
Author: Arafat2198 <[email protected]>
AuthorDate: Thu Mar 20 10:14:08 2025 +0530

    HDDS-12589. Fix Incorrect FSO Key Listing for Container-to-Key Mapping. 
(#8109)
---
 .../hadoop/ozone/recon/api/ContainerEndpoint.java  |  23 ++--
 .../recon/spi/ReconContainerMetadataManager.java   |   2 +-
 .../impl/ReconContainerMetadataManagerImpl.java    |   8 +-
 .../ozone/recon/api/TestContainerEndpoint.java     | 118 +++++++++++++++++++++
 .../TestReconContainerMetadataManagerImpl.java     |  10 +-
 .../recon/tasks/TestContainerKeyMapperTask.java    | 116 ++++++++++++++++++++
 6 files changed, 254 insertions(+), 23 deletions(-)

diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java
index af69fb88f9..17382f0634 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/ContainerEndpoint.java
@@ -235,10 +235,14 @@ public Response getKeysForContainer(
     // Last key prefix to be used for pagination. It will be exposed in the 
response.
     String lastKey = "";
 
+    // If -1 is passed, set limit to the maximum integer value to retrieve all 
records
+    if (limit == -1) {
+      limit = Integer.MAX_VALUE;
+    }
+
     try {
       Map<ContainerKeyPrefix, Integer> containerKeyPrefixMap =
-          reconContainerMetadataManager.getKeyPrefixesForContainer(containerID,
-              prevKeyPrefix);
+          
reconContainerMetadataManager.getKeyPrefixesForContainer(containerID, 
prevKeyPrefix, limit);
       // Get set of Container-Key mappings for given containerId.
       for (ContainerKeyPrefix containerKeyPrefix : containerKeyPrefixMap
           .keySet()) {
@@ -267,10 +271,7 @@ public Response getKeysForContainer(
           List<ContainerBlockMetadata> blockIds =
               getBlocks(matchedKeys, containerID);
 
-          String ozoneKey = omMetadataManager.getOzoneKey(
-              omKeyInfo.getVolumeName(),
-              omKeyInfo.getBucketName(),
-              omKeyInfo.getKeyName());
+          String ozoneKey = containerKeyPrefix.getKeyPrefix();
           lastKey = ozoneKey;
           if (keyMetadataMap.containsKey(ozoneKey)) {
             keyMetadataMap.get(ozoneKey).getVersions()
@@ -279,10 +280,6 @@ public Response getKeysForContainer(
             keyMetadataMap.get(ozoneKey).getBlockIds()
                 .put(containerKeyPrefix.getKeyVersion(), blockIds);
           } else {
-            // break the for loop if limit has been reached
-            if (keyMetadataMap.size() == limit) {
-              break;
-            }
             KeyMetadata keyMetadata = new KeyMetadata();
             keyMetadata.setBucket(omKeyInfo.getBucketName());
             keyMetadata.setVolume(omKeyInfo.getVolumeName());
@@ -305,11 +302,9 @@ public Response getKeysForContainer(
       totalCount =
           reconContainerMetadataManager.getKeyCountForContainer(containerID);
     } catch (IOException ioEx) {
-      throw new WebApplicationException(ioEx,
-          Response.Status.INTERNAL_SERVER_ERROR);
+      throw new WebApplicationException(ioEx, 
Response.Status.INTERNAL_SERVER_ERROR);
     }
-    KeysResponse keysResponse =
-        new KeysResponse(totalCount, keyMetadataMap.values(), lastKey);
+    KeysResponse keysResponse = new KeysResponse(totalCount, 
keyMetadataMap.values(), lastKey);
     return Response.ok(keysResponse).build();
   }
 
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconContainerMetadataManager.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconContainerMetadataManager.java
index 1400279d12..4b0b7df42f 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconContainerMetadataManager.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/ReconContainerMetadataManager.java
@@ -172,7 +172,7 @@ Map<ContainerKeyPrefix, Integer> getKeyPrefixesForContainer(
    * @return Map of Key prefix -&gt; count.
    */
   Map<ContainerKeyPrefix, Integer> getKeyPrefixesForContainer(
-      long containerId, String prevKeyPrefix) throws IOException;
+      long containerId, String prevKeyPrefix, int limit) throws IOException;
 
   /**
    * Get a Map of containerID, containerMetadata of Containers only for the
diff --git 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconContainerMetadataManagerImpl.java
 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconContainerMetadataManagerImpl.java
index 17cb793985..771e4372ad 100644
--- 
a/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconContainerMetadataManagerImpl.java
+++ 
b/hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/spi/impl/ReconContainerMetadataManagerImpl.java
@@ -48,6 +48,7 @@
 import org.apache.hadoop.hdds.utils.db.TableIterator;
 import org.apache.hadoop.ozone.om.helpers.BucketLayout;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
+import org.apache.hadoop.ozone.recon.ReconConstants;
 import org.apache.hadoop.ozone.recon.ReconUtils;
 import org.apache.hadoop.ozone.recon.api.types.ContainerKeyPrefix;
 import org.apache.hadoop.ozone.recon.api.types.ContainerMetadata;
@@ -346,7 +347,8 @@ public Integer getCountForContainerKeyPrefix(
   public Map<ContainerKeyPrefix, Integer> getKeyPrefixesForContainer(
       long containerId) throws IOException {
     // set the default startKeyPrefix to empty string
-    return getKeyPrefixesForContainer(containerId, StringUtils.EMPTY);
+    return getKeyPrefixesForContainer(containerId, StringUtils.EMPTY,
+        Integer.parseInt(ReconConstants.DEFAULT_FETCH_COUNT));
   }
 
   /**
@@ -360,7 +362,7 @@ public Map<ContainerKeyPrefix, Integer> 
getKeyPrefixesForContainer(
    */
   @Override
   public Map<ContainerKeyPrefix, Integer> getKeyPrefixesForContainer(
-      long containerId, String prevKeyPrefix) throws IOException {
+      long containerId, String prevKeyPrefix, int limit) throws IOException {
 
     Map<ContainerKeyPrefix, Integer> prefixes = new LinkedHashMap<>();
     try (TableIterator<ContainerKeyPrefix,
@@ -387,7 +389,7 @@ public Map<ContainerKeyPrefix, Integer> 
getKeyPrefixesForContainer(
         return prefixes;
       }
 
-      while (containerIterator.hasNext()) {
+      while (containerIterator.hasNext() && prefixes.size() < limit) {
         KeyValue<ContainerKeyPrefix, Integer> keyValue =
             containerIterator.next();
         ContainerKeyPrefix containerKeyPrefix = keyValue.getKey();
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestContainerEndpoint.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestContainerEndpoint.java
index dd85f6c753..4db2a276c8 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestContainerEndpoint.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestContainerEndpoint.java
@@ -1701,4 +1701,122 @@ public void 
testGetOmContainersDeletedInSCMPrevContainerParam()
     assertEquals(1, containerDiscrepancyInfoList.size());
     assertEquals(2, containerDiscrepancyInfoList.get(0).getContainerID());
   }
+
+  /**
+   * Helper method that creates duplicate FSO file keys – two keys having the 
same file
+   * name but under different directories. It creates the necessary volume, 
bucket, and
+   * directory entries, and then writes two keys using writeKeyToOm.
+   */
+  private void setUpDuplicateFSOFileKeys() throws IOException {
+    // Ensure the volume exists.
+    String volumeKey = reconOMMetadataManager.getVolumeKey(VOLUME_NAME);
+    OmVolumeArgs volArgs = OmVolumeArgs.newBuilder()
+        .setVolume(VOLUME_NAME)
+        .setAdminName("TestUser")
+        .setOwnerName("TestUser")
+        .setObjectID(VOL_OBJECT_ID)
+        .build();
+    reconOMMetadataManager.getVolumeTable().put(volumeKey, volArgs);
+
+    // Ensure the bucket exists.
+    OmBucketInfo bucketInfo = OmBucketInfo.newBuilder()
+        .setVolumeName(VOLUME_NAME)
+        .setBucketName(BUCKET_NAME)
+        .setBucketLayout(BucketLayout.FILE_SYSTEM_OPTIMIZED)
+        .setObjectID(BUCKET_OBJECT_ID)
+        .build();
+    String bucketKey = reconOMMetadataManager.getBucketKey(VOLUME_NAME, 
BUCKET_NAME);
+    reconOMMetadataManager.getBucketTable().put(bucketKey, bucketInfo);
+
+    // Create two directories: "dirA" and "dirB" with unique object IDs.
+    // For a top-level directory in a bucket, the parent's object id is the 
bucket's id.
+    OmDirectoryInfo dirA = OmDirectoryInfo.newBuilder()
+        .setName("dirA")
+        .setParentObjectID(BUCKET_OBJECT_ID)
+        .setUpdateID(1L)
+        .setObjectID(5L)   // Unique object id for dirA.
+        .build();
+    OmDirectoryInfo dirB = OmDirectoryInfo.newBuilder()
+        .setName("dirB")
+        .setParentObjectID(BUCKET_OBJECT_ID)
+        .setUpdateID(1L)
+        .setObjectID(6L)   // Unique object id for dirB.
+        .build();
+    // Build DB directory keys. (The third parameter is used to form a unique 
key.)
+    String dirKeyA = reconOMMetadataManager.getOzonePathKey(VOL_OBJECT_ID, 
BUCKET_OBJECT_ID, 5L, "dirA");
+    String dirKeyB = reconOMMetadataManager.getOzonePathKey(VOL_OBJECT_ID, 
BUCKET_OBJECT_ID, 6L, "dirB");
+    reconOMMetadataManager.getDirectoryTable().put(dirKeyA, dirA);
+    reconOMMetadataManager.getDirectoryTable().put(dirKeyB, dirB);
+
+    // Use a common OmKeyLocationInfoGroup.
+    OmKeyLocationInfoGroup locationInfoGroup = getLocationInfoGroup1();
+
+    // Write two FSO keys with the same file name ("dupFile") but in different 
directories.
+    // The file name stored in OM is the full path (e.g., "dirA/dupFile" vs. 
"dirB/dupFile").
+    writeKeyToOm(reconOMMetadataManager,
+        "dupFileKey1",           // internal key name for the first key
+        BUCKET_NAME,
+        VOLUME_NAME,
+        "dupFileKey1",          // full file path for the first key
+        100L,                    // object id (example)
+        5L,                      // parent's object id for dirA (same as 
dirA's object id)
+        BUCKET_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(locationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    writeKeyToOm(reconOMMetadataManager,
+        "dupFileKey1",           // internal key name for the second key
+        BUCKET_NAME,
+        VOLUME_NAME,
+        "dupFileKey1",          // full file path for the second key
+        100L,
+        6L,                      // parent's object id for dirB
+        BUCKET_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(locationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+  }
+
+  /**
+   * Test method that sets up two duplicate FSO file keys (same file name but 
in different directories)
+   * and then verifies that the ContainerEndpoint returns two distinct key 
records.
+   */
+  @Test
+  public void testDuplicateFSOKeysForContainerEndpoint() throws IOException {
+    // Set up duplicate FSO file keys.
+    setUpDuplicateFSOFileKeys();
+    NSSummaryTaskWithFSO nSSummaryTaskWithFso =
+        new NSSummaryTaskWithFSO(reconNamespaceSummaryManager,
+            reconOMMetadataManager, 10);
+    nSSummaryTaskWithFso.reprocessWithFSO(reconOMMetadataManager);
+    // Reprocess the container key mappings so that the new keys are loaded.
+    reprocessContainerKeyMapper();
+
+    // Assume that FSO keys are mapped to container ID 20L (as in previous 
tests for FSO).
+    Response response = containerEndpoint.getKeysForContainer(20L, -1, "");
+    KeysResponse keysResponse = (KeysResponse) response.getEntity();
+    Collection<KeyMetadata> keyMetadataList = keysResponse.getKeys();
+
+    // We expect two distinct keys.
+    assertEquals(2, keysResponse.getTotalCount());
+    assertEquals(2, keyMetadataList.size());
+
+    for (KeyMetadata km : keyMetadataList) {
+      String completePath = km.getCompletePath();
+      assertNotNull(completePath);
+      // Verify that the complete path reflects either directory "dirA" or 
"dirB"
+      if (completePath.contains("dirA")) {
+        assertEquals(VOLUME_NAME + "/" + BUCKET_NAME + "/dirA/dupFileKey1", 
completePath);
+      } else if (completePath.contains("dirB")) {
+        assertEquals(VOLUME_NAME + "/" + BUCKET_NAME + "/dirB/dupFileKey1", 
completePath);
+      } else {
+        throw new AssertionError("Unexpected complete path: " + completePath);
+      }
+    }
+  }
+
+
 }
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/spi/impl/TestReconContainerMetadataManagerImpl.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/spi/impl/TestReconContainerMetadataManagerImpl.java
index a3b5cef123..693bb50049 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/spi/impl/TestReconContainerMetadataManagerImpl.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/spi/impl/TestReconContainerMetadataManagerImpl.java
@@ -309,25 +309,25 @@ public void testGetKeyPrefixesForContainerWithKeyPrefix() 
throws Exception {
 
     Map<ContainerKeyPrefix, Integer> keyPrefixMap =
         reconContainerMetadataManager.getKeyPrefixesForContainer(containerId,
-            keyPrefix1);
+            keyPrefix1, 1000);
     assertEquals(1, keyPrefixMap.size());
     assertEquals(2, keyPrefixMap.get(containerKeyPrefix2).longValue());
 
     keyPrefixMap = reconContainerMetadataManager.getKeyPrefixesForContainer(
-        nextContainerId, keyPrefix3);
+        nextContainerId, keyPrefix3, 1000);
     assertEquals(0, keyPrefixMap.size());
 
     // test for negative cases
     keyPrefixMap = reconContainerMetadataManager.getKeyPrefixesForContainer(
-        containerId, "V3/B1/invalid");
+        containerId, "V3/B1/invalid", 1000);
     assertEquals(0, keyPrefixMap.size());
 
     keyPrefixMap = reconContainerMetadataManager.getKeyPrefixesForContainer(
-        containerId, keyPrefix3);
+        containerId, keyPrefix3, 1000);
     assertEquals(0, keyPrefixMap.size());
 
     keyPrefixMap = reconContainerMetadataManager.getKeyPrefixesForContainer(
-        10L, "");
+        10L, "", 1000);
     assertEquals(0, keyPrefixMap.size());
   }
 
diff --git 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestContainerKeyMapperTask.java
 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestContainerKeyMapperTask.java
index fb31537ec7..c30fc451b0 100644
--- 
a/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestContainerKeyMapperTask.java
+++ 
b/hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/tasks/TestContainerKeyMapperTask.java
@@ -472,6 +472,122 @@ public void testFileTableProcess() throws Exception {
         firstKeyPrefix.getKeyPrefix());
   }
 
+  @Test
+  public void testDuplicateFSOKeysInDifferentDirectories() throws Exception {
+    // Ensure container 1 is initially empty.
+    Map<ContainerKeyPrefix, Integer> keyPrefixesForContainer =
+        reconContainerMetadataManager.getKeyPrefixesForContainer(1L);
+    assertThat(keyPrefixesForContainer).isEmpty();
+
+    Pipeline pipeline = getRandomPipeline();
+    // Create a common OmKeyLocationInfoGroup for all keys.
+    List<OmKeyLocationInfo> omKeyLocationInfoList = new ArrayList<>();
+    BlockID blockID = new BlockID(1L, 1L);
+    OmKeyLocationInfo omKeyLocationInfo = getOmKeyLocationInfo(blockID, 
pipeline);
+    omKeyLocationInfoList.add(omKeyLocationInfo);
+    OmKeyLocationInfoGroup omKeyLocationInfoGroup =
+        new OmKeyLocationInfoGroup(0L, omKeyLocationInfoList);
+
+    // Define file names.
+    String file1Key = "file1";
+    String file2Key = "file2";
+
+    // Define directory (parent) object IDs with shorter values.
+    long dir1Id = -101L;
+    long dir2Id = -102L;
+    long dir3Id = -103L;
+
+    // Write three FSO keys for "file1" with different parent object IDs.
+    writeKeyToOm(reconOMMetadataManager,
+        file1Key,                // keyName
+        BUCKET_NAME,             // bucketName
+        VOLUME_NAME,             // volName
+        file1Key,                // fileName
+        KEY_ONE_OBJECT_ID,       // objectId
+        dir1Id,                  // ObjectId for first directory
+        BUCKET_ONE_OBJECT_ID,    // bucketObjectId
+        VOL_OBJECT_ID,           // volumeObjectId
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    writeKeyToOm(reconOMMetadataManager,
+        file1Key,
+        BUCKET_NAME,
+        VOLUME_NAME,
+        file1Key,
+        KEY_ONE_OBJECT_ID,
+        dir2Id,            // ObjectId for second directory
+        BUCKET_ONE_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    writeKeyToOm(reconOMMetadataManager,
+        file1Key,
+        BUCKET_NAME,
+        VOLUME_NAME,
+        file1Key,
+        KEY_ONE_OBJECT_ID,
+        dir3Id,            // ObjectId for third directory
+        BUCKET_ONE_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    // Write three FSO keys for "file2" with different parent object IDs.
+    writeKeyToOm(reconOMMetadataManager,
+        "fso-file2",
+        BUCKET_NAME,
+        VOLUME_NAME,
+        file2Key,
+        KEY_ONE_OBJECT_ID,
+        dir1Id,
+        BUCKET_ONE_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    writeKeyToOm(reconOMMetadataManager,
+        "fso-file2",
+        BUCKET_NAME,
+        VOLUME_NAME,
+        file2Key,
+        KEY_ONE_OBJECT_ID,
+        dir2Id,
+        BUCKET_ONE_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    writeKeyToOm(reconOMMetadataManager,
+        "fso-file2",
+        BUCKET_NAME,
+        VOLUME_NAME,
+        file2Key,
+        KEY_ONE_OBJECT_ID,
+        dir3Id,
+        BUCKET_ONE_OBJECT_ID,
+        VOL_OBJECT_ID,
+        Collections.singletonList(omKeyLocationInfoGroup),
+        BucketLayout.FILE_SYSTEM_OPTIMIZED,
+        KEY_ONE_SIZE);
+
+    // Reprocess container key mappings.
+    ContainerKeyMapperTaskFSO containerKeyMapperTask =
+        new ContainerKeyMapperTaskFSO(reconContainerMetadataManager, 
omConfiguration);
+    containerKeyMapperTask.reprocess(reconOMMetadataManager);
+
+    // With our changes using the raw key prefix as the unique identifier,
+    // we expect six distinct entries in container 1.
+    keyPrefixesForContainer = 
reconContainerMetadataManager.getKeyPrefixesForContainer(1L);
+    assertEquals(6, keyPrefixesForContainer.size());
+  }
+
   private OmKeyInfo buildOmKeyInfo(String volume,
                                    String bucket,
                                    String key,


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

Reply via email to