Copilot commented on code in PR #9079:
URL: https://github.com/apache/ozone/pull/9079#discussion_r3049102758


##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -1180,6 +1199,37 @@ Set<String> getDeltaFiles(OmSnapshot fromSnapshot,
     // TODO: [SNAPSHOT] Refactor the parameter list
     Optional<Set<String>> deltaFiles = Optional.empty();
 
+    OmSnapshotLocalData fromSnapshotLocalData = getSnapshotLocalData(fsInfo);
+    int fromSnapshotVersion = fromSnapshotLocalData.getVersion();
+    OmSnapshotLocalData toSnapshotLocalData = getSnapshotLocalData(tsInfo);
+    int toSnapshotVersion = toSnapshotLocalData.getVersion();
+
+    try {
+      if (fromSnapshotVersion > 0 && toSnapshotVersion > 0) {
+        // both snapshots are defragmented, To calculate snap-diff, we can 
simply compare the
+        // SST files contained in OmSnapshotLocalData instances of both these 
and get the delta files
+        OmSnapshotLocalData.VersionMeta toSnapVersionMeta =
+            
toSnapshotLocalData.getVersionSstFileInfos().get(toSnapshotVersion);
+        // get the source snapshot SST files (versionMeta)  corresponding to 
target snapshot
+        OmSnapshotLocalData.VersionMeta fromSnapVersionMeta =
+            resolveBaseVersionMeta(toSnapshotLocalData, 
fromSnapshot.getSnapshotID());
+        // Calculate diff files using helper method
+        if (toSnapVersionMeta == null) {
+          String errMsg =
+              "Cannot find corresponding version of from snapshot " + 
fromSnapshotVersion + " from " + tsInfo;
+          LOG.error(errMsg);
+          throw new IOException(errMsg);
+        }
+        List<String> diffFiles = calculateDiffFiles(fromSnapVersionMeta, 
toSnapVersionMeta);
+        return OmSnapshotUtils.getSSTDiffListWithFullPath(diffFiles,
+            
OmSnapshotManager.getSnapshotPath(ozoneManager.getMetadataManager(), 
fsInfo).toString(),
+            
OmSnapshotManager.getSnapshotPath(ozoneManager.getMetadataManager(), 
tsInfo).toString(), diffDir);

Review Comment:
   `getDeltaFiles` now unconditionally reads snapshot-local YAML via 
`getSnapshotLocalData(fsInfo/tsInfo)` before entering the try/catch and before 
checking `fsInfo/tsInfo` for null. If either SnapshotInfo is null (which this 
method already guards for later) or if YAML loading throws, the exception will 
propagate and prevent the intended fallback to DAG/full-diff paths. Consider 
moving these calls inside the try block and guarding them with `fsInfo != null 
&& tsInfo != null` so failures fall back cleanly.
   ```suggestion
       try {
         if (fsInfo != null && tsInfo != null) {
           OmSnapshotLocalData fromSnapshotLocalData = 
getSnapshotLocalData(fsInfo);
           int fromSnapshotVersion = fromSnapshotLocalData.getVersion();
           OmSnapshotLocalData toSnapshotLocalData = 
getSnapshotLocalData(tsInfo);
           int toSnapshotVersion = toSnapshotLocalData.getVersion();
   
           if (fromSnapshotVersion > 0 && toSnapshotVersion > 0) {
             // both snapshots are defragmented, To calculate snap-diff, we can 
simply compare the
             // SST files contained in OmSnapshotLocalData instances of both 
these and get the delta files
             OmSnapshotLocalData.VersionMeta toSnapVersionMeta =
                 
toSnapshotLocalData.getVersionSstFileInfos().get(toSnapshotVersion);
             // get the source snapshot SST files (versionMeta)  corresponding 
to target snapshot
             OmSnapshotLocalData.VersionMeta fromSnapVersionMeta =
                 resolveBaseVersionMeta(toSnapshotLocalData, 
fromSnapshot.getSnapshotID());
             // Calculate diff files using helper method
             if (toSnapVersionMeta == null) {
               String errMsg =
                   "Cannot find corresponding version of from snapshot " + 
fromSnapshotVersion + " from " + tsInfo;
               LOG.error(errMsg);
               throw new IOException(errMsg);
             }
             List<String> diffFiles = calculateDiffFiles(fromSnapVersionMeta, 
toSnapVersionMeta);
             return OmSnapshotUtils.getSSTDiffListWithFullPath(diffFiles,
                 
OmSnapshotManager.getSnapshotPath(ozoneManager.getMetadataManager(), 
fsInfo).toString(),
                 
OmSnapshotManager.getSnapshotPath(ozoneManager.getMetadataManager(), 
tsInfo).toString(), diffDir);
           }
   ```



##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -1223,6 +1273,81 @@ Set<String> getDeltaFiles(OmSnapshot fromSnapshot,
             toSnapshot.getSnapshotTableKey()));
   }
 
