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);
+ }
}