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

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

commit 9a01de85d2e73cadb0d6ae999dd353a50e75a76a
Author: Jinrui.Zhang <[email protected]>
AuthorDate: Tue Oct 24 19:50:43 2023 +0800

    add hard link method for TsFileResource to leverage hard link in an unified 
approach
---
 .../common/tsfile/PipeTsFileInsertionEvent.java    |  2 +-
 .../resource/tsfile/PipeTsFileResourceManager.java | 15 ++--
 .../dataregion/snapshot/SnapshotTaker.java         | 14 +++-
 .../dataregion/tsfile/TsFileResource.java          | 96 ++++++++++++----------
 .../dataregion/tsfile/TsFileResourceStatus.java    |  5 ++
 .../resource/PipeTsFileResourceManagerTest.java    | 22 +++--
 6 files changed, 94 insertions(+), 60 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeTsFileInsertionEvent.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeTsFileInsertionEvent.java
index 8914e56cbfa..78b519e0498 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeTsFileInsertionEvent.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/PipeTsFileInsertionEvent.java
@@ -126,7 +126,7 @@ public class PipeTsFileInsertionEvent extends EnrichedEvent 
implements TsFileIns
   @Override
   public boolean internallyIncreaseResourceReferenceCount(String 
holderMessage) {
     try {
-      tsFile = PipeResourceManager.tsfile().increaseFileReference(tsFile, 
true);
+      tsFile = PipeResourceManager.tsfile().increaseFileReference(tsFile, 
true, resource);
       return true;
     } catch (Exception e) {
       LOGGER.warn(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/tsfile/PipeTsFileResourceManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/tsfile/PipeTsFileResourceManager.java
index 9839d793a25..28094e2212b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/tsfile/PipeTsFileResourceManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/tsfile/PipeTsFileResourceManager.java
@@ -54,10 +54,12 @@ public class PipeTsFileResourceManager {
    * @param file tsfile, resource file or mod file. can be original file or 
hardlink/copy of
    *     original file
    * @param isTsFile true to create hardlink, false to copy file
+   * @param resource original TsFileResource or null if isTsFile is false
    * @return the hardlink or copied file
    * @throws IOException when create hardlink or copy file failed
    */
-  public synchronized File increaseFileReference(File file, boolean isTsFile) 
throws IOException {
+  public synchronized File increaseFileReference(
+      File file, boolean isTsFile, TsFileResource resource) throws IOException 
{
     // if the file is already a hardlink or copied file, just increase 
reference count and return it
     if (increaseReferenceIfExists(file.getPath())) {
       return file;
@@ -77,9 +79,12 @@ public class PipeTsFileResourceManager {
     fileNameToFileMap.put(hardlinkOrCopiedFile.getPath(), 
hardlinkOrCopiedFile);
     // if the file is a tsfile, create a hardlink in pipe dir and return it.
     // otherwise, copy the file (.mod or .resource) to pipe dir and return it.
-    return isTsFile
-        ? createHardLink(file, hardlinkOrCopiedFile)
-        : copyFile(file, hardlinkOrCopiedFile);
+    if (isTsFile) {
+      resource.hardLinkOrCopyTsFileTo(hardlinkOrCopiedFile.toPath());
+    } else {
+      copyFile(file, hardlinkOrCopiedFile);
+    }
+    return hardlinkOrCopiedFile;
   }
 
   private boolean increaseReferenceIfExists(String path) {
@@ -181,7 +186,7 @@ public class PipeTsFileResourceManager {
   }
 
   public synchronized void pinTsFileResource(TsFileResource resource) throws 
IOException {
-    increaseFileReference(resource.getTsFile(), true);
+    increaseFileReference(resource.getTsFile(), true, resource);
   }
 
   public synchronized void unpinTsFileResource(TsFileResource resource) throws 
IOException {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotTaker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotTaker.java
index b404ca2fd5e..39a44eddfd5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotTaker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotTaker.java
@@ -172,7 +172,7 @@ public class SnapshotTaker {
         }
         File snapshotTsFile = getSnapshotFilePathForTsFile(tsFile, snapshotId);
         // create hard link for tsfile, resource, mods
-        createHardLink(snapshotTsFile, tsFile);
+        createHardLinkForTsFile(snapshotTsFile, resource);
         createHardLink(
             new File(snapshotTsFile.getAbsolutePath() + 
TsFileResource.RESOURCE_SUFFIX),
             new File(tsFile.getAbsolutePath() + 
TsFileResource.RESOURCE_SUFFIX));
@@ -201,6 +201,18 @@ public class SnapshotTaker {
     snapshotLogger.logFile(source);
   }
 
+  private void createHardLinkForTsFile(File target, TsFileResource source) 
throws IOException {
+    if (!target.getParentFile().exists()) {
+      LOGGER.error("Hard link target dir {} doesn't exist", 
target.getParentFile());
+    }
+    if (!source.getTsFile().exists()) {
+      LOGGER.error("Hard link source file {} doesn't exist", source);
+    }
+    Files.deleteIfExists(target.toPath());
+    source.hardLinkOrCopyTsFileTo(target.toPath());
+    snapshotLogger.logFile(source.getTsFile());
+  }
+
   /**
    * Construct the snapshot file path for a given tsfile, and will create the 
dir. Eg, given a
    * tsfile in /data/iotdb/data/sequence/root.testsg/1/0/1-1-0-0.tsfile, with 
snapshotId "sm123",
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java
index ce49b700768..c6103673337 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResource.java
@@ -38,7 +38,6 @@ import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.ITimeIndex;
 import 
org.apache.iotdb.db.storageengine.dataregion.tsfile.timeindex.TimeIndexLevel;
 import org.apache.iotdb.db.storageengine.rescon.disk.TierManager;
 import org.apache.iotdb.db.utils.DateTimeUtils;
-import org.apache.iotdb.tsfile.common.constant.TsFileConstant;
 import org.apache.iotdb.tsfile.file.metadata.IChunkMetadata;
 import org.apache.iotdb.tsfile.file.metadata.ITimeSeriesMetadata;
 import org.apache.iotdb.tsfile.fileSystem.FSFactoryProducer;
@@ -57,9 +56,8 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -155,6 +153,11 @@ public class TsFileResource {
 
   private ProgressIndex maxProgressIndex;
 
+  private long dataSize;
+
+  private boolean isMetaSplit = false;
+  private boolean hasHardLink = false;
+
   public TsFileResource() {}
 
   public TsFileResource(TsFileResource other) throws IOException {
@@ -659,6 +662,10 @@ public class TsFileResource {
       case COMPACTING:
         return compareAndSetStatus(
             TsFileResourceStatus.COMPACTION_CANDIDATE, 
TsFileResourceStatus.COMPACTING);
+      case HARD_LINKING:
+        return compareAndSetStatus(TsFileResourceStatus.NORMAL, 
TsFileResourceStatus.HARD_LINKING)
+            || compareAndSetStatus(
+                TsFileResourceStatus.COMPACTION_CANDIDATE, 
TsFileResourceStatus.HARD_LINKING);
       case COMPACTION_CANDIDATE:
         return compareAndSetStatus(
             TsFileResourceStatus.NORMAL, 
TsFileResourceStatus.COMPACTION_CANDIDATE);
@@ -824,48 +831,6 @@ public class TsFileResource {
     return timeIndex.isSpanMultiTimePartitions();
   }
 
-  /**
-   * Create a hardlink for the TsFile and modification file (if exists) The 
hardlink will have a
-   * suffix like ".{sysTime}_{randomLong}"
-   *
-   * @return a new TsFileResource with its file changed to the hardlink or 
null the hardlink cannot
-   *     be created.
-   */
-  public TsFileResource createHardlink() {
-    if (!file.exists()) {
-      return null;
-    }
-
-    TsFileResource newResource;
-    try {
-      newResource = new TsFileResource(this);
-    } catch (IOException e) {
-      LOGGER.error("Cannot create hardlink for {}", file, e);
-      return null;
-    }
-
-    while (true) {
-      String hardlinkSuffix =
-          TsFileConstant.PATH_SEPARATOR + System.currentTimeMillis() + "_" + 
random.nextLong();
-      File hardlink = new File(file.getAbsolutePath() + hardlinkSuffix);
-
-      try {
-        Files.createLink(Paths.get(hardlink.getAbsolutePath()), 
Paths.get(file.getAbsolutePath()));
-        newResource.setFile(hardlink);
-        if (modFile != null && modFile.exists()) {
-          newResource.setModFile(modFile.createHardlink());
-        }
-        break;
-      } catch (FileAlreadyExistsException e) {
-        // retry a different name if the file is already created
-      } catch (IOException e) {
-        LOGGER.error("Cannot create hardlink for {}", file, e);
-        return null;
-      }
-    }
-    return newResource;
-  }
-
   public void setModFile(ModificationFile modFile) {
     synchronized (this) {
       this.modFile = modFile;
@@ -1154,4 +1119,45 @@ public class TsFileResource {
   public String getDataRegionId() {
     return file.getParentFile().getParentFile().getName();
   }
+
+  public void hardLinkOrCopyTsFileTo(Path targetPath) throws IOException {
+    if (setStatus(TsFileResourceStatus.HARD_LINKING)) {
+      // use hard link directly
+      Files.createLink(targetPath, getTsFile().toPath());
+      this.hasHardLink = true;
+    } else {
+      // copy this file to target path
+      readLock();
+      try {
+        if (isMetaSplit) {
+          // combine the data part and meta part into target file
+
+        } else {
+          Files.copy(getTsFile().toPath(), targetPath);
+        }
+      } finally {
+        readUnlock();
+      }
+    }
+  }
+
+  public void setDataSize(long dataSize) {
+    this.dataSize = dataSize;
+  }
+
+  public long getDataSize() {
+    return dataSize;
+  }
+
+  public boolean isMetaSplit() {
+    return isMetaSplit;
+  }
+
+  public void setMetaSplit(boolean metaSplit) {
+    isMetaSplit = metaSplit;
+  }
+
+  public boolean isHasHardLink() {
+    return hasHardLink;
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResourceStatus.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResourceStatus.java
index 2cf69fac58d..8014da39618 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResourceStatus.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/tsfile/TsFileResourceStatus.java
@@ -24,5 +24,10 @@ public enum TsFileResourceStatus {
   NORMAL,
   COMPACTION_CANDIDATE,
   COMPACTING,
+  /**
+   * Indicating the TsFile is prepared to be hard linked. It is seemed as a 
write operation because
+   * it cannot be modified by other module during hard linking
+   */
+  HARD_LINKING,
   DELETED
 }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/PipeTsFileResourceManagerTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/PipeTsFileResourceManagerTest.java
index ef9496901f3..6ad7858f3cc 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/PipeTsFileResourceManagerTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/PipeTsFileResourceManagerTest.java
@@ -27,6 +27,7 @@ import 
org.apache.iotdb.db.pipe.resource.tsfile.PipeTsFileResourceManager;
 import org.apache.iotdb.db.storageengine.dataregion.modification.Deletion;
 import org.apache.iotdb.db.storageengine.dataregion.modification.Modification;
 import 
org.apache.iotdb.db.storageengine.dataregion.modification.ModificationFile;
+import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
 import org.apache.iotdb.tsfile.write.TsFileWriter;
@@ -150,12 +151,14 @@ public class PipeTsFileResourceManagerTest {
   @Test
   public void testIncreaseTsfile() throws IOException {
     File originTsfile = new File(TS_FILE_NAME);
+    TsFileResource originTsFileResource = new TsFileResource(originTsfile);
     File originModFile = new File(MODS_FILE_NAME);
     Assert.assertEquals(0, 
pipeTsFileResourceManager.getFileReferenceCount(originTsfile));
     Assert.assertEquals(0, 
pipeTsFileResourceManager.getFileReferenceCount(originModFile));
 
-    File pipeTsfile = 
pipeTsFileResourceManager.increaseFileReference(originTsfile, true);
-    File pipeModFile = 
pipeTsFileResourceManager.increaseFileReference(originModFile, false);
+    File pipeTsfile =
+        pipeTsFileResourceManager.increaseFileReference(originTsfile, true, 
originTsFileResource);
+    File pipeModFile = 
pipeTsFileResourceManager.increaseFileReference(originModFile, false, null);
     Assert.assertEquals(1, 
pipeTsFileResourceManager.getFileReferenceCount(pipeTsfile));
     Assert.assertEquals(1, 
pipeTsFileResourceManager.getFileReferenceCount(pipeModFile));
     Assert.assertTrue(Files.exists(originTsfile.toPath()));
@@ -163,19 +166,20 @@ public class PipeTsFileResourceManagerTest {
     Assert.assertTrue(Files.exists(pipeTsfile.toPath()));
     Assert.assertTrue(Files.exists(pipeModFile.toPath()));
 
-    pipeTsFileResourceManager.increaseFileReference(originTsfile, true);
-    pipeTsFileResourceManager.increaseFileReference(originModFile, false);
+    pipeTsFileResourceManager.increaseFileReference(originTsfile, true, 
originTsFileResource);
+    pipeTsFileResourceManager.increaseFileReference(originModFile, false, 
null);
     Assert.assertEquals(2, 
pipeTsFileResourceManager.getFileReferenceCount(pipeTsfile));
     Assert.assertEquals(2, 
pipeTsFileResourceManager.getFileReferenceCount(pipeModFile));
 
     // test use hardlinkTsFile to increase reference counts
-    pipeTsFileResourceManager.increaseFileReference(pipeTsfile, true);
+    pipeTsFileResourceManager.increaseFileReference(
+        pipeTsfile, true, new TsFileResource(pipeTsfile));
     Assert.assertEquals(3, 
pipeTsFileResourceManager.getFileReferenceCount(pipeTsfile));
     Assert.assertTrue(Files.exists(originTsfile.toPath()));
     Assert.assertTrue(Files.exists(pipeTsfile.toPath()));
 
     // test use copyFile to increase reference counts
-    pipeTsFileResourceManager.increaseFileReference(pipeModFile, false);
+    pipeTsFileResourceManager.increaseFileReference(pipeModFile, false, null);
     Assert.assertEquals(3, 
pipeTsFileResourceManager.getFileReferenceCount(pipeModFile));
     Assert.assertTrue(Files.exists(originModFile.toPath()));
     Assert.assertTrue(Files.exists(pipeModFile.toPath()));
@@ -191,8 +195,10 @@ public class PipeTsFileResourceManagerTest {
     Assert.assertEquals(0, 
pipeTsFileResourceManager.getFileReferenceCount(originFile));
     Assert.assertEquals(0, 
pipeTsFileResourceManager.getFileReferenceCount(originModFile));
 
-    File pipeTsfile = 
pipeTsFileResourceManager.increaseFileReference(originFile, true);
-    File pipeModFile = 
pipeTsFileResourceManager.increaseFileReference(originModFile, false);
+    File pipeTsfile =
+        pipeTsFileResourceManager.increaseFileReference(
+            originFile, true, new TsFileResource(originFile));
+    File pipeModFile = 
pipeTsFileResourceManager.increaseFileReference(originModFile, false, null);
     Assert.assertEquals(1, 
pipeTsFileResourceManager.getFileReferenceCount(pipeTsfile));
     Assert.assertEquals(1, 
pipeTsFileResourceManager.getFileReferenceCount(pipeModFile));
     Assert.assertTrue(Files.exists(pipeTsfile.toPath()));

Reply via email to