+  /**
+   * Resolve the VersionMeta of the ancestor snapshot (fromSnapshotId)
+   * that the given snapshot (toSnapshot) was built on.
+   * Traverses the snapshot chain backwards using prevSnapId.
+   *
+   * @param toSnapshot the target snapshot.
+   * @param fromSnapshotId UUID of the source snapshot.
+   * @return the resolved VersionMeta of the source that was used to build the 
target.
+   */
+  private OmSnapshotLocalData.VersionMeta resolveBaseVersionMeta(
+      OmSnapshotLocalData toSnapshot,
+      UUID fromSnapshotId) throws IOException {
+    OmMetadataManagerImpl metadataManager =
+        (OmMetadataManagerImpl) ozoneManager.getMetadataManager();
+    // Start walking back from the child snapshot
+    OmSnapshotLocalData child = toSnapshot;
+    int numSnapshotsTraversed = 0;
+    while (numSnapshotsTraversed <= maxSnapshotLimit && 
!child.getPreviousSnapshotId().equals(fromSnapshotId)) {
+      UUID parentId = child.getPreviousSnapshotId();
+      // Load the parent snapshot in the chain
+      child = getSnapshotLocalData(
+          getSnapshotInfo(ozoneManager,
+              metadataManager.getSnapshotChainManager(),
+              parentId));

Review Comment:
   `resolveBaseVersionMeta` calls 
`child.getPreviousSnapshotId().equals(fromSnapshotId)` in the loop condition. 
Since snapshot local YAML can now have a null previousSnapshotId, this can 
throw NPE and spam error logs during normal operation. Add explicit handling 
for `child.getPreviousSnapshotId() == null` (eg return null to trigger 
fallback) and consider using `numSnapshotsTraversed < maxSnapshotLimit` to 
avoid the current off-by-one traversal behavior.



##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/SnapshotDiffManager.java:
##########
@@ -1180,6 +1199,37 @@ Set<String> getDeltaFiles(OmSnapshot fromSnapshot,
     // TODO: [SNAPSHOT] Refactor the parameter list
     Optional<Set<String>> deltaFiles = Optional.empty();
 
+    OmSnapshotLocalData fromSnapshotLocalData = getSnapshotLocalData(fsInfo);
+    int fromSnapshotVersion = fromSnapshotLocalData.getVersion();
+    OmSnapshotLocalData toSnapshotLocalData = getSnapshotLocalData(tsInfo);
+    int toSnapshotVersion = toSnapshotLocalData.getVersion();
+
+    try {
+      if (fromSnapshotVersion > 0 && toSnapshotVersion > 0) {
+        // both snapshots are defragmented, To calculate snap-diff, we can 
simply compare the
+        // SST files contained in OmSnapshotLocalData instances of both these 
and get the delta files
+        OmSnapshotLocalData.VersionMeta toSnapVersionMeta =
+            
toSnapshotLocalData.getVersionSstFileInfos().get(toSnapshotVersion);
+        // get the source snapshot SST files (versionMeta)  corresponding to 
target snapshot
+        OmSnapshotLocalData.VersionMeta fromSnapVersionMeta =
+            resolveBaseVersionMeta(toSnapshotLocalData, 
fromSnapshot.getSnapshotID());
+        // Calculate diff files using helper method
+        if (toSnapVersionMeta == null) {
+          String errMsg =
+              "Cannot find corresponding version of from snapshot " + 
fromSnapshotVersion + " from " + tsInfo;

Review Comment:
   The error message when `toSnapVersionMeta == null` is misleading: it 
mentions "corresponding version of from snapshot" but the null being checked is 
the *to* snapshot's VersionMeta for `toSnapshotVersion`, and it prints 
`fromSnapshotVersion` rather than `toSnapshotVersion`. Updating the message 
(and including both snapshot IDs/versions) would make diagnosing 
YAML/version-map issues much easier.
   ```suggestion
                 "Cannot find VersionMeta for to snapshot [id="
                     + toSnapshot.getSnapshotID() + ", version=" + 
toSnapshotVersion
                     + "] while calculating diff from from snapshot [id="
                     + fromSnapshot.getSnapshotID() + ", version="
                     + fromSnapshotVersion + "], fsInfo=" + fsInfo
                     + ", tsInfo=" + tsInfo;
   ```



##########
hadoop-ozone/ozone-manager/src/test/java/org/apache/hadoop/ozone/om/snapshot/TestSnapshotDiffManager.java:
##########
@@ -1651,4 +1685,242 @@ public void testGetSnapshotDiffReportJob() throws 
Exception {
       }
     }
   }
+
+  @Test
+  public void testGetDeltaFilesWithDefragmentedSnapshotsSameVersion() throws 
IOException {
+    // Setup test snapshots
+    UUID fromSnapId = UUID.randomUUID();
+    UUID toSnapId = UUID.randomUUID();
+    String fromSnapName = "fromSnap-" + fromSnapId;
+    String toSnapName = "toSnap-" + toSnapId;
+    // Create mock snapshots
+    OmSnapshot fromSnapshot = getMockedOmSnapshot(fromSnapId);
+    OmSnapshot toSnapshot = getMockedOmSnapshot(toSnapId);
+    SnapshotInfo fromSnapshotInfo = getSnapshotInfoInstance(VOLUME_NAME, 
BUCKET_NAME, fromSnapName, fromSnapId);
+    SnapshotInfo toSnapshotInfo = getSnapshotInfoInstance(VOLUME_NAME, 
BUCKET_NAME, toSnapName, toSnapId);
+    when(ozoneManager.getMetadataManager()).thenReturn(omMetadataManagerImpl);
+    // Create mock local data with version 1 for both snapshots
+    // fromSnapshot has: [common.sst]
+    // toSnapshot has: [common.sst, unique_to.sst]
+    // Expected diff: [unique_to.sst]
+    OmSnapshotLocalData fromLocalData = createMockVersionedLocalData(1,
+        Arrays.asList("common.sst"), null, 0);
+    OmSnapshotLocalData toLocalData = createMockVersionedLocalData(1,
+        Arrays.asList("common.sst", "unique_to.sst"), fromSnapId, 1);
+    SnapshotDiffManager spy = spy(snapshotDiffManager);
+    // Mock dependencies
+    doNothing().when(spy).recordActivity(any(), any());
+    doNothing().when(spy).updateProgress(anyString(), anyDouble());
+    doReturn(fromLocalData).when(spy).getSnapshotLocalData(fromSnapshotInfo);
+    doReturn(toLocalData).when(spy).getSnapshotLocalData(toSnapshotInfo);
+
+    String diffJobKey = fromSnapId + DELIMITER + toSnapId;
+    String diffDir = snapDiffDir.getAbsolutePath();
+
+    try (MockedStatic<OmSnapshotUtils> mockedUtils = 
mockStatic(OmSnapshotUtils.class);
+        MockedStatic<OmSnapshotManager> mockedManager = 
mockStatic(OmSnapshotManager.class);
+        MockedStatic<SnapshotUtils> snapshotUtilsMockedStatic = 
mockStatic(SnapshotUtils.class)) {
+      // Mock path resolution
+      mockedManager.when(
+              () -> 
OmSnapshotManager.getSnapshotPath(any(OmMetadataManagerImpl.class), 
eq(fromSnapshotInfo)))
+          .thenReturn(snapDiffDir.toPath().resolve("from"));
+      mockedManager.when(() -> 
OmSnapshotManager.getSnapshotPath(any(OmMetadataManagerImpl.class), 
eq(toSnapshotInfo)))
+          .thenReturn(snapDiffDir.toPath().resolve("to"));
+      snapshotUtilsMockedStatic.when(() -> SnapshotUtils.getSnapshotInfo(
+          eq(ozoneManager),
+          any(),
+          eq(fromSnapId))).thenReturn(fromSnapshotInfo);
+
+      snapshotUtilsMockedStatic.when(() -> SnapshotUtils.getSnapshotInfo(
+          eq(ozoneManager),
+          any(),
+          eq(toSnapId))).thenReturn(toSnapshotInfo);
+      // Expected diff files: files that are different between snapshots
+      Set<String> expectedDiffFiles = Sets.newHashSet("unique_to.sst");
+      // Mock the utility method to return the expected diff files
+      mockedUtils.when(
+          () -> 
OmSnapshotUtils.getSSTDiffListWithFullPath(eq(Arrays.asList("unique_to.sst")), 
anyString(), anyString(),
+              eq(diffDir))).thenReturn(expectedDiffFiles);
+      // Execute the method under test
+      Set<String> result = spy.getDeltaFiles(
+          fromSnapshot,
+          toSnapshot,
+          Arrays.asList("cf1", "cf2"),
+          fromSnapshotInfo,
+          toSnapshotInfo,
+          false,  // useFullDiff = false to test optimized path
+          Collections.emptyMap(),
+          diffDir,
+          diffJobKey);
+      // Verify results - should contain exactly the 2 different files

Review Comment:
   This assertion block says "should contain exactly the 2 different files", 
but the test data/expectations are for a single diff file (`unique_to.sst`) and 
`result.size()` is asserted to be 1. Please update the comment to match the 
actual expectation to avoid confusion when maintaining the test.
   ```suggestion
         // Verify results - should contain exactly the expected single 
different file
   ```



##########
hadoop-ozone/ozone-manager/src/main/java/org/apache/hadoop/ozone/om/snapshot/OmSnapshotUtils.java:
##########
@@ -214,4 +215,69 @@ public static void linkFiles(File oldDir, File newDir) 
throws IOException {
       }
     }
   }
+
+  /**
+   * Locates the full path of the specified SST file by searching the backup 
directory
+   * first, followed by the provided database directories.
+   *
+   * @param sstFilename   The name of the SST file to locate.
+   * @param sstBackupDir  The directory where SST backups are stored.
+   * @param dbPaths       One or more database directories to search for the 
SST file.
+   * @return The full path to the SST file as a string.
+   * @throws RuntimeException if the SST file cannot be found in any of the 
specified directories.
+   */
+  private static String getSSTFullPath(String sstFilename, String sstBackupDir,
+      String... dbPaths) {
+
+    // Try to locate the SST in the backup dir first
+    final Path sstPathInBackupDir = Paths.get(sstBackupDir,
+        sstFilename);
+    if (Files.exists(sstPathInBackupDir)) {
+      return sstPathInBackupDir.toString();
+    }
+
+    // SST file does not exist in the SST backup dir, this means the SST file
+    // has not gone through any compactions yet and is only available in the
+    // src DB directory or destDB directory
+    for (String dbPath : dbPaths) {
+      final Path sstPathInDBDir = Paths.get(dbPath,
+          sstFilename);
+      if (Files.exists(sstPathInDBDir)) {
+        return sstPathInDBDir.toString();
+      }
+    }
+
+    // TODO: More graceful error handling?
+    throw new RuntimeException("Unable to locate SST file: " +
+        sstFilename);
+  }

Review Comment:
   `getSSTFullPath` throws a `RuntimeException` with only the filename when it 
can't locate an SST. Since this is used in snapshot diff execution paths, the 
resulting failure is hard to diagnose. Consider throwing an `IOException` (or 
at least including all searched directories in the message) so callers can log 
actionable context and/or fall back predictably.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to