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

prashantpogde 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 f4b6bb1e52 HDDS-8739. Snapdiff should return complete absolute path in 
Diff Entry (#4823)
f4b6bb1e52 is described below

commit f4b6bb1e52da23eb2bb86320073468e9e1191ec5
Author: Swaminathan Balachandran <[email protected]>
AuthorDate: Thu Jun 22 09:35:06 2023 -0700

    HDDS-8739. Snapdiff should return complete absolute path in Diff Entry 
(#4823)
---
 .../org/apache/ozone/rocksdiff/RocksDiffUtils.java |   2 +-
 .../main/java/org/apache/hadoop/ozone/OmUtils.java |   7 +-
 .../ozone/snapshot/SnapshotDiffReportOzone.java    |  46 +++++-
 .../hadoop/fs/ozone/TestRootedOzoneFileSystem.java |   6 +-
 .../org/apache/hadoop/ozone/om/TestOmSnapshot.java |  48 +++---
 .../om/snapshot/FSODirectoryPathResolver.java      | 109 +++++++++++++
 .../ozone/om/snapshot/ObjectPathResolver.java      |  34 ++++
 .../ozone/om/snapshot/SnapshotDiffManager.java     | 178 +++++++++++++++------
 .../om/snapshot/TestFSODirectoryPathResolver.java  | 150 +++++++++++++++++
 .../ozone/om/snapshot/TestSnapshotDiffManager.java |  52 +++---
 10 files changed, 531 insertions(+), 101 deletions(-)

diff --git 
a/hadoop-hdds/rocksdb-checkpoint-differ/src/main/java/org/apache/ozone/rocksdiff/RocksDiffUtils.java
 
b/hadoop-hdds/rocksdb-checkpoint-differ/src/main/java/org/apache/ozone/rocksdiff/RocksDiffUtils.java
index cca3eaf0e7..225130bce7 100644
--- 
a/hadoop-hdds/rocksdb-checkpoint-differ/src/main/java/org/apache/ozone/rocksdiff/RocksDiffUtils.java
+++ 
b/hadoop-hdds/rocksdb-checkpoint-differ/src/main/java/org/apache/ozone/rocksdiff/RocksDiffUtils.java
@@ -89,7 +89,7 @@ public final class RocksDiffUtils {
       TableProperties properties = sstFileReader.get().getTableProperties();
       String tableName = new String(properties.getColumnFamilyName(), UTF_8);
       if (tableToPrefixMap.containsKey(tableName)) {
-        String prefix = tableToPrefixMap.get(tableName) + OM_KEY_PREFIX;
+        String prefix = tableToPrefixMap.get(tableName);
         try (ManagedSstFileReaderIterator iterator =
             ManagedSstFileReaderIterator.managed(sstFileReader.get()
                 .newIterator(new ReadOptions()))) {
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
index 4eea2aa7a2..1ff24bed16 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java
@@ -40,7 +40,6 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.Comparator;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hdds.conf.ConfigurationException;
 import org.apache.hadoop.hdds.conf.ConfigurationSource;
@@ -65,6 +64,7 @@ import static 
org.apache.hadoop.hdds.HddsUtils.getHostNameFromConfigKeys;
 import static org.apache.hadoop.hdds.HddsUtils.getPortNumberFromConfigKeys;
 import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
 import static org.apache.hadoop.ozone.OzoneConsts.OM_SNAPSHOT_INDICATOR;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
 import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_BIND_HOST_DEFAULT;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_DECOMMISSIONED_NODES_KEY;
@@ -675,7 +675,6 @@ public final class OmUtils {
    * does not preserve.
    * @return normalized key name.
    */
-  @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
   public static String normalizeKey(String keyName,
       boolean preserveTrailingSlash) {
     // For empty strings do nothing, just return the same.
@@ -692,8 +691,8 @@ public final class OmUtils {
         LOG.debug("Normalized key {} to {} ", keyName,
             normalizedKeyName.substring(1));
       }
-      if (preserveTrailingSlash && keyName.endsWith("/")) {
-        return normalizedKeyName.substring(1) + "/";
+      if (preserveTrailingSlash && keyName.endsWith(OZONE_URI_DELIMITER)) {
+        return normalizedKeyName.substring(1) + OZONE_URI_DELIMITER;
       }
       return normalizedKeyName.substring(1);
     }
diff --git 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/snapshot/SnapshotDiffReportOzone.java
 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/snapshot/SnapshotDiffReportOzone.java
index 03a1ed418e..14f204999f 100644
--- 
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/snapshot/SnapshotDiffReportOzone.java
+++ 
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/snapshot/SnapshotDiffReportOzone.java
@@ -24,12 +24,15 @@ import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.utils.db.Codec;
 import org.apache.hadoop.hdds.utils.db.DelegatedCodec;
 import org.apache.hadoop.hdds.utils.db.Proto2Codec;
+import org.apache.hadoop.hdfs.DFSUtilClient;
+import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport;
 import org.apache.hadoop.ozone.OFSPath;
 import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.DiffReportEntryProto;
 import 
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.SnapshotDiffReportProto;
 
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -163,7 +166,7 @@ public class SnapshotDiffReportOzone
       return null;
     }
     DiffType type = fromProtobufDiffType(entry.getDiffType());
-    return type == null ? null : new DiffReportEntry(type,
+    return type == null ? null : new DiffReportEntryOzone(type,
         entry.getSourcePath().getBytes(StandardCharsets.UTF_8),
         entry.hasTargetPath() ?
             entry.getTargetPath().getBytes(StandardCharsets.UTF_8) : null);
@@ -191,7 +194,7 @@ public class SnapshotDiffReportOzone
 
   public static DiffReportEntry getDiffReportEntry(final DiffType type,
       final String sourcePath, final String targetPath) {
-    return new DiffReportEntry(type,
+    return new DiffReportEntryOzone(type,
         sourcePath.getBytes(StandardCharsets.UTF_8),
         targetPath != null ? targetPath.getBytes(StandardCharsets.UTF_8) :
             null);
@@ -206,5 +209,44 @@ public class SnapshotDiffReportOzone
     this.getDiffList().addAll(diffReport.getDiffList());
   }
 
+  /**
+   * DiffReportEntry for ozone.
+   */
+  public static class DiffReportEntryOzone extends DiffReportEntry {
+
+    public DiffReportEntryOzone(DiffType type, byte[] sourcePath) {
+      super(type, sourcePath);
+    }
+
+    public DiffReportEntryOzone(DiffType type, byte[][] sourcePathComponents) {
+      super(type, sourcePathComponents);
+    }
+
+    public DiffReportEntryOzone(DiffType type, byte[] sourcePath,
+                                byte[] targetPath) {
+      super(type, sourcePath, targetPath);
+    }
 
+    public DiffReportEntryOzone(DiffType type, byte[][] sourcePathComponents,
+                                byte[][] targetPathComponents) {
+      super(type, sourcePathComponents, targetPathComponents);
+    }
+
+    static String getPathString(byte[] path) {
+      String pathStr = DFSUtilClient.bytes2String(path);
+      return pathStr.isEmpty() ? "." : Paths.get(pathStr)
+          .toAbsolutePath().toString();
+    }
+
+    @Override
+    public String toString() {
+      String str = this.getType().getLabel() + "\t" +
+          getPathString(this.getSourcePath());
+      if (this.getType() == SnapshotDiffReport.DiffType.RENAME) {
+        str = str + " -> " + getPathString(this.getTargetPath());
+      }
+
+      return str;
+    }
+  }
 }
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java
index 4ea79970f2..6f2be4b099 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/fs/ozone/TestRootedOzoneFileSystem.java
@@ -2481,9 +2481,11 @@ public class TestRootedOzoneFileSystem {
         diff.getDiffList().get(0).getType());
     Assert.assertEquals(SnapshotDiffReport.DiffType.CREATE,
         diff.getDiffList().get(1).getType());
-    Assert.assertArrayEquals("key1".getBytes(StandardCharsets.UTF_8),
+    Assert.assertArrayEquals(
+        "/key1".getBytes(StandardCharsets.UTF_8),
         diff.getDiffList().get(0).getSourcePath());
-    Assert.assertArrayEquals("key2".getBytes(StandardCharsets.UTF_8),
+    Assert.assertArrayEquals(
+        "/key2".getBytes(StandardCharsets.UTF_8),
         diff.getDiffList().get(1).getSourcePath());
 
     // test whether snapdiff returns aggregated response as
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
index 1d94ba1b6a..4195fa0994 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOmSnapshot.java
@@ -29,6 +29,7 @@ import org.apache.hadoop.hdds.scm.HddsWhiteboxTestUtils;
 import org.apache.hadoop.hdds.utils.db.DBProfile;
 import org.apache.hadoop.hdds.utils.db.RDBStore;
 import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksObjectUtils;
+import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport;
 import org.apache.hadoop.hdfs.protocol.SnapshotDiffReport.DiffReportEntry;
 import org.apache.hadoop.ozone.MiniOzoneCluster;
 import org.apache.hadoop.ozone.TestDataUtil;
@@ -81,6 +82,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_DB_PROFILE;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
 import static 
org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isDone;
 import static 
org.apache.hadoop.ozone.admin.scm.FinalizeUpgradeCommandUtil.isStarting;
 import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
@@ -216,7 +218,7 @@ public class TestOmSnapshot {
 
     // stop the deletion services so that keys can still be read
     keyManager.stop();
-    preFinalizationChecks();
+//    preFinalizationChecks();
     finalizeOMUpgrade();
     counter = new AtomicInteger();
   }
@@ -599,12 +601,14 @@ public class TestOmSnapshot {
     SnapshotDiffReportOzone
         diff2 = getSnapDiffReport(volume, bucket, snap2, snap3);
     Assert.assertEquals(2, diff2.getDiffList().size());
-    Assert.assertTrue(diff2.getDiffList().contains(
-        SnapshotDiffReportOzone.getDiffReportEntry(
-            SnapshotDiffReportOzone.DiffType.CREATE, key2)));
-    Assert.assertTrue(diff2.getDiffList().contains(
-        SnapshotDiffReportOzone.getDiffReportEntry(
-            SnapshotDiffReportOzone.DiffType.DELETE, key1)));
+    Assert.assertEquals(
+        Arrays.asList(SnapshotDiffReportOzone.getDiffReportEntry(
+            SnapshotDiffReport.DiffType.DELETE,
+                OZONE_URI_DELIMITER + key1),
+            SnapshotDiffReportOzone.getDiffReportEntry(
+                SnapshotDiffReport.DiffType.CREATE,
+                OZONE_URI_DELIMITER + key2)),
+        diff2.getDiffList());
 
     // Rename Key2
     String key2Renamed = key2 + "_renamed";
@@ -617,7 +621,8 @@ public class TestOmSnapshot {
     Assert.assertEquals(1, diff3.getDiffList().size());
     Assert.assertTrue(diff3.getDiffList().contains(
         SnapshotDiffReportOzone.getDiffReportEntry(
-            SnapshotDiffReportOzone.DiffType.RENAME, key2, key2Renamed)));
+            SnapshotDiffReportOzone.DiffType.RENAME, OZONE_URI_DELIMITER + 
key2,
+            OZONE_URI_DELIMITER + key2Renamed)));
 
 
     // Create a directory
@@ -628,14 +633,10 @@ public class TestOmSnapshot {
     SnapshotDiffReportOzone
         diff4 = getSnapDiffReport(volume, bucket, snap4, snap5);
     Assert.assertEquals(1, diff4.getDiffList().size());
-    // for non-fso, directories are a special type of key with "/" appended
-    // at the end.
-    if (!bucket1.getBucketLayout().isFileSystemOptimized()) {
-      dir1 = dir1 + OM_KEY_PREFIX;
-    }
     Assert.assertTrue(diff4.getDiffList().contains(
         SnapshotDiffReportOzone.getDiffReportEntry(
-            SnapshotDiffReportOzone.DiffType.CREATE, dir1)));
+            SnapshotDiffReportOzone.DiffType.CREATE,
+            OM_KEY_PREFIX + dir1)));
 
     String key3 = createFileKeyWithPrefix(bucket1, "key-3-");
     String snap6 = "snap" + counter.incrementAndGet();
@@ -648,17 +649,14 @@ public class TestOmSnapshot {
     createSnapshot(volume, bucket, snap7);
     SnapshotDiffReportOzone
         diff5 = getSnapDiffReport(volume, bucket, snap6, snap7);
-    assertEquals(2, diff5.getDiffList().size());
-    assertEquals(SnapshotDiffReportOzone.DiffType.RENAME,
-        diff5.getDiffList().get(0).getType());
-    assertEquals(key3, org.apache.hadoop.hdds.StringUtils.bytes2String(
-        diff5.getDiffList().get(0).getSourcePath()));
-    assertEquals(renamedKey3, org.apache.hadoop.hdds.StringUtils.bytes2String(
-        diff5.getDiffList().get(0).getTargetPath()));
-    assertEquals(SnapshotDiffReportOzone.DiffType.MODIFY,
-        diff5.getDiffList().get(1).getType());
-    assertEquals(key3, org.apache.hadoop.hdds.StringUtils.bytes2String(
-        diff5.getDiffList().get(1).getSourcePath()));
+    List<SnapshotDiffReport.DiffReportEntry> expectedDiffList =
+        Arrays.asList(SnapshotDiffReportOzone.getDiffReportEntry(
+            SnapshotDiffReport.DiffType.RENAME, OZONE_URI_DELIMITER + key3,
+            OZONE_URI_DELIMITER + renamedKey3),
+            SnapshotDiffReportOzone.getDiffReportEntry(
+                SnapshotDiffReport.DiffType.MODIFY, OZONE_URI_DELIMITER + key3)
+        );
+    assertEquals(expectedDiffList, diff5.getDiffList());
   }
 
   @Test
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/FSODirectoryPathResolver.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/FSODirectoryPathResolver.java
new file mode 100644
index 0000000000..0d0bcb2276
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/FSODirectoryPathResolver.java
@@ -0,0 +1,109 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import com.google.common.collect.Sets;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
+import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Queue;
+import java.util.Set;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
+
+/**
+ * Class to resolve absolute paths for FSO DirectoryInfo Objects.
+ */
+public class FSODirectoryPathResolver implements ObjectPathResolver {
+
+  private final String prefix;
+  private final long bucketId;
+  private final Table<String, OmDirectoryInfo> dirInfoTable;
+
+  public FSODirectoryPathResolver(String prefix, long bucketId,
+      Table<String, OmDirectoryInfo> dirInfoTable) {
+    this.prefix = prefix;
+    this.dirInfoTable = dirInfoTable;
+    this.bucketId = bucketId;
+  }
+
+  private void addToPathMap(Pair<Long, Path> objectIDPath,
+                            Set<Long> dirObjIds, Map<Long, Path> pathMap) {
+    if (dirObjIds.contains(objectIDPath.getKey())) {
+      pathMap.put(objectIDPath.getKey(), objectIDPath.getValue());
+      dirObjIds.remove(objectIDPath.getKey());
+    }
+  }
+
+  /**
+   * Assuming all dirObjIds belong to a bucket this function resolves absolute
+   * path for a given FSO bucket.
+   * @param dirObjIds Object Ids corresponding to which absolute path is 
needed.
+   * @return Map of Path corresponding to provided directory object IDs
+   */
+  @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
+  @Override
+  public Map<Long, Path> getAbsolutePathForObjectIDs(
+      Optional<Set<Long>> dirObjIds) throws IOException {
+    // Root of a bucket would always have the
+    // key as /volumeId/bucketId/bucketId/
+    if (!dirObjIds.isPresent() || dirObjIds.get().isEmpty()) {
+      return Collections.emptyMap();
+    }
+    Set<Long> objIds = Sets.newHashSet(dirObjIds.get());
+    Map<Long, Path> objectIdPathMap = new HashMap<>();
+    Queue<Pair<Long, Path>> objectIdPathVals = new LinkedList<>();
+    Pair<Long, Path> root = Pair.of(bucketId, Paths.get(OZONE_URI_DELIMITER));
+    objectIdPathVals.add(root);
+    addToPathMap(root, objIds, objectIdPathMap);
+
+    while (!objectIdPathVals.isEmpty() && objIds.size() > 0) {
+      Pair<Long, Path> parent = objectIdPathVals.poll();
+      try (TableIterator<String,
+              ? extends Table.KeyValue<String, OmDirectoryInfo>>
+              subDirIter = dirInfoTable.iterator(
+                  prefix + parent.getKey() + OM_KEY_PREFIX)) {
+        while (objIds.size() > 0 && subDirIter.hasNext()) {
+          OmDirectoryInfo childDir = subDirIter.next().getValue();
+          Pair<Long, Path> pathVal = Pair.of(childDir.getObjectID(),
+              parent.getValue().resolve(childDir.getName()));
+          addToPathMap(pathVal, objIds, objectIdPathMap);
+          objectIdPathVals.add(pathVal);
+        }
+      }
+    }
+    // Invalid directory objectId which does not exist in the given bucket.
+    if (objIds.size() > 0) {
+      throw new IllegalArgumentException(
+          "Dir object Ids required but not found in bucket: " + objIds);
+    }
+    return objectIdPathMap;
+  }
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/ObjectPathResolver.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/ObjectPathResolver.java
new file mode 100644
index 0000000000..b9c5bb0c45
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/ObjectPathResolver.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Class to resolve paths of Objects.
+ */
+public interface ObjectPathResolver {
+
+  Map<Long, Path> getAbsolutePathForObjectIDs(Optional<Set<Long>> objIds)
+      throws IOException;
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java
 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java
index 9ad256a016..d4a759e2cd 100644
--- 
a/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java
@@ -20,6 +20,7 @@ package org.apache.hadoop.ozone.om.snapshot;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
 import java.io.BufferedWriter;
@@ -41,6 +42,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.UUID;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.conf.StorageUnit;
 import org.apache.hadoop.hdds.utils.NativeLibraryNotLoadedException;
@@ -61,6 +63,7 @@ import org.apache.hadoop.ozone.OFSPath;
 import org.apache.hadoop.hdds.utils.db.managed.ManagedSSTDumpTool;
 import org.apache.hadoop.hdds.utils.db.managed.ManagedColumnFamilyOptions;
 import org.apache.hadoop.hdds.utils.db.managed.ManagedRocksDB;
+import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.hadoop.ozone.om.OMConfigKeys;
 import org.apache.hadoop.ozone.om.OMMetadataManager;
 import org.apache.hadoop.ozone.om.OmMetadataManagerImpl;
@@ -74,6 +77,7 @@ import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
 import org.apache.hadoop.ozone.om.helpers.WithObjectID;
 import org.apache.hadoop.ozone.om.service.SnapshotDeletingService;
 import 
org.apache.hadoop.ozone.om.snapshot.SnapshotDiffObject.SnapshotDiffObjectBuilder;
+import org.apache.hadoop.ozone.om.helpers.WithParentObjectId;
 import org.apache.hadoop.ozone.snapshot.SnapshotDiffReportOzone;
 import org.apache.hadoop.util.ClosableIterator;
 import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse;
@@ -94,6 +98,7 @@ import org.slf4j.LoggerFactory;
 import java.util.concurrent.SynchronousQueue;
 
 import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
+import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_DIFF_JOB_DEFAULT_WAIT_TIME;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_DIFF_JOB_DEFAULT_WAIT_TIME_DEFAULT;
 import static 
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SNAPSHOT_DIFF_MAX_ALLOWED_KEYS_CHANGED_PER_DIFF_JOB;
@@ -106,7 +111,6 @@ import static 
org.apache.hadoop.ozone.om.OmSnapshotManager.DELIMITER;
 import static org.apache.hadoop.ozone.om.helpers.SnapshotInfo.getTableKey;
 import static 
org.apache.hadoop.ozone.om.snapshot.SnapshotUtils.checkSnapshotActive;
 import static 
org.apache.hadoop.ozone.om.snapshot.SnapshotUtils.dropColumnFamilyHandle;
-import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
 import static 
org.apache.hadoop.ozone.om.snapshot.SnapshotUtils.getSnapshotInfo;
 import static 
org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.CANCELLED;
 import static 
org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus.DONE;
@@ -343,11 +347,12 @@ public class SnapshotDiffManager implements AutoCloseable 
{
     String bucketId = String.valueOf(
         omMetadataManager.getBucketId(volumeName, bucketName));
     tablePrefixes.put(OmMetadataManagerImpl.KEY_TABLE,
-        OM_KEY_PREFIX + volumeName + OM_KEY_PREFIX + bucketName);
+        OM_KEY_PREFIX + volumeName + OM_KEY_PREFIX + bucketName
+        + OM_KEY_PREFIX);
     tablePrefixes.put(OmMetadataManagerImpl.FILE_TABLE,
-        OM_KEY_PREFIX + volumeId + OM_KEY_PREFIX + bucketId);
+        OM_KEY_PREFIX + volumeId + OM_KEY_PREFIX + bucketId + OM_KEY_PREFIX);
     tablePrefixes.put(OmMetadataManagerImpl.DIRECTORY_TABLE,
-        OM_KEY_PREFIX + volumeId + OM_KEY_PREFIX + bucketId);
+        OM_KEY_PREFIX + volumeId + OM_KEY_PREFIX + bucketId + OM_KEY_PREFIX);
     return tablePrefixes;
   }
 
@@ -859,6 +864,25 @@ public class SnapshotDiffManager implements AutoCloseable {
       Table<String, OmKeyInfo> tsKeyTable = toSnapshot.getMetadataManager()
           .getKeyTable(bucketLayout);
 
+      final Optional<Set<Long>> oldParentIds;
+      final Optional<Set<Long>> newParentIds;
+      if (bucketLayout.isFileSystemOptimized()) {
+        oldParentIds = Optional.of(new HashSet<>());
+        newParentIds = Optional.of(new HashSet<>());
+      } else {
+        oldParentIds = Optional.empty();
+        newParentIds = Optional.empty();
+      }
+
+      final Optional<Map<Long, Path>> oldParentIdPathMap;
+      final Optional<Map<Long, Path>> newParentIdPathMap;
+      if (bucketLayout.isFileSystemOptimized()) {
+        oldParentIdPathMap = Optional.of(Maps.newHashMap());
+        newParentIdPathMap = Optional.of(Maps.newHashMap());
+      } else {
+        oldParentIdPathMap = Optional.empty();
+        newParentIdPathMap = Optional.empty();
+      }
       // These are the most time and resource consuming method calls.
       // Split the calls into steps and store them in an array, to avoid
       // repetition while constantly checking if the job is cancelled.
@@ -868,7 +892,7 @@ public class SnapshotDiffManager implements AutoCloseable {
                 fromSnapshot, toSnapshot, fsInfo, tsInfo, useFullDiff,
                 tablePrefixes, objectIdToKeyNameMapForFromSnapshot,
                 objectIdToKeyNameMapForToSnapshot, objectIdToDiffObject,
-                path.toString());
+                oldParentIds, newParentIds, path.toString());
             return null;
           },
           () -> {
@@ -882,7 +906,25 @@ public class SnapshotDiffManager implements AutoCloseable {
                   fromSnapshot, toSnapshot, fsInfo, tsInfo, useFullDiff,
                   tablePrefixes, objectIdToKeyNameMapForFromSnapshot,
                   objectIdToKeyNameMapForToSnapshot, objectIdToDiffObject,
-                  path.toString());
+                  oldParentIds, newParentIds, path.toString());
+            }
+            return null;
+          },
+          () -> {
+            if (bucketLayout.isFileSystemOptimized()) {
+              long bucketId = toSnapshot.getMetadataManager()
+                  .getBucketId(volumeName, bucketName);
+              String tablePrefix = getTablePrefix(tablePrefixes,
+                  fromSnapshot.getMetadataManager()
+                      .getDirectoryTable().getName());
+              oldParentIdPathMap.get().putAll(new FSODirectoryPathResolver(
+                  tablePrefix, bucketId,
+                  fromSnapshot.getMetadataManager().getDirectoryTable())
+                  .getAbsolutePathForObjectIDs(oldParentIds));
+              newParentIdPathMap.get().putAll(new FSODirectoryPathResolver(
+                  tablePrefix, bucketId,
+                  toSnapshot.getMetadataManager().getDirectoryTable())
+                  .getAbsolutePathForObjectIDs(newParentIds));
             }
             return null;
           },
@@ -894,7 +936,9 @@ public class SnapshotDiffManager implements AutoCloseable {
                 objectIdToKeyNameMapForFromSnapshot,
                 objectIdToKeyNameMapForToSnapshot,
                 volumeName, bucketName,
-                fromSnapshotName, toSnapshotName);
+                fromSnapshotName, toSnapshotName,
+                bucketLayout.isFileSystemOptimized(), oldParentIdPathMap,
+                newParentIdPathMap);
             // If job is cancelled, totalDiffEntries will be equal to -1.
             if (totalDiffEntries >= 0 &&
                 areDiffJobAndSnapshotsActive(volumeName, bucketName,
@@ -939,19 +983,17 @@ public class SnapshotDiffManager implements AutoCloseable 
{
 
   @SuppressWarnings("checkstyle:ParameterNumber")
   private void getDeltaFilesAndDiffKeysToObjectIdToKeyMap(
-      final Table<String, ? extends WithObjectID> fsTable,
-      final Table<String, ? extends WithObjectID> tsTable,
-      final OmSnapshot fromSnapshot,
-      final OmSnapshot toSnapshot,
-      final SnapshotInfo fsInfo,
-      final SnapshotInfo tsInfo,
-      final boolean useFullDiff,
-      final Map<String, String> tablePrefixes,
+      final Table<String, ? extends WithParentObjectId> fsTable,
+      final Table<String, ? extends WithParentObjectId> tsTable,
+      final OmSnapshot fromSnapshot, final OmSnapshot toSnapshot,
+      final SnapshotInfo fsInfo, final SnapshotInfo tsInfo,
+      final boolean useFullDiff, final Map<String, String> tablePrefixes,
       final PersistentMap<byte[], byte[]> oldObjIdToKeyMap,
       final PersistentMap<byte[], byte[]> newObjIdToKeyMap,
       final PersistentMap<byte[], SnapshotDiffObject> objectIdToDiffObject,
-      final String diffDir
-  ) throws IOException, RocksDBException {
+      final Optional<Set<Long>> oldParentIds,
+      final Optional<Set<Long>> newParentIds,
+      final String diffDir) throws IOException, RocksDBException {
 
     List<String> tablesToLookUp = Collections.singletonList(fsTable.getName());
 
@@ -974,6 +1016,8 @@ public class SnapshotDiffManager implements AutoCloseable {
           oldObjIdToKeyMap,
           newObjIdToKeyMap,
           objectIdToDiffObject,
+          oldParentIds,
+          newParentIds,
           tablePrefixes);
     } catch (NativeLibraryNotLoadedException e) {
       LOG.warn("SSTDumpTool load failure, retrying without it.", e);
@@ -990,6 +1034,8 @@ public class SnapshotDiffManager implements AutoCloseable {
             oldObjIdToKeyMap,
             newObjIdToKeyMap,
             objectIdToDiffObject,
+            oldParentIds,
+            newParentIds,
             tablePrefixes);
       } catch (NativeLibraryNotLoadedException ex) {
         throw new IllegalStateException(ex);
@@ -998,19 +1044,20 @@ public class SnapshotDiffManager implements 
AutoCloseable {
   }
 
   @SuppressWarnings("checkstyle:ParameterNumber")
-  void addToObjectIdMap(
-      final Table<String, ? extends WithObjectID> fsTable,
-      final Table<String, ? extends WithObjectID> tsTable,
-      final Set<String> deltaFiles, boolean nativeRocksToolsLoaded,
-      final PersistentMap<byte[], byte[]> oldObjIdToKeyMap,
-      final PersistentMap<byte[], byte[]> newObjIdToKeyMap,
+  void addToObjectIdMap(Table<String, ? extends WithParentObjectId> fsTable,
+      Table<String, ? extends WithParentObjectId> tsTable,
+      Set<String> deltaFiles, boolean nativeRocksToolsLoaded,
+      PersistentMap<byte[], byte[]> oldObjIdToKeyMap,
+      PersistentMap<byte[], byte[]> newObjIdToKeyMap,
       final PersistentMap<byte[], SnapshotDiffObject> objectIdToDiffObject,
-      final Map<String, String> tablePrefixes
-  ) throws IOException,
+      Optional<Set<Long>> oldParentIds,
+      Optional<Set<Long>> newParentIds,
+      Map<String, String> tablePrefixes) throws IOException,
       NativeLibraryNotLoadedException, RocksDBException {
     if (deltaFiles.isEmpty()) {
       return;
     }
+    String tablePrefix = getTablePrefix(tablePrefixes, fsTable.getName());
     boolean isDirectoryTable =
         fsTable.getName().equals(OmMetadataManagerImpl.DIRECTORY_TABLE);
     ManagedSstFileReader sstFileReader = new ManagedSstFileReader(deltaFiles);
@@ -1022,8 +1069,8 @@ public class SnapshotDiffManager implements AutoCloseable 
{
                  : sstFileReader.getKeyStream()) {
       keysToCheck.forEach(key -> {
         try {
-          final WithObjectID fromObjectId = fsTable.get(key);
-          final WithObjectID toObjectId = tsTable.get(key);
+          final WithParentObjectId fromObjectId = fsTable.get(key);
+          final WithParentObjectId toObjectId = tsTable.get(key);
           if (areKeysEqual(fromObjectId, toObjectId) || !isKeyInBucket(key,
               tablePrefixes, fsTable.getName())) {
             // We don't have to do anything.
@@ -1032,23 +1079,31 @@ public class SnapshotDiffManager implements 
AutoCloseable {
           if (fromObjectId != null) {
             byte[] rawObjId = codecRegistry.asRawData(
                 fromObjectId.getObjectID());
+            // Removing volume bucket info by removing the table bucket Prefix
+            // from the key.
+            // For FSO buckets will be left with the parent id/keyname.
+            // For OBS buckets will be left with the complete path
             byte[] rawValue = codecRegistry.asRawData(
-                getKeyOrDirectoryName(isDirectoryTable, fromObjectId));
+                key.substring(tablePrefix.length()));
             oldObjIdToKeyMap.put(rawObjId, rawValue);
             SnapshotDiffObject diffObject =
                 createDiffObjectWithOldName(fromObjectId.getObjectID(), key,
-                objectIdToDiffObject.get(rawObjId));
+                    objectIdToDiffObject.get(rawObjId));
             objectIdToDiffObject.put(rawObjId, diffObject);
+            oldParentIds.ifPresent(set -> set.add(
+                fromObjectId.getParentObjectID()));
           }
           if (toObjectId != null) {
             byte[] rawObjId = 
codecRegistry.asRawData(toObjectId.getObjectID());
             byte[] rawValue = codecRegistry.asRawData(
-                getKeyOrDirectoryName(isDirectoryTable, toObjectId));
+                key.substring(tablePrefix.length()));
             newObjIdToKeyMap.put(rawObjId, rawValue);
             SnapshotDiffObject diffObject =
                 createDiffObjectWithNewName(toObjectId.getObjectID(), key,
                     objectIdToDiffObject.get(rawObjId));
             objectIdToDiffObject.put(rawObjId, diffObject);
+            newParentIds.ifPresent(set -> set.add(toObjectId
+                .getParentObjectID()));
           }
         } catch (IOException e) {
           throw new RuntimeException(e);
@@ -1159,6 +1214,25 @@ public class SnapshotDiffManager implements 
AutoCloseable {
     }
   }
 
+  @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
+  private String resolveAbsolutePath(boolean isFSOBucket,
+          final Optional<Map<Long, Path>> parentIdMap, byte[] keyVal)
+      throws IOException {
+    String key = codecRegistry.asObject(keyVal, String.class);
+    if (isFSOBucket) {
+      String[] splitKey = key.split(OM_KEY_PREFIX, 2);
+      Long parentId = Long.valueOf(splitKey[0]);
+      if (parentIdMap.map(m -> !m.containsKey(parentId)).orElse(true)) {
+        throw new IllegalStateException(String.format(
+            "Cannot resolve path for key: %s with parent Id: %d", key,
+            parentId));
+      }
+      return parentIdMap.map(m -> m.get(parentId).resolve(splitKey[1]))
+          .get().toString();
+    }
+    return Paths.get(OzoneConsts.OZONE_URI_DELIMITER).resolve(key).toString();
+  }
+
   @SuppressWarnings({"checkstyle:ParameterNumber", "checkstyle:MethodLength"})
   long generateDiffReport(
       final String jobId,
@@ -1170,7 +1244,10 @@ public class SnapshotDiffManager implements 
AutoCloseable {
       final String volumeName,
       final String bucketName,
       final String fromSnapshotName,
-      final String toSnapshotName) {
+      final String toSnapshotName,
+      final boolean isFSOBucket,
+      final Optional<Map<Long, Path>> oldParentIdPathMap,
+      final Optional<Map<Long, Path>> newParentIdPathMap) {
     LOG.info("Starting diff report generation for jobId: {}.", jobId);
     ColumnFamilyHandle deleteDiffColumnFamily = null;
     ColumnFamilyHandle renameDiffColumnFamily = null;
@@ -1236,26 +1313,31 @@ public class SnapshotDiffManager implements 
AutoCloseable {
             throw new IllegalStateException(
                 "Old and new key name both are null");
           } else if (oldKeyName == null) { // Key Created.
-            String key = codecRegistry.asObject(newKeyName, String.class);
+            String key = resolveAbsolutePath(isFSOBucket, newParentIdPathMap,
+                newKeyName);
             DiffReportEntry entry =
                 SnapshotDiffReportOzone.getDiffReportEntry(DiffType.CREATE,
                     key);
             createDiffs.add(codecRegistry.asRawData(entry));
           } else if (newKeyName == null) { // Key Deleted.
-            String key = codecRegistry.asObject(oldKeyName, String.class);
+            String key = resolveAbsolutePath(isFSOBucket, oldParentIdPathMap,
+                oldKeyName);
             DiffReportEntry entry =
                 SnapshotDiffReportOzone.getDiffReportEntry(DiffType.DELETE,
                     key);
             deleteDiffs.add(codecRegistry.asRawData(entry));
           } else if (Arrays.equals(oldKeyName, newKeyName)) { // Key modified.
-            String key = codecRegistry.asObject(newKeyName, String.class);
+            String key = resolveAbsolutePath(isFSOBucket, newParentIdPathMap,
+                newKeyName);
             DiffReportEntry entry =
                 SnapshotDiffReportOzone.getDiffReportEntry(DiffType.MODIFY,
                     key);
             modifyDiffs.add(codecRegistry.asRawData(entry));
-          } else {
-            String oldKey = codecRegistry.asObject(oldKeyName, String.class);
-            String newKey = codecRegistry.asObject(newKeyName, String.class);
+          } else { // Key Renamed.
+            String oldKey = resolveAbsolutePath(isFSOBucket, 
oldParentIdPathMap,
+                oldKeyName);
+            String newKey = resolveAbsolutePath(isFSOBucket, 
newParentIdPathMap,
+                newKeyName);
             renameDiffs.add(codecRegistry.asRawData(
                 SnapshotDiffReportOzone.getDiffReportEntry(DiffType.RENAME,
                     oldKey, newKey)));
@@ -1440,23 +1522,27 @@ public class SnapshotDiffManager implements 
AutoCloseable {
   }
 
   /**
-   * check if the given key is in the bucket specified by tablePrefix map.
+   * Get table prefix given a tableName.
    */
-  boolean isKeyInBucket(String key, Map<String, String> tablePrefixes,
-                        String tableName) {
-    String volumeBucketDbPrefix;
+  private String getTablePrefix(Map<String, String> tablePrefixes,
+                                String tableName) {
     // In case of FSO - either File/Directory table
     // the key Prefix would be volumeId/bucketId and
     // in case of non-fso - volumeName/bucketName
     if (tableName.equals(
         OmMetadataManagerImpl.DIRECTORY_TABLE) || tableName.equals(
         OmMetadataManagerImpl.FILE_TABLE)) {
-      volumeBucketDbPrefix =
-          tablePrefixes.get(OmMetadataManagerImpl.DIRECTORY_TABLE);
-    } else {
-      volumeBucketDbPrefix = 
tablePrefixes.get(OmMetadataManagerImpl.KEY_TABLE);
+      return tablePrefixes.get(OmMetadataManagerImpl.DIRECTORY_TABLE);
     }
-    return key.startsWith(volumeBucketDbPrefix);
+    return tablePrefixes.get(OmMetadataManagerImpl.KEY_TABLE);
+  }
+
+  /**
+   * check if the given key is in the bucket specified by tablePrefix map.
+   */
+  boolean isKeyInBucket(String key, Map<String, String> tablePrefixes,
+      String tableName) {
+    return key.startsWith(getTablePrefix(tablePrefixes, tableName));
   }
 
   /**
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestFSODirectoryPathResolver.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestFSODirectoryPathResolver.java
new file mode 100644
index 0000000000..f56b8657e4
--- /dev/null
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestFSODirectoryPathResolver.java
@@ -0,0 +1,150 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.apache.hadoop.hdds.utils.db.Table;
+import org.apache.hadoop.hdds.utils.db.TableIterator;
+import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.apache.hadoop.ozone.OzoneConsts.OM_KEY_PREFIX;
+
+/**
+ * Test class for FSODirectoryPathResolver.
+ */
+public class TestFSODirectoryPathResolver {
+
+  private Table<String, OmDirectoryInfo> getMockedDirectoryInfoTable(
+      String prefix, Map<Integer, List<Integer>> dirMap) throws IOException {
+    Table<String, OmDirectoryInfo> dirInfos = Mockito.mock(Table.class);
+
+    Mockito.when(dirInfos.iterator(Mockito.anyString()))
+        .thenAnswer(i -> {
+          int dirId = Integer.parseInt(((String)i.getArgument(0))
+               .split(OM_KEY_PREFIX)[3]);
+          Iterator<? extends Table.KeyValue<String, OmDirectoryInfo>> iterator 
=
+              dirMap
+              .getOrDefault(dirId, Collections.emptyList()).stream()
+              .map(children -> new Table.KeyValue<String, OmDirectoryInfo>() {
+                  @Override
+                  public String getKey() {
+                    return prefix + children + OM_KEY_PREFIX + "dir" + 
children;
+                  }
+
+                  @Override
+                  public OmDirectoryInfo getValue() {
+                    return OmDirectoryInfo.newBuilder()
+                        .setName("dir" + children).setObjectID(children)
+                        .build();
+                  }
+              })
+              .iterator();
+          return new TableIterator<String,
+              Table.KeyValue<String, OmDirectoryInfo>>() {
+
+            @Override
+            public boolean hasNext() {
+              return iterator.hasNext();
+            }
+
+            @Override
+            public Table.KeyValue<String, OmDirectoryInfo> next() {
+              return iterator.next();
+            }
+
+            @Override
+            public void close() {
+            }
+
+            @Override
+            public void seekToFirst() {
+            }
+
+            @Override
+            public void seekToLast() {
+            }
+
+            @Override
+            public Table.KeyValue<String, OmDirectoryInfo> seek(String s) {
+              return null;
+            }
+
+            @Override
+            public void removeFromDB() {
+
+            }
+          };
+
+        });
+
+    return dirInfos;
+  }
+
+  @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
+  @Test
+  public void testGetAbsolutePathForValidObjectIDs() throws IOException {
+    Map<Integer, List<Integer>> dirMap = ImmutableMap.of(
+        1, Lists.newArrayList(2, 3, 4, 5, 6),
+        2, Lists.newArrayList(7, 8, 9, 10, 11),
+        3, Lists.newArrayList(12, 13, 14, 15),
+        9, Lists.newArrayList(16),
+        14, Lists.newArrayList(17),
+        18, Lists.newArrayList(19, 20)
+        );
+    String prefix = "/vol/buck/";
+    FSODirectoryPathResolver fsoDirectoryPathResolver =
+        new FSODirectoryPathResolver(prefix, 1,
+            getMockedDirectoryInfoTable(prefix, dirMap));
+    Set<Long> objIds = Sets.newHashSet(17L, 9L, 10L, 15L, 4L, 3L, 1L);
+    Map<Long, Path> absolutePathMap = fsoDirectoryPathResolver
+        .getAbsolutePathForObjectIDs(Optional.of(objIds));
+
+    Assertions.assertEquals(ImmutableMap.of(
+        17L, Paths.get("/dir3/dir14/dir17"),
+        9L, Paths.get("/dir2/dir9"),
+        10L, Paths.get("/dir2/dir10"),
+        15L, Paths.get("/dir3/dir15"),
+        4L, Paths.get("/dir4"),
+        3L, Paths.get("/dir3"),
+        1L, Paths.get("/")
+    ), absolutePathMap);
+    Assertions.assertEquals(objIds.size(), absolutePathMap.size());
+    // Invalid Obj Id 19 with dirInfo dir19 which is not present in the bucket.
+    Assertions.assertThrows(IllegalArgumentException.class,
+        () -> fsoDirectoryPathResolver.getAbsolutePathForObjectIDs(
+            Optional.of(Sets.newHashSet(17L, 9L, 10L, 15L, 4L, 3L, 1L, 19L))),
+        "Dir object Ids required but not found in bucket: [19]");
+  }
+
+}
diff --git 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java
 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java
index 355cce7774..fa7d99e5a2 100644
--- 
a/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java
+++ 
b/hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java
@@ -20,8 +20,8 @@ package org.apache.hadoop.ozone.om.snapshot;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.commons.lang3.RandomStringUtils;
@@ -48,8 +48,8 @@ import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
 import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
 import org.apache.hadoop.ozone.om.helpers.SnapshotDiffJob;
 import org.apache.hadoop.ozone.om.helpers.SnapshotInfo;
-import org.apache.hadoop.ozone.om.helpers.WithObjectID;
 import 
org.apache.hadoop.ozone.om.snapshot.SnapshotDiffObject.SnapshotDiffObjectBuilder;
+import org.apache.hadoop.ozone.om.helpers.WithParentObjectId;
 import org.apache.hadoop.ozone.snapshot.SnapshotDiffReportOzone;
 import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse;
 import org.apache.hadoop.ozone.snapshot.SnapshotDiffResponse.JobStatus;
@@ -303,24 +303,26 @@ public class TestSnapshotDiffManager {
     }
   }
 
-  private Table<String, ? extends WithObjectID> getMockedTable(
-      Map<String, WithObjectID> map, String tableName)
+  private Table<String, ? extends WithParentObjectId> getMockedTable(
+      Map<String, WithParentObjectId> map, String tableName)
       throws IOException {
-    Table<String, ? extends WithObjectID> mocked = mock(Table.class);
+    Table<String, ? extends WithParentObjectId> mocked = mock(Table.class);
     Mockito.when(mocked.get(Matchers.any()))
         .thenAnswer(invocation -> map.get(invocation.getArgument(0)));
     Mockito.when(mocked.getName()).thenReturn(tableName);
     return mocked;
   }
 
-  private WithObjectID getObjectID(int objectId, int updateId,
-                                   String snapshotTableName) {
+  private WithParentObjectId getKeyInfo(int objectId, int updateId,
+                                        int parentObjectId,
+                                        String snapshotTableName) {
     String name = "key" + objectId;
     if (snapshotTableName.equals(OmMetadataManagerImpl.DIRECTORY_TABLE)) {
       return OmDirectoryInfo.newBuilder()
           .setObjectID(objectId).setName(name).build();
     }
     return new OmKeyInfo.Builder().setObjectID(objectId)
+        .setParentObjectID(parentObjectId)
         .setVolumeName("vol").setBucketName("bucket").setUpdateID(updateId)
         .setReplicationConfig(new ECReplicationConfig(3, 2))
         .setKeyName(name).build();
@@ -355,10 +357,10 @@ public class TestSnapshotDiffManager {
       throws NativeLibraryNotLoadedException, IOException, RocksDBException {
     // Mocking SST file with keys in SST file including tombstones
     Set<String> keysWithTombstones = IntStream.range(0, 100)
-        .boxed().map(i -> "key" + i).collect(Collectors.toSet());
+        .boxed().map(i -> (i + 100) + "/key" + i).collect(Collectors.toSet());
     // Mocking SST file with keys in SST file excluding tombstones
     Set<String> keys = IntStream.range(0, 50).boxed()
-        .map(i -> "key" + i).collect(Collectors.toSet());
+        .map(i -> (i + 100) + "/key" + i).collect(Collectors.toSet());
     // Mocking SSTFileReader functions to return the above keys list.
     try (MockedConstruction<ManagedSstFileReader> mockedSSTFileReader =
              Mockito.mockConstruction(ManagedSstFileReader.class,
@@ -374,22 +376,23 @@ public class TestSnapshotDiffManager {
                  })
     ) {
       //
-      Map<String, WithObjectID> toSnapshotTableMap =
+      Map<String, WithParentObjectId> toSnapshotTableMap =
           IntStream.concat(IntStream.range(0, 25), IntStream.range(50, 100))
-              .boxed().collect(Collectors.toMap(i -> "key" + i,
-                  i -> getObjectID(i, i, snapshotTableName)));
+              .boxed().collect(Collectors.toMap(i -> (i + 100) + "/key" + i,
+                  i -> getKeyInfo(i, i, i + 100,
+                      snapshotTableName)));
       // Mocking To snapshot table containing list of keys b/w 0-25, 50-100
-      Table<String, ? extends WithObjectID> toSnapshotTable =
+      Table<String, ? extends WithParentObjectId> toSnapshotTable =
           getMockedTable(toSnapshotTableMap, snapshotTableName);
       // Mocking To snapshot table containing list of keys b/w 0-50
-      Map<String, WithObjectID> fromSnapshotTableMap =
+      Map<String, WithParentObjectId> fromSnapshotTableMap =
           IntStream.range(0, 50)
-              .boxed().collect(Collectors.toMap(i -> "key" + i,
-                  i -> getObjectID(i, i, snapshotTableName)));
+              .boxed().collect(Collectors.toMap(i -> (i + 100) + "/key" + i,
+                  i -> getKeyInfo(i, i, i + 100, snapshotTableName)));
       // Expected Diff 25-50 are newly created keys & keys b/w are deleted,
       // when reding keys with tombstones the keys would be added to
       // objectIdsToBeChecked otherwise it wouldn't be added
-      Table<String, ? extends WithObjectID> fromSnapshotTable =
+      Table<String, ? extends WithParentObjectId> fromSnapshotTable =
           getMockedTable(fromSnapshotTableMap, snapshotTableName);
       SnapshotDiffManager snapshotDiffManager =
           getMockedSnapshotDiffManager(10);
@@ -397,7 +400,7 @@ public class TestSnapshotDiffManager {
       // Odd keys should be filtered out in the diff.
       Mockito.doAnswer((Answer<Boolean>) invocationOnMock ->
           Integer.parseInt(invocationOnMock.getArgument(0, String.class)
-              .substring(3)) % 2 == 0).when(snapshotDiffManager)
+              .substring(7)) % 2 == 0).when(snapshotDiffManager)
           .isKeyInBucket(Matchers.anyString(), Matchers.anyMap(),
               Matchers.anyString());
       PersistentMap<byte[], byte[]> oldObjectIdKeyMap =
@@ -406,10 +409,16 @@ public class TestSnapshotDiffManager {
           new SnapshotTestUtils.StubbedPersistentMap<>();
       PersistentMap<byte[], SnapshotDiffObject> objectIdsToCheck =
           new SnapshotTestUtils.StubbedPersistentMap<>();
+      Set<Long> oldParentIds = Sets.newHashSet();
+      Set<Long> newParentIds = Sets.newHashSet();
       snapshotDiffManager.addToObjectIdMap(toSnapshotTable,
           fromSnapshotTable, Sets.newHashSet("dummy.sst"),
           nativeLibraryLoaded, oldObjectIdKeyMap, newObjectIdKeyMap,
-          objectIdsToCheck, Maps.newHashMap());
+          objectIdsToCheck, Optional.ofNullable(oldParentIds),
+          Optional.ofNullable(newParentIds),
+          ImmutableMap.of(OmMetadataManagerImpl.DIRECTORY_TABLE, "",
+              OmMetadataManagerImpl.KEY_TABLE, "",
+              OmMetadataManagerImpl.FILE_TABLE, ""));
 
       Iterator<Entry<byte[], byte[]>> oldObjectIdIter =
           oldObjectIdKeyMap.iterator();
@@ -574,7 +583,8 @@ public class TestSnapshotDiffManager {
 
       snapshotDiffManager.generateDiffReport("jobId", fromSnapTable,
           toSnapTable, objectIdToDiffObject, oldObjectIdKeyMap,
-          newObjectIdKeyMap, volumeName, bucketName, fromSnapName, toSnapName);
+          newObjectIdKeyMap, volumeName, bucketName, fromSnapName, toSnapName,
+          false, null, null);
 
       snapshotDiffJob.setStatus(JobStatus.DONE);
       snapshotDiffManager.getSnapDiffJobTable().put(jobKey, snapshotDiffJob);
@@ -595,7 +605,7 @@ public class TestSnapshotDiffManager {
         actualOrder.add(entry.getType());
 
         long objectId = Long.parseLong(
-            DFSUtilClient.bytes2String(entry.getSourcePath()).substring(3));
+            DFSUtilClient.bytes2String(entry.getSourcePath()).substring(4));
         Assertions.assertEquals(diffMap.get(objectId), entry.getType());
       }
       Assertions.assertEquals(expectedOrder, actualOrder);


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

Reply via email to