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 47d93f181cc HDDS-15209. Stabilize 
TestRocksDBCheckpointDiffer#testDifferWithDB for RocksDB layout variance 
(#11324)
47d93f181cc is described below

commit 47d93f181cc1745c39d5df1b17c4a7ebb7f65d2c
Author: Arun Sarin <[email protected]>
AuthorDate: Mon Sep 28 22:29:53 2026 +0530

    HDDS-15209. Stabilize TestRocksDBCheckpointDiffer#testDifferWithDB for 
RocksDB layout variance (#11324)
---
 .../rocksdiff/TestRocksDBCheckpointDiffer.java     | 107 +++++++++++++--------
 1 file changed, 65 insertions(+), 42 deletions(-)

diff --git 
a/hadoop-hdds/rocksdb-checkpoint-differ/src/test/java/org/apache/ozone/rocksdiff/TestRocksDBCheckpointDiffer.java
 
b/hadoop-hdds/rocksdb-checkpoint-differ/src/test/java/org/apache/ozone/rocksdiff/TestRocksDBCheckpointDiffer.java
index fff123043e9..506fa9ea6aa 100644
--- 
a/hadoop-hdds/rocksdb-checkpoint-differ/src/test/java/org/apache/ozone/rocksdiff/TestRocksDBCheckpointDiffer.java
+++ 
b/hadoop-hdds/rocksdb-checkpoint-differ/src/test/java/org/apache/ozone/rocksdiff/TestRocksDBCheckpointDiffer.java
@@ -20,6 +20,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Arrays.asList;
 import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.apache.commons.io.FilenameUtils.getBaseName;
 import static org.apache.hadoop.hdds.StringUtils.bytes2String;
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_OM_SNAPSHOT_COMPACTION_DAG_MAX_TIME_ALLOWED;
 import static 
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_OM_SNAPSHOT_COMPACTION_DAG_MAX_TIME_ALLOWED_DEFAULT;
@@ -115,7 +116,6 @@
 import 
org.apache.ozone.rocksdiff.RocksDBCheckpointDiffer.DifferSnapshotVersion;
 import org.apache.ozone.rocksdiff.RocksDBCheckpointDiffer.NodeComparator;
 import org.apache.ozone.test.GenericTestUtils;
-import org.apache.ozone.test.tag.Flaky;
 import org.apache.ratis.util.UncheckedAutoCloseable;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
@@ -956,12 +956,13 @@ public void testGetSSTDiffListWithoutDB(String 
description,
    * Does actual DB write, flush, compaction.
    */
   @Test
-  @Flaky("HDDS-15209")
   void testDifferWithDB() throws Exception {
     writeKeysAndCheckpointing();
     readRocksDBInstance(ACTIVE_DB_DIR_NAME, activeRocksDB, null,
         rocksDBCheckpointDiffer);
 
+    GenericTestUtils.waitFor(() -> 
rocksDBCheckpointDiffer.getInflightCompactions().isEmpty(), 1000, 10000);
+
     if (LOG.isDebugEnabled()) {
       printAllSnapshots();
     }
@@ -971,24 +972,12 @@ void testDifferWithDB() throws Exception {
         rocksDBCheckpointDiffer.getForwardCompactionDAG());
 
     diffAllSnapshots(rocksDBCheckpointDiffer);
+    assertCompactionSstBackups(rocksDBCheckpointDiffer);
 
-    // Confirm correct links created
-    try (Stream<Path> sstPathStream = Files.list(sstBackUpDir.toPath())) {
-      List<String> actualLinks = sstPathStream.map(Path::getFileName)
-              .map(Object::toString).sorted().collect(Collectors.toList());
-      assertThat(actualLinks).hasSize(7);
-      assertThat(actualLinks).allMatch(link -> link.matches("\\d{6}\\.sst"));
-      for (String linkName : actualLinks) {
-        assertTrue(Files.size(sstBackUpDir.toPath().resolve(linkName)) > 0,
-            "SST link should not be empty: " + linkName);
-      }
-    }
     
rocksDBCheckpointDiffer.getForwardCompactionDAG().nodes().stream().forEach(compactionNode
 -> {
       Assertions.assertNotNull(compactionNode.getStartKey());
       Assertions.assertNotNull(compactionNode.getEndKey());
     });
-    GenericTestUtils.waitFor(() -> 
rocksDBCheckpointDiffer.getInflightCompactions().isEmpty(), 1000,
-        10000);
     if (LOG.isDebugEnabled()) {
       rocksDBCheckpointDiffer.dumpCompactionNodeTable();
     }
@@ -1006,35 +995,28 @@ private static List<ColumnFamilyDescriptor> 
getColumnFamilyDescriptors() {
   void diffAllSnapshots(RocksDBCheckpointDiffer differ)
       throws IOException {
     final DifferSnapshotInfo src = snapshots.get(snapshots.size() - 1);
+    Set<String> allTables = allTablesForDiff();
     boolean sawNonEmptyDiff = false;
+
+    // Each test snapshot only stores SST metadata for checkpoint version 0 
(see createCheckpoint).
     for (DifferSnapshotInfo snap : snapshots) {
-      // Returns a list of SST files to be fed into RocksCheckpointDiffer Dag.
-      List<String> tablesToTrack = new 
ArrayList<>(COLUMN_FAMILIES_TO_TRACK_IN_DAG);
-      // Add some invalid index.
-      tablesToTrack.add("compactionLogTable");
-
-      // Baseline diff when tracking every table. A subset's diff must equal
-      // this baseline filtered to the subset's column families (files with no
-      // column family are always kept). This relationship is deterministic and
-      // stable across RocksDB versions, unlike hard-coded SST file names.
-      Set<String> allTables = new HashSet<>(tablesToTrack);
-      List<SstFileInfo> baseline = differ.getSSTDiffList(
+      List<SstFileInfo> fullDagDiff = differ.getSSTDiffList(
           new DifferSnapshotVersion(src, 0, allTables),
           new DifferSnapshotVersion(snap, 0, allTables),
-          null, allTables, true).orElse(Collections.emptyList());
-      sawNonEmptyDiff = sawNonEmptyDiff || !baseline.isEmpty();
-
-      // Independent structural oracle, not derived from getSSTDiffList's own
-      // output: a snapshot diffed against itself must have no differing SST
-      // files. Together with the sawNonEmptyDiff guard below, this bounds a
-      // systematically broken diff in both directions (returning nothing, or
-      // returning files even for identical snapshots).
+          null, allTables, true).orElseThrow();
+      sawNonEmptyDiff = sawNonEmptyDiff || !fullDagDiff.isEmpty();
+
+      // Independent structural oracle: a snapshot diffed against itself must 
have no
+      // differing SST files. Together with sawNonEmptyDiff below, this bounds 
a broken
+      // diff in both directions (returning nothing, or returning files for 
identical snapshots).
       if (snap == src) {
-        assertThat(baseline)
+        assertThat(fullDagDiff)
             .as("diff of a snapshot against itself must be empty")
             .isEmpty();
       }
 
+      List<String> tablesToTrack = new ArrayList<>(allTables);
+
       Set<String> tableToLookUp = new HashSet<>();
       for (int i = 0; i < Math.pow(2, tablesToTrack.size()); i++) {
         tableToLookUp.clear();
@@ -1046,15 +1028,12 @@ void diffAllSnapshots(RocksDBCheckpointDiffer differ)
         }
         DifferSnapshotVersion srcSnapVersion = new DifferSnapshotVersion(src, 
0, tableToLookUp);
         DifferSnapshotVersion destSnapVersion = new 
DifferSnapshotVersion(snap, 0, tableToLookUp);
-        List<SstFileInfo> sstDiffList = differ.getSSTDiffList(srcSnapVersion, 
destSnapVersion, null,
-                tableToLookUp, true).orElse(Collections.emptyList());
+        List<SstFileInfo> sstDiffList = differ.getSSTDiffList(
+            srcSnapVersion, destSnapVersion, null, tableToLookUp, 
true).orElseThrow();
         LOG.info("SST diff list from '{}' to '{}': {} tables: {}",
             src.getDbPath(0), snap.getDbPath(0), sstDiffList, tableToLookUp);
 
-        // Expected files: baseline entries whose column family is untracked
-        // (null) or included in this subset. getSSTDiffList returns the values
-        // of a HashMap, so its ordering is not guaranteed; compare as sets.
-        List<String> expectedFiles = baseline.stream()
+        List<String> expectedFiles = fullDagDiff.stream()
             .filter(sstFileInfo -> sstFileInfo.getColumnFamily() == null
                 || tableToLookUp.contains(sstFileInfo.getColumnFamily()))
             .map(SstFileInfo::getFileName)
@@ -1065,12 +1044,55 @@ void diffAllSnapshots(RocksDBCheckpointDiffer differ)
         
assertThat(actualFiles).containsExactlyInAnyOrderElementsOf(expectedFiles);
       }
     }
-    // Guard against getSSTDiffList silently returning nothing for every input.
     assertThat(sawNonEmptyDiff)
         .as("expected at least one non-empty SST diff across snapshots")
         .isTrue();
   }
 
+  private Set<String> allTablesForDiff() {
+    Set<String> tables = new HashSet<>(COLUMN_FAMILIES_TO_TRACK_IN_DAG);
+    tables.add("compactionLogTable");
+    return tables;
+  }
+
+  private void assertCompactionSstBackups(RocksDBCheckpointDiffer differ) 
throws IOException {
+    Set<String> tablesToLookup = allTablesForDiff();
+    DifferSnapshotInfo firstSnapshot = snapshots.get(0);
+    DifferSnapshotInfo lastSnapshot = snapshots.get(snapshots.size() - 1);
+    List<SstFileInfo> diffSinceFirst = differ.getSSTDiffList(
+        new DifferSnapshotVersion(lastSnapshot, 0, tablesToLookup),
+        new DifferSnapshotVersion(firstSnapshot, 0, tablesToLookup),
+        null, tablesToLookup, true).orElseThrow();
+    // Version 0 is the only RocksDB checkpoint layer we record for each 
snapshot in this test.
+    Set<String> lastSnapshotFileNames = lastSnapshot.getSstFiles(0, 
tablesToLookup).stream()
+        .map(SstFileInfo::getFileName)
+        .collect(Collectors.toSet());
+    Set<String> diffNotInLastSnapshot = diffSinceFirst.stream()
+        .map(SstFileInfo::getFileName)
+        .filter(name -> !lastSnapshotFileNames.contains(name))
+        .collect(Collectors.toSet());
+    ConcurrentMap<String, CompactionNode> compactionNodes = 
differ.getCompactionNodeMap();
+    Set<String> backupBaseNames;
+    try (Stream<Path> sstPathStream = Files.list(sstBackUpDir.toPath())) {
+      List<Path> backupPaths = sstPathStream.collect(Collectors.toList());
+      backupBaseNames = backupPaths.stream()
+          .map(path -> getBaseName(path.getFileName().toString()))
+          .collect(Collectors.toSet());
+      assertThat(backupBaseNames).hasSizeGreaterThanOrEqualTo(7);
+      assertThat(backupPaths).allMatch(path -> 
path.getFileName().toString().matches("\\d+\\.sst"));
+      for (Path path : backupPaths) {
+        assertTrue(Files.size(path) > 0, "SST link should not be empty: " + 
path);
+      }
+    }
+    assertThat(backupBaseNames)
+        .as("SSTs compacted away since the first snapshot should be backed up")
+        .containsAll(diffNotInLastSnapshot);
+    for (String backupName : backupBaseNames) {
+      assertTrue(compactionNodes.containsKey(backupName),
+          () -> "Backup " + backupName + " should match a tracked compaction 
SST");
+    }
+  }
+
   /**
    * Helper function that creates an RDB checkpoint (= Ozone snapshot).
    */
@@ -1094,6 +1116,7 @@ private void createCheckpoint(ManagedRocksDB rocksDB) 
throws RocksDBException {
     List<ColumnFamilyHandle> colHandle = new ArrayList<>();
     try (ManagedRocksDB rdb = ManagedRocksDB.openReadOnly(cpPath, 
getColumnFamilyDescriptors(), colHandle)) {
       TreeMap<Integer, List<SstFileInfo>> versionSstFilesMap = new TreeMap<>();
+      // OM snapshots can track multiple linked DB checkpoints; this test 
builds just one (version 0).
       versionSstFilesMap.put(0, 
rdb.getLiveMetadataForSSTFiles().values().stream().map(SstFileInfo::new)
           .collect(Collectors.toList()));
       final DifferSnapshotInfo currentSnapshot = new 
DifferSnapshotInfo((version) -> Paths.get(cpPath),


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

Reply via email to