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

jiangtian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new d9c3fc9f908 fix: record directory allocation for hard-linked files 
(#17077)
d9c3fc9f908 is described below

commit d9c3fc9f90872b3df975fdbb08ab31fcd56b96a7
Author: Hongzhi Gao <[email protected]>
AuthorDate: Mon Jan 26 14:08:36 2026 +0800

    fix: record directory allocation for hard-linked files (#17077)
    
    * fix: record directory allocation for hard-linked files
    
    Fix missing fileTarget.put() when hard link creation succeeds, ensuring 
consistent directory assignment for files with same prefix.
    
    * fix: record directory allocation for hard-linked files
    
    Fix missing fileTarget.put() when hard link creation succeeds, ensuring 
consistent directory assignment for files with same prefix.
    
    * fix: record directory allocation for hard-linked files
    
    Fix missing fileTarget.put() when hard link creation succeeds, ensuring 
consistent directory assignment for files with same prefix.
---
 .../dataregion/snapshot/SnapshotLoader.java        |  1 +
 .../dataregion/snapshot/IoTDBSnapshotTest.java     | 78 ++++++++++++++++++++++
 2 files changed, 79 insertions(+)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
index 9ac4956724c..59fa7967241 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
@@ -345,6 +345,7 @@ public class SnapshotLoader {
                 try {
                   Files.createLink(targetFile.toPath(), file.toPath());
                   LOGGER.debug("Created hard link from {} to {}", file, 
targetFile);
+                  fileTarget.put(fileKey, effectiveDir);
                   return targetFile;
                 } catch (IOException e) {
                   LOGGER.info(
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
index 9314fc2fa0a..7976c14e87a 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
@@ -28,7 +28,9 @@ import 
org.apache.iotdb.db.storageengine.dataregion.DataRegion;
 import org.apache.iotdb.db.storageengine.dataregion.flush.CompressionRatio;
 import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResourceStatus;
+import org.apache.iotdb.db.storageengine.rescon.disk.FolderManager;
 import org.apache.iotdb.db.storageengine.rescon.disk.TierManager;
+import 
org.apache.iotdb.db.storageengine.rescon.disk.strategy.DirectoryStrategyType;
 import org.apache.iotdb.db.utils.EnvironmentUtils;
 
 import org.apache.tsfile.exception.write.WriteProcessException;
@@ -43,7 +45,9 @@ import org.mockito.Mockito;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.apache.tsfile.common.constant.TsFileConstant.PATH_SEPARATOR;
@@ -252,4 +256,78 @@ public class IoTDBSnapshotTest {
 
     Assert.assertTrue(snapshotFile.getParentFile().exists());
   }
+
+  /**
+   * Ensure snapshot-related files with the same fileKey are placed into the 
same data directory
+   * even when hard link succeeds and the method returns early.
+   */
+  @Test
+  public void testFileTargetRecordedWhenHardLinkSuccess() throws Exception {
+    // snapshot source dir
+    File snapshotDir = new File("target/test/snapshot-hardlink");
+    if (snapshotDir.exists()) {
+      FileUtils.recursivelyDeleteFolder(snapshotDir.getAbsolutePath());
+    }
+    Assert.assertTrue(snapshotDir.mkdirs());
+
+    // same fileKey
+    File tsFile = new File(snapshotDir, "1-1-0-0.tsfile");
+    File resFile = new File(snapshotDir, "1-1-0-0.resource");
+    File modsFile = new File(snapshotDir, "1-1-0-0.mods");
+
+    Assert.assertTrue(tsFile.createNewFile());
+    Assert.assertTrue(resFile.createNewFile());
+    Assert.assertTrue(modsFile.createNewFile());
+
+    File[] files = new File[] {tsFile, resFile, modsFile};
+
+    // data dirs
+    String[] dataDirs =
+        new String[] {"target/test/data1", "target/test/data2", 
"target/test/data3"};
+
+    for (String dir : dataDirs) {
+      File base = new File(dir);
+      if (base.exists()) {
+        FileUtils.recursivelyDeleteFolder(base.getAbsolutePath());
+      }
+      Assert.assertTrue(base.mkdirs());
+    }
+
+    FolderManager folderManager =
+        new FolderManager(Arrays.asList(dataDirs), 
DirectoryStrategyType.SEQUENCE_STRATEGY);
+
+    String targetSuffix = "sequence/root.testsg/0/0";
+
+    Method method =
+        SnapshotLoader.class.getDeclaredMethod(
+            "createLinksFromSnapshotToSourceDir", String.class, File[].class, 
FolderManager.class);
+    method.setAccessible(true);
+
+    SnapshotLoader loader = new SnapshotLoader("dummy", "root.testsg", "0");
+
+    method.invoke(loader, targetSuffix, files, folderManager);
+
+    // verify: only ONE dir contains all three files
+    int hitDirCount = 0;
+
+    for (String dir : dataDirs) {
+      File targetDir = new File(dir + "/" + targetSuffix);
+      if (!targetDir.exists()) {
+        continue;
+      }
+
+      boolean ts = new File(targetDir, tsFile.getName()).exists();
+      boolean res = new File(targetDir, resFile.getName()).exists();
+      boolean mods = new File(targetDir, modsFile.getName()).exists();
+
+      if (ts || res || mods) {
+        hitDirCount++;
+        Assert.assertTrue(ts);
+        Assert.assertTrue(res);
+        Assert.assertTrue(mods);
+      }
+    }
+
+    Assert.assertEquals(1, hitDirCount);
+  }
 }

Reply via email to