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

jt2594838 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 35cf94a780a Improve pipe runtime memory allocation (#18051)
35cf94a780a is described below

commit 35cf94a780ab6fe7f105cf1a021e2143f8b503bc
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jul 7 10:30:27 2026 +0800

    Improve pipe runtime memory allocation (#18051)
    
    * Improve pipe runtime memory allocation
    
    * Fix pipe TsFile parser memory self-lock
    
    * Fix Chinese pipe TsFile parser i18n message
    
    * Fix TsFile parser OOM retry test setup
    
    * Address pipe dynamic memory review comments
    
    * Fix scan parser tablet memory release
    
    * Deduplicate pipe meta heartbeat reporting
    
    * Reduce pipe sync file transfer duplication
    
    * Fix pipe i18n review comments
---
 .../apache/iotdb/db/i18n/DataNodePipeMessages.java |  10 +-
 .../apache/iotdb/db/i18n/DataNodePipeMessages.java |   9 +-
 .../db/pipe/agent/task/PipeDataNodeTaskAgent.java  | 455 ++++-----------------
 .../subtask/processor/PipeProcessorSubtask.java    |   2 +
 .../common/tsfile/PipeTsFileInsertionEvent.java    |  51 ++-
 .../tsfile/parser/TsFileInsertionEventParser.java  |  12 +-
 .../query/TsFileInsertionEventQueryParser.java     |   1 +
 .../scan/TsFileInsertionEventScanParser.java       |  14 +-
 .../table/TsFileInsertionEventTableParser.java     |  22 +-
 .../processor/aggregate/AggregateProcessor.java    |   3 +
 .../downsampling/DownSamplingProcessor.java        |   3 +
 .../db/pipe/resource/memory/PipeMemoryManager.java |  17 +-
 .../evolvable/batch/PipeTabletEventBatch.java      |  69 +++-
 .../evolvable/batch/PipeTabletEventPlainBatch.java |  51 ++-
 .../batch/PipeTabletEventTsFileBatch.java          |  30 +-
 .../protocol/airgap/IoTDBDataRegionAirGapSink.java |  18 +-
 .../iotconsensusv2/IoTConsensusV2SyncSink.java     |  20 +-
 .../IoTConsensusV2TsFileInsertionEventHandler.java |  34 +-
 .../IoTConsensusV2TransferBatchReqBuilder.java     | 109 +++--
 .../sink/protocol/legacy/IoTDBLegacyPipeSink.java  |  17 +-
 .../async/handler/PipeTransferTsFileHandler.java   |  42 +-
 .../thrift/sync/IoTDBDataRegionSyncSink.java       | 136 ++++++
 .../pipe/agent/task/PipeDataNodeTaskAgentTest.java |  88 ----
 .../pipe/event/TsFileInsertionEventParserTest.java |  98 +++++
 24 files changed, 667 insertions(+), 644 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index e053b83d99d..dad8dcbc7f4 100644
--- 
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -293,8 +293,8 @@ public final class DataNodePipeMessages {
           + "PipeInsertNodeTabletInsertionEvent({}) overlaps with the time 
range: [{}, {}]. "
           + "Returning true to ensure data integrity.";
   public static final String FAILED_TO_ALLOCATE_MEMORY_FOR_PARSING_TSFILE =
-      "{}: failed to allocate memory for parsing TsFile {}, tablet event no. 
{}, retry count "
-          + "is {}, will keep retrying.";
+      "{}: failed to allocate memory for parsing TsFile {}, tablet event no. 
{}, "
+          + "will release parser memory and retry the TsFile event later.";
   public static final String FAILED_TO_BUILD_TABLET = "Failed to build tablet";
   public static final String FAILED_TO_CHECK_NEXT = "Failed to check next";
   public static final String FAILED_TO_CLOSE_TSFILEREADER = "Failed to close 
TsFileReader";
@@ -2473,5 +2473,11 @@ public final class DataNodePipeMessages {
       "Invalid aligned value chunk index %d, while there are %d time chunks.";
   public static final String 
MESSAGE_FAILED_TO_ROLLBACK_CREATED_REALTIME_PIPE_ARG_STATUS_ARG_CE14334A =
       "Failed to rollback created realtime pipe {}. Status: {}";
+  public static final String 
LOG_REPORTING_PIPE_META_ARG_ISCOMPLETED_ARG_REMAININGEVENTCOUNT_ARG_8F996DF3 =
+      "Reporting pipe meta: %s, isCompleted: %s, remainingEventCount: %s";
+  public static final String LOG_REPORTED_ARG_PIPE_METAS_12068FC6 =
+      "Reported %s pipe metas.";
+  public static final String 
MESSAGE_TRANSFER_FILE_ARG_ERROR_RESULT_STATUS_ARG_E565D9FD =
+      "Transfer file %s error, result status %s.";
 
 }
diff --git 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index e0f3fe3a744..b9b042aff52 100644
--- 
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++ 
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -279,7 +279,8 @@ public final class DataNodePipeMessages {
       "判断 PipeInsertNodeTabletInsertionEvent({}) 的事件时间是否与时间范围 [{}, {}] 重叠时"
           + "发生异常。为保证数据完整性返回 true。";
   public static final String FAILED_TO_ALLOCATE_MEMORY_FOR_PARSING_TSFILE =
-      "{}:为解析 TsFile {} 分配内存失败,tablet event 序号 {},重试次数 {},将持续重试。";
+      "{}:为解析 TsFile {} 分配内存失败,tablet 事件编号 {},"
+          + "将释放解析器内存并稍后重试该 TsFile 事件。";
   public static final String FAILED_TO_BUILD_TABLET = "构建 tablet 失败";
   public static final String FAILED_TO_CHECK_NEXT = "check next 失败";
   public static final String FAILED_TO_CLOSE_TSFILEREADER = "关闭 TsFileReader 
失败";
@@ -2311,5 +2312,11 @@ public final class DataNodePipeMessages {
       "对齐值 chunk 索引 %d 无效,而共有 %d 个 time chunk。";
   public static final String 
MESSAGE_FAILED_TO_ROLLBACK_CREATED_REALTIME_PIPE_ARG_STATUS_ARG_CE14334A =
       "回滚已创建的 realtime pipe {} 失败。状态:{}";
+  public static final String 
LOG_REPORTING_PIPE_META_ARG_ISCOMPLETED_ARG_REMAININGEVENTCOUNT_ARG_8F996DF3 =
+      "正在上报 pipe meta:%s,isCompleted:%s,remainingEventCount:%s";
+  public static final String LOG_REPORTED_ARG_PIPE_METAS_12068FC6 =
+      "已上报 %s 个 pipe meta。";
+  public static final String 
MESSAGE_TRANSFER_FILE_ARG_ERROR_RESULT_STATUS_ARG_E565D9FD =
+      "传输文件 %s 出错,结果状态为 %s。";
 
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
index c069addeeaf..df08311151a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java
@@ -19,7 +19,6 @@
 
 package org.apache.iotdb.db.pipe.agent.task;
 
-import org.apache.iotdb.common.rpc.thrift.TEndPoint;
 import org.apache.iotdb.common.rpc.thrift.TPipeHeartbeatResp;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.concurrent.IoTThreadFactory;
@@ -39,7 +38,6 @@ import org.apache.iotdb.commons.pipe.agent.task.PipeTaskAgent;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
-import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStatus;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInAgent;
@@ -47,10 +45,8 @@ import 
org.apache.iotdb.commons.pipe.agent.task.meta.PipeType;
 import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant;
 import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
-import org.apache.iotdb.commons.pipe.config.constant.SystemConstant;
 import org.apache.iotdb.commons.pipe.resource.log.PipeLogger;
 import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId;
-import org.apache.iotdb.commons.utils.NodeUrlUtils;
 import org.apache.iotdb.consensus.exception.ConsensusException;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
@@ -59,7 +55,6 @@ import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
 import org.apache.iotdb.db.pipe.agent.task.builder.PipeDataNodeBuilder;
 import org.apache.iotdb.db.pipe.agent.task.builder.PipeDataNodeTaskBuilder;
-import org.apache.iotdb.db.pipe.agent.task.subtask.sink.PipeSinkSubtaskManager;
 import org.apache.iotdb.db.pipe.metric.overview.PipeDataNodeSinglePipeMetrics;
 import org.apache.iotdb.db.pipe.metric.overview.PipeTsFileToTabletsMetrics;
 import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
@@ -109,32 +104,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_END_TIME_KEY;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_HISTORY_ENABLE_DEFAULT_VALUE;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_HISTORY_ENABLE_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_HISTORY_END_TIME_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_HISTORY_START_TIME_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATH_EXCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATH_INCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATH_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATTERN_EXCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATTERN_INCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_PATTERN_KEY;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_REALTIME_ENABLE_DEFAULT_VALUE;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_REALTIME_ENABLE_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.EXTRACTOR_START_TIME_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_END_TIME_KEY;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_HISTORY_ENABLE_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_HISTORY_END_TIME_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_HISTORY_START_TIME_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATH_EXCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATH_INCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATH_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATTERN_EXCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATTERN_INCLUSION_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_PATTERN_KEY;
 import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_REALTIME_ENABLE_KEY;
-import static 
org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant.SOURCE_START_TIME_KEY;
 
 public class PipeDataNodeTaskAgent extends PipeTaskAgent {
 
@@ -199,9 +174,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
           || needConstructDataRegionTask
           || needConstructSchemaRegionTask) {
         calculateMemoryUsage(
-            pipeStaticMeta,
-            Collections.singletonList(new Pair<>(consensusGroupId, 
pipeTaskMeta)),
-            false);
+            pipeStaticMeta, Collections.singletonList(new 
Pair<>(consensusGroupId, pipeTaskMeta)));
 
         final PipeDataNodeTask pipeTask =
             new PipeDataNodeTaskBuilder(pipeStaticMeta, consensusGroupId, 
pipeTaskMeta).build();
@@ -416,75 +389,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
                 
PipeConfig.getInstance().getPipeMetaReportMaxLogIntervalRounds(),
                 pipeMetaKeeper.getPipeMetaCount());
 
-    final Set<Integer> dataRegionIds =
-        StorageEngine.getInstance().getAllDataRegionIds().stream()
-            .map(DataRegionId::getId)
-            .collect(Collectors.toSet());
-
-    final List<ByteBuffer> pipeMetaBinaryList = new ArrayList<>();
-    final List<Boolean> pipeCompletedList = new ArrayList<>();
-    final List<Long> pipeRemainingEventCountList = new ArrayList<>();
-    final List<Double> pipeRemainingTimeList = new ArrayList<>();
-    final List<Integer> pipeDegradedStatusList = new ArrayList<>();
-    try {
-      for (final PipeMeta pipeMeta : pipeMetaKeeper.getPipeMetaList()) {
-        pipeMetaBinaryList.add(pipeMeta.serialize());
-
-        final PipeStaticMeta staticMeta = pipeMeta.getStaticMeta();
-
-        final Map<Integer, PipeTask> pipeTaskMap = 
pipeTaskManager.getPipeTasks(staticMeta);
-        final boolean isAllDataRegionCompleted =
-            pipeTaskMap == null
-                || pipeTaskMap.entrySet().stream()
-                    .filter(entry -> dataRegionIds.contains(entry.getKey()))
-                    .allMatch(entry -> ((PipeDataNodeTask) 
entry.getValue()).isCompleted());
-        final String sourceModeValue =
-            pipeMeta
-                .getStaticMeta()
-                .getSourceParameters()
-                .getStringOrDefault(
-                    Arrays.asList(
-                        PipeSourceConstant.EXTRACTOR_MODE_KEY, 
PipeSourceConstant.SOURCE_MODE_KEY),
-                    PipeSourceConstant.EXTRACTOR_MODE_DEFAULT_VALUE);
-        final boolean includeDataAndNeedDrop =
-            
DataRegionListeningFilter.parseInsertionDeletionListeningOptionPair(
-                        pipeMeta.getStaticMeta().getSourceParameters())
-                    .getLeft()
-                && 
(sourceModeValue.equalsIgnoreCase(PipeSourceConstant.EXTRACTOR_MODE_QUERY_VALUE)
-                    || sourceModeValue.equalsIgnoreCase(
-                        PipeSourceConstant.EXTRACTOR_MODE_SNAPSHOT_VALUE));
-
-        final boolean isCompleted = isAllDataRegionCompleted && 
includeDataAndNeedDrop;
-        final Pair<Long, Double> remainingEventAndTime =
-            PipeDataNodeSinglePipeMetrics.getInstance()
-                .getRemainingEventAndTime(staticMeta.getPipeName(), 
staticMeta.getCreationTime());
-        pipeCompletedList.add(isCompleted);
-        pipeRemainingEventCountList.add(remainingEventAndTime.getLeft());
-        pipeRemainingTimeList.add(remainingEventAndTime.getRight());
-        pipeDegradedStatusList.add(
-            PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(
-                ((PipeTemporaryMetaInAgent) pipeMeta.getTemporaryMeta())
-                    .getGlobalTsFileEpochDegraded()));
-
-        logger.ifPresent(
-            l ->
-                PipeLogger.log(
-                    l::info,
-                    "Reporting pipe meta: %s, isCompleted: %s, 
remainingEventCount: %s",
-                    pipeMeta.coreReportMessage(),
-                    isCompleted,
-                    remainingEventAndTime.getLeft()));
-      }
-      logger.ifPresent(
-          l -> PipeLogger.log(l::info, "Reported %s pipe metas.", 
pipeMetaBinaryList.size()));
-    } catch (final IOException | IllegalPathException e) {
-      throw new TException(e);
-    }
-    resp.setPipeMetaList(pipeMetaBinaryList);
-    resp.setPipeCompletedList(pipeCompletedList);
-    resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
-    resp.setPipeRemainingTimeList(pipeRemainingTimeList);
-    resp.setPipeDegradedStatusList(pipeDegradedStatusList);
+    collectPipeMetaReport(logger, true).setTo(resp);
     PipeInsertionDataNodeListener.getInstance().listenToHeartbeat(true);
   }
 
@@ -506,19 +411,21 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     LOGGER.debug(
         DataNodePipeMessages.RECEIVED_PIPE_HEARTBEAT_REQUEST_FROM_CONFIG_NODE, 
req.heartbeatId);
 
+    collectPipeMetaReport(logger, false).setTo(resp);
+    PipeInsertionDataNodeListener.getInstance().listenToHeartbeat(true);
+  }
+
+  private PipeMetaReport collectPipeMetaReport(
+      final Optional<Logger> logger, final boolean includeQueryMode) throws 
TException {
     final Set<Integer> dataRegionIds =
         StorageEngine.getInstance().getAllDataRegionIds().stream()
             .map(DataRegionId::getId)
             .collect(Collectors.toSet());
 
-    final List<ByteBuffer> pipeMetaBinaryList = new ArrayList<>();
-    final List<Boolean> pipeCompletedList = new ArrayList<>();
-    final List<Long> pipeRemainingEventCountList = new ArrayList<>();
-    final List<Double> pipeRemainingTimeList = new ArrayList<>();
-    final List<Integer> pipeDegradedStatusList = new ArrayList<>();
+    final PipeMetaReport report = new PipeMetaReport();
     try {
       for (final PipeMeta pipeMeta : pipeMetaKeeper.getPipeMetaList()) {
-        pipeMetaBinaryList.add(pipeMeta.serialize());
+        report.pipeMetaBinaryList.add(pipeMeta.serialize());
 
         final PipeStaticMeta staticMeta = pipeMeta.getStaticMeta();
 
@@ -528,21 +435,15 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
                 || pipeTaskMap.entrySet().stream()
                     .filter(entry -> dataRegionIds.contains(entry.getKey()))
                     .allMatch(entry -> ((PipeDataNodeTask) 
entry.getValue()).isCompleted());
-
-        final boolean includeDataAndNeedDrop =
-            
DataRegionListeningFilter.parseInsertionDeletionListeningOptionPair(
-                        pipeMeta.getStaticMeta().getSourceParameters())
-                    .getLeft()
-                && 
isSnapshotMode(pipeMeta.getStaticMeta().getSourceParameters());
-
-        final boolean isCompleted = isAllDataRegionCompleted && 
includeDataAndNeedDrop;
+        final boolean isCompleted =
+            isAllDataRegionCompleted && includeDataAndNeedDrop(pipeMeta, 
includeQueryMode);
         final Pair<Long, Double> remainingEventAndTime =
             PipeDataNodeSinglePipeMetrics.getInstance()
                 .getRemainingEventAndTime(staticMeta.getPipeName(), 
staticMeta.getCreationTime());
-        pipeCompletedList.add(isCompleted);
-        pipeRemainingEventCountList.add(remainingEventAndTime.getLeft());
-        pipeRemainingTimeList.add(remainingEventAndTime.getRight());
-        pipeDegradedStatusList.add(
+        report.pipeCompletedList.add(isCompleted);
+        
report.pipeRemainingEventCountList.add(remainingEventAndTime.getLeft());
+        report.pipeRemainingTimeList.add(remainingEventAndTime.getRight());
+        report.pipeDegradedStatusList.add(
             PipeTemporaryMeta.encodeTsFileEpochDegradedStatus(
                 ((PipeTemporaryMetaInAgent) pipeMeta.getTemporaryMeta())
                     .getGlobalTsFileEpochDegraded()));
@@ -551,22 +452,66 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
             l ->
                 PipeLogger.log(
                     l::info,
-                    "Reporting pipe meta: %s, isCompleted: %s, 
remainingEventCount: %s",
+                    DataNodePipeMessages
+                        
.LOG_REPORTING_PIPE_META_ARG_ISCOMPLETED_ARG_REMAININGEVENTCOUNT_ARG_8F996DF3,
                     pipeMeta.coreReportMessage(),
                     isCompleted,
                     remainingEventAndTime.getLeft()));
       }
       logger.ifPresent(
-          l -> PipeLogger.log(l::info, "Reported %s pipe metas.", 
pipeMetaBinaryList.size()));
+          l ->
+              PipeLogger.log(
+                  l::info,
+                  DataNodePipeMessages.LOG_REPORTED_ARG_PIPE_METAS_12068FC6,
+                  report.pipeMetaBinaryList.size()));
     } catch (final IOException | IllegalPathException e) {
       throw new TException(e);
     }
-    resp.setPipeMetaList(pipeMetaBinaryList);
-    resp.setPipeCompletedList(pipeCompletedList);
-    resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
-    resp.setPipeRemainingTimeList(pipeRemainingTimeList);
-    resp.setPipeDegradedStatusList(pipeDegradedStatusList);
-    PipeInsertionDataNodeListener.getInstance().listenToHeartbeat(true);
+    return report;
+  }
+
+  private boolean includeDataAndNeedDrop(final PipeMeta pipeMeta, final 
boolean includeQueryMode)
+      throws IllegalPathException {
+    final PipeParameters sourceParameters = 
pipeMeta.getStaticMeta().getSourceParameters();
+    if 
(!DataRegionListeningFilter.parseInsertionDeletionListeningOptionPair(sourceParameters)
+        .getLeft()) {
+      return false;
+    }
+    if (!includeQueryMode) {
+      return isSnapshotMode(sourceParameters);
+    }
+
+    final String sourceModeValue =
+        sourceParameters.getStringOrDefault(
+            Arrays.asList(
+                PipeSourceConstant.EXTRACTOR_MODE_KEY, 
PipeSourceConstant.SOURCE_MODE_KEY),
+            PipeSourceConstant.EXTRACTOR_MODE_DEFAULT_VALUE);
+    return 
sourceModeValue.equalsIgnoreCase(PipeSourceConstant.EXTRACTOR_MODE_QUERY_VALUE)
+        || 
sourceModeValue.equalsIgnoreCase(PipeSourceConstant.EXTRACTOR_MODE_SNAPSHOT_VALUE);
+  }
+
+  private static class PipeMetaReport {
+    private final List<ByteBuffer> pipeMetaBinaryList = new ArrayList<>();
+    private final List<Boolean> pipeCompletedList = new ArrayList<>();
+    private final List<Long> pipeRemainingEventCountList = new ArrayList<>();
+    private final List<Double> pipeRemainingTimeList = new ArrayList<>();
+    private final List<Integer> pipeDegradedStatusList = new ArrayList<>();
+
+    private void setTo(final TDataNodeHeartbeatResp resp) {
+      resp.setPipeMetaList(pipeMetaBinaryList);
+      resp.setPipeCompletedList(pipeCompletedList);
+      resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
+      resp.setPipeRemainingTimeList(pipeRemainingTimeList);
+      resp.setPipeDegradedStatusList(pipeDegradedStatusList);
+    }
+
+    private void setTo(final TPipeHeartbeatResp resp) {
+      resp.setPipeMetaList(pipeMetaBinaryList);
+      resp.setPipeCompletedList(pipeCompletedList);
+      resp.setPipeRemainingEventCountList(pipeRemainingEventCountList);
+      resp.setPipeRemainingTimeList(pipeRemainingTimeList);
+      resp.setPipeDegradedStatusList(pipeDegradedStatusList);
+    }
   }
 
   ///////////////////////// Terminate Logic /////////////////////////
@@ -820,21 +765,11 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
       return;
     }
 
-    final PipeMeta pipeMetaInAgent = 
pipeMetaKeeper.getPipeMeta(staticMeta.getPipeName());
-    final boolean ignoreRegisteredSinkSubtasks =
-        Objects.nonNull(pipeMetaInAgent)
-            && (!pipeMetaInAgent.getStaticMeta().equals(staticMeta)
-                || 
PipeStatus.DROPPED.equals(pipeMetaInAgent.getRuntimeMeta().getStatus().get()));
-    calculateMemoryUsage(
-        staticMeta,
-        collectPipeTasksToBeCreated(pipeMetaFromCoordinator),
-        ignoreRegisteredSinkSubtasks);
+    calculateMemoryUsage(staticMeta, 
collectPipeTasksToBeCreated(pipeMetaFromCoordinator));
   }
 
   private void calculateMemoryUsage(
-      final PipeStaticMeta staticMeta,
-      final List<Pair<Integer, PipeTaskMeta>> pipeTasksToBeCreated,
-      final boolean ignoreRegisteredSinkSubtasks)
+      final PipeStaticMeta staticMeta, final List<Pair<Integer, PipeTaskMeta>> 
pipeTasksToBeCreated)
       throws IllegalPathException {
     if (!PipeConfig.getInstance().isPipeEnableMemoryCheck()
         || !isInnerSource(staticMeta.getSourceParameters())
@@ -848,8 +783,7 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
     }
 
     final MemoryEstimation memoryEstimation =
-        calculateIncrementalMemoryUsage(
-            staticMeta, pipeTasksToBeCreated, ignoreRegisteredSinkSubtasks);
+        calculateIncrementalMemoryUsage(staticMeta, pipeTasksToBeCreated);
     calculateInsertNodeQueueMemory(
         staticMeta.getSourceParameters(), 
memoryEstimation.dataRegionTaskCount);
 
@@ -917,48 +851,19 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent {
 
   private MemoryEstimation calculateIncrementalMemoryUsage(
       final PipeStaticMeta staticMeta,
-      final List<Pair<Integer, PipeTaskMeta>> pipeTasksToBeCreated,
-      final boolean ignoreRegisteredSinkSubtasks) {
-    long needMemory = 0;
+      final List<Pair<Integer, PipeTaskMeta>> pipeTasksToBeCreated) {
     int dataRegionTaskCount = 0;
-    final Set<String> sinkSubtasksToBeCreated = new HashSet<>();
 
     for (final Pair<Integer, PipeTaskMeta> regionIdAndTaskMeta : 
pipeTasksToBeCreated) {
-      final int regionId = regionIdAndTaskMeta.getLeft();
-      final PipeTaskMeta pipeTaskMeta = regionIdAndTaskMeta.getRight();
-      final PipeParameters sourceParameters =
-          PipeDataNodeTaskBuilder.blendUserAndSystemParameters(
-              staticMeta.getSourceParameters(), pipeTaskMeta);
-      final PipeParameters sinkParameters =
-          PipeDataNodeTaskBuilder.blendUserAndSystemParameters(
-              staticMeta.getSinkParameters(), pipeTaskMeta);
-      PipeDataNodeTaskBuilder.preprocessParameters(sourceParameters, 
sinkParameters);
-
-      final boolean isDataRegionTask = isDataRegionTask(regionId);
-      if (isDataRegionTask) {
+      if (isDataRegionTask(regionIdAndTaskMeta.getLeft())) {
         dataRegionTaskCount++;
-        needMemory += calculateTsFileParserMemory(sourceParameters, 
sinkParameters);
-      }
-
-      final String sinkSubtaskId =
-          PipeSinkSubtaskManager.generateAttributeSortedString(sinkParameters, 
regionId);
-      if (isDataRegionTask
-          && !sinkSubtasksToBeCreated.contains(sinkSubtaskId)
-          && (ignoreRegisteredSinkSubtasks
-              || !PipeSinkSubtaskManager.instance()
-                  .hasRegisteredSubtasks(sinkParameters, regionId))) {
-        sinkSubtasksToBeCreated.add(sinkSubtaskId);
-        final int sinkSubtaskNum =
-            PipeSinkSubtaskManager.calculateSinkSubtaskNum(sinkParameters, 
regionId);
-        needMemory += calculateSinkBatchMemory(sinkParameters) * 
sinkSubtaskNum;
-        needMemory +=
-            calculateSendTsFileReadBufferMemory(sourceParameters, 
sinkParameters) * sinkSubtaskNum;
       }
     }
 
-    if (dataRegionTaskCount > 0) {
-      needMemory += calculateAssignerMemory(staticMeta.getSourceParameters());
-    }
+    // TsFile parser, sink batch, and TsFile read buffer memory are allocated 
dynamically
+    // from PipeMemoryManager only while they are active.
+    final long needMemory =
+        dataRegionTaskCount > 0 ? 
calculateAssignerMemory(staticMeta.getSourceParameters()) : 0;
     return new MemoryEstimation(needMemory, dataRegionTaskCount);
   }
 
@@ -1029,212 +934,6 @@ public class PipeDataNodeTaskAgent extends PipeTaskAgent 
{
     }
   }
 
-  private long calculateTsFileParserMemory(
-      final PipeParameters sourceParameters, final PipeParameters 
sinkParameters) {
-
-    // If the source is not history, we do not need to allocate memory
-    boolean isExtractorHistory =
-        sourceParameters.getBooleanOrDefault(
-                SystemConstant.RESTART_OR_NEWLY_ADDED_KEY,
-                SystemConstant.RESTART_OR_NEWLY_ADDED_DEFAULT_VALUE)
-            || sourceParameters.getBooleanOrDefault(
-                Arrays.asList(EXTRACTOR_HISTORY_ENABLE_KEY, 
SOURCE_HISTORY_ENABLE_KEY),
-                EXTRACTOR_HISTORY_ENABLE_DEFAULT_VALUE);
-
-    // If the source is history, and has start/end time, we need to allocate 
memory
-    boolean isTSFileParser =
-        isExtractorHistory
-            && sourceParameters.hasAnyAttributes(
-                EXTRACTOR_HISTORY_START_TIME_KEY, 
SOURCE_HISTORY_START_TIME_KEY);
-
-    isTSFileParser =
-        isTSFileParser
-            || (isExtractorHistory
-                && sourceParameters.hasAnyAttributes(
-                    EXTRACTOR_HISTORY_END_TIME_KEY, 
SOURCE_HISTORY_END_TIME_KEY));
-
-    // if the source has start/end time, we need to allocate memory
-    isTSFileParser =
-        isTSFileParser
-            || sourceParameters.hasAnyAttributes(SOURCE_START_TIME_KEY, 
EXTRACTOR_START_TIME_KEY);
-
-    isTSFileParser =
-        isTSFileParser
-            || sourceParameters.hasAnyAttributes(SOURCE_END_TIME_KEY, 
EXTRACTOR_END_TIME_KEY);
-
-    // If the source has pattern or path, we need to allocate memory
-    isTSFileParser =
-        isTSFileParser
-            || sourceParameters.hasAnyAttributes(
-                EXTRACTOR_PATTERN_KEY,
-                SOURCE_PATTERN_KEY,
-                EXTRACTOR_PATTERN_INCLUSION_KEY,
-                SOURCE_PATTERN_INCLUSION_KEY,
-                EXTRACTOR_PATTERN_EXCLUSION_KEY,
-                SOURCE_PATTERN_EXCLUSION_KEY);
-
-    isTSFileParser =
-        isTSFileParser
-            || sourceParameters.hasAnyAttributes(
-                EXTRACTOR_PATH_KEY,
-                SOURCE_PATH_KEY,
-                EXTRACTOR_PATH_INCLUSION_KEY,
-                SOURCE_PATH_INCLUSION_KEY,
-                EXTRACTOR_PATTERN_INCLUSION_KEY,
-                SOURCE_PATTERN_INCLUSION_KEY,
-                EXTRACTOR_PATH_EXCLUSION_KEY,
-                SOURCE_PATH_EXCLUSION_KEY);
-
-    // If the source is not hybrid, we do need to allocate memory
-    isTSFileParser =
-        isTSFileParser
-            || !PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE.equals(
-                sinkParameters.getStringOrDefault(
-                    Arrays.asList(
-                        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.SINK_FORMAT_KEY),
-                    PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE));
-
-    if (!isTSFileParser) {
-      return 0;
-    }
-
-    return PipeConfig.getInstance().getTsFileParserMemory();
-  }
-
-  private static long calculateSinkBatchMemory(final PipeParameters 
sinkParameters) {
-    final String format =
-        sinkParameters.getStringOrDefault(
-            Arrays.asList(PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.SINK_FORMAT_KEY),
-            PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE);
-    final boolean usingTsFileBatch = 
PipeSinkConstant.CONNECTOR_FORMAT_TS_FILE_VALUE.equals(format);
-
-    // TsFile format always uses a batch. Other formats only use a batch when 
batch mode is enabled.
-    final boolean needUseBatch =
-        usingTsFileBatch
-            || sinkParameters.getBooleanOrDefault(
-                Arrays.asList(
-                    PipeSinkConstant.CONNECTOR_IOTDB_BATCH_MODE_ENABLE_KEY,
-                    PipeSinkConstant.SINK_IOTDB_BATCH_MODE_ENABLE_KEY),
-                
PipeSinkConstant.CONNECTOR_IOTDB_BATCH_MODE_ENABLE_DEFAULT_VALUE);
-
-    if (!needUseBatch) {
-      return 0;
-    }
-
-    final long batchSizeInBytes =
-        sinkParameters.getLongOrDefault(
-            Arrays.asList(
-                PipeSinkConstant.CONNECTOR_IOTDB_BATCH_SIZE_KEY,
-                PipeSinkConstant.SINK_IOTDB_BATCH_SIZE_KEY),
-            usingTsFileBatch
-                ? 
PipeSinkConstant.CONNECTOR_IOTDB_TS_FILE_BATCH_SIZE_DEFAULT_VALUE
-                : 
PipeSinkConstant.CONNECTOR_IOTDB_PLAIN_BATCH_SIZE_DEFAULT_VALUE);
-
-    return batchSizeInBytes * calculateBatchShardCount(sinkParameters, 
usingTsFileBatch);
-  }
-
-  private static long calculateBatchShardCount(
-      final PipeParameters sinkParameters, final boolean usingTsFileBatch) {
-    if (usingTsFileBatch
-        || !sinkParameters.getBooleanOrDefault(
-            Arrays.asList(
-                PipeSinkConstant.CONNECTOR_LEADER_CACHE_ENABLE_KEY,
-                PipeSinkConstant.SINK_LEADER_CACHE_ENABLE_KEY),
-            PipeSinkConstant.CONNECTOR_LEADER_CACHE_ENABLE_DEFAULT_VALUE)) {
-      return 1;
-    }
-
-    // Plain batches always allocate the default batch and may lazily allocate 
one batch per target
-    // endpoint when leader cache splits events by endpoint.
-    return 1L + calculateTargetEndPointCount(sinkParameters);
-  }
-
-  private static int calculateTargetEndPointCount(final PipeParameters 
sinkParameters) {
-    final Set<TEndPoint> targetEndPoints = new HashSet<>();
-    try {
-      addTargetEndPoint(
-          targetEndPoints,
-          sinkParameters,
-          PipeSinkConstant.CONNECTOR_IOTDB_IP_KEY,
-          PipeSinkConstant.CONNECTOR_IOTDB_HOST_KEY,
-          PipeSinkConstant.CONNECTOR_IOTDB_PORT_KEY);
-      addTargetEndPoint(
-          targetEndPoints,
-          sinkParameters,
-          PipeSinkConstant.SINK_IOTDB_IP_KEY,
-          PipeSinkConstant.SINK_IOTDB_HOST_KEY,
-          PipeSinkConstant.SINK_IOTDB_PORT_KEY);
-      if 
(sinkParameters.hasAttribute(PipeSinkConstant.CONNECTOR_IOTDB_NODE_URLS_KEY)) {
-        targetEndPoints.addAll(
-            NodeUrlUtils.parseTEndPointUrls(
-                Arrays.asList(
-                    sinkParameters
-                        
.getStringByKeys(PipeSinkConstant.CONNECTOR_IOTDB_NODE_URLS_KEY)
-                        .replace(" ", "")
-                        .split(","))));
-      }
-      if 
(sinkParameters.hasAttribute(PipeSinkConstant.SINK_IOTDB_NODE_URLS_KEY)) {
-        targetEndPoints.addAll(
-            NodeUrlUtils.parseTEndPointUrls(
-                Arrays.asList(
-                    sinkParameters
-                        
.getStringByKeys(PipeSinkConstant.SINK_IOTDB_NODE_URLS_KEY)
-                        .replace(" ", "")
-                        .split(","))));
-      }
-    } catch (final Exception ignored) {
-      return 1;
-    }
-    return Math.max(1, targetEndPoints.size());
-  }
-
-  private static void addTargetEndPoint(
-      final Set<TEndPoint> targetEndPoints,
-      final PipeParameters sinkParameters,
-      final String ipKey,
-      final String hostKey,
-      final String portKey) {
-    if (sinkParameters.hasAttribute(ipKey) && 
sinkParameters.hasAttribute(portKey)) {
-      targetEndPoints.add(
-          new TEndPoint(
-              sinkParameters.getStringByKeys(ipKey), 
sinkParameters.getIntByKeys(portKey)));
-    }
-    if (sinkParameters.hasAttribute(hostKey) && 
sinkParameters.hasAttribute(portKey)) {
-      targetEndPoints.add(
-          new TEndPoint(
-              sinkParameters.getStringByKeys(hostKey), 
sinkParameters.getIntByKeys(portKey)));
-    }
-  }
-
-  private static long calculateSendTsFileReadBufferMemory(
-      final PipeParameters sourceParameters, final PipeParameters 
sinkParameters) {
-    // If the source is history enable, we need to transfer tsfile
-    boolean needTransferTsFile =
-        sourceParameters.getBooleanOrDefault(
-                SystemConstant.RESTART_OR_NEWLY_ADDED_KEY,
-                SystemConstant.RESTART_OR_NEWLY_ADDED_DEFAULT_VALUE)
-            || sourceParameters.getBooleanOrDefault(
-                Arrays.asList(EXTRACTOR_HISTORY_ENABLE_KEY, 
SOURCE_HISTORY_ENABLE_KEY),
-                EXTRACTOR_HISTORY_ENABLE_DEFAULT_VALUE);
-
-    String format =
-        sinkParameters.getStringOrDefault(
-            Arrays.asList(PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.SINK_FORMAT_KEY),
-            PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE);
-
-    // If the sink format is tsfile and hybrid, we need to transfer tsfile
-    needTransferTsFile =
-        needTransferTsFile
-            || PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE.equals(format)
-            || PipeSinkConstant.CONNECTOR_FORMAT_TS_FILE_VALUE.equals(format);
-
-    if (!needTransferTsFile) {
-      return 0;
-    }
-
-    return PipeConfig.getInstance().getPipeSinkReadFileBufferSize();
-  }
-
   private long calculateAssignerMemory(final PipeParameters sourceParameters) {
     try {
       if (!PipeInsertionDataNodeListener.getInstance().isEmpty()
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java
index f9d02f7c2ca..1003c8a0ccb 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/subtask/processor/PipeProcessorSubtask.java
@@ -179,6 +179,8 @@ public class PipeProcessorSubtask extends 
PipeReportableSubtask {
                   event1 -> {
                     try {
                       pipeProcessor.process(event1, outputEventCollector);
+                    } catch (PipeRuntimeOutOfMemoryCriticalException e) {
+                      throw e;
                     } catch (Exception e) {
                       ex.set(e);
                     }
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 085d5388c64..d59b39ecca5 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
@@ -726,38 +726,37 @@ public class PipeTsFileInsertionEvent extends 
PipeInsertionEvent
 
   public void consumeTabletInsertionEventsWithRetry(
       final TabletInsertionEventConsumer consumer, final String callerName) 
throws Exception {
-    final Iterable<TabletInsertionEvent> iterable = toTabletInsertionEvents();
-    final Iterator<TabletInsertionEvent> iterator = iterable.iterator();
     int tabletEventCount = 0;
-    while (iterator.hasNext()) {
-      final TabletInsertionEvent parsedEvent = iterator.next();
-      tabletEventCount++;
-      int retryCount = 0;
-      while (true) {
-        // If failed due do insufficient memory, retry until success to avoid 
race among multiple
-        // processor threads
+    try {
+      final Iterable<TabletInsertionEvent> iterable = 
toTabletInsertionEvents();
+      final Iterator<TabletInsertionEvent> iterator = iterable.iterator();
+      while (iterator.hasNext()) {
+        final TabletInsertionEvent parsedEvent = iterator.next();
+        tabletEventCount++;
         try {
           consumer.consume((PipeRawTabletInsertionEvent) parsedEvent);
-          break;
         } catch (final PipeRuntimeOutOfMemoryCriticalException e) {
-          if (retryCount++ % 100 == 0) {
-            LOGGER.warn(
-                
DataNodePipeMessages.FAILED_TO_ALLOCATE_MEMORY_FOR_PARSING_TSFILE,
-                callerName,
-                getTsFile(),
-                tabletEventCount,
-                retryCount);
-          } else if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug(
-                
DataNodePipeMessages.FAILED_TO_ALLOCATE_MEMORY_FOR_PARSING_TSFILE,
-                callerName,
-                getTsFile(),
-                tabletEventCount,
-                retryCount,
-                e);
-          }
+          releaseParsedTabletEvent(parsedEvent);
+          throw e;
         }
       }
+    } catch (final PipeRuntimeOutOfMemoryCriticalException e) {
+      close();
+      LOGGER.warn(
+          DataNodePipeMessages.FAILED_TO_ALLOCATE_MEMORY_FOR_PARSING_TSFILE,
+          callerName,
+          getTsFile(),
+          tabletEventCount,
+          e);
+      throw e;
+    }
+  }
+
+  private void releaseParsedTabletEvent(final TabletInsertionEvent 
parsedEvent) {
+    if (parsedEvent instanceof PipeRawTabletInsertionEvent
+        && ((PipeRawTabletInsertionEvent) parsedEvent).getReferenceCount() == 0
+        && !((PipeRawTabletInsertionEvent) parsedEvent).isReleased()) {
+      ((PipeRawTabletInsertionEvent) 
parsedEvent).clearReferenceCount(getClass().getName());
     }
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParser.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParser.java
index 627731fa7ed..da5cc6b88db 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParser.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParser.java
@@ -24,7 +24,6 @@ import org.apache.iotdb.commons.path.PatternTreeMap;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
 import org.apache.iotdb.commons.pipe.datastructure.pattern.TablePattern;
 import org.apache.iotdb.commons.pipe.datastructure.pattern.TreePattern;
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.event.common.PipeInsertionEvent;
 import 
org.apache.iotdb.db.pipe.event.common.tsfile.parser.table.TsFileInsertionEventTableParser;
@@ -109,9 +108,7 @@ public abstract class TsFileInsertionEventParser implements 
AutoCloseable {
     this.sourceEvent = sourceEvent;
 
     this.allocatedMemoryBlockForTablet =
-        PipeDataNodeResourceManager.memory()
-            .forceAllocateForTabletWithRetry(
-                
IoTDBDescriptor.getInstance().getConfig().getPipeDataStructureTabletSizeInBytes());
+        
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
 
     LOGGER.debug(
         
DataNodePipeMessages.TSFILE_HAS_INITIALIZED_PIPENAME_CREATION_TIME_PATTERN,
@@ -180,6 +177,13 @@ public abstract class TsFileInsertionEventParser 
implements AutoCloseable {
     }
   }
 
+  protected void releaseTabletMemoryBlock() {
+    if (allocatedMemoryBlockForTablet != null
+        && allocatedMemoryBlockForTablet.getMemoryUsageInBytes() > 0) {
+      
PipeDataNodeResourceManager.memory().forceResize(allocatedMemoryBlockForTablet, 
0);
+    }
+  }
+
   @Override
   public void close() {
     tabletInsertionIterable = null;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/query/TsFileInsertionEventQueryParser.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/query/TsFileInsertionEventQueryParser.java
index 33652f3f3da..ba840b11c32 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/query/TsFileInsertionEventQueryParser.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/query/TsFileInsertionEventQueryParser.java
@@ -524,6 +524,7 @@ public class TsFileInsertionEventQueryParser extends 
TsFileInsertionEventParser
                   final Tablet tablet = tabletIterator.next();
                   // Record tablet metrics
                   recordTabletMetrics(tablet);
+                  releaseTabletMemoryBlock();
                   final boolean isAligned =
                       deviceIsAlignedMap.getOrDefault(
                           
IDeviceID.Factory.DEFAULT_FACTORY.create(tablet.getDeviceId()), false);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/scan/TsFileInsertionEventScanParser.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/scan/TsFileInsertionEventScanParser.java
index 76d0a0916fb..d4d6ebdc293 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/scan/TsFileInsertionEventScanParser.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/scan/TsFileInsertionEventScanParser.java
@@ -29,7 +29,6 @@ import 
org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
 import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.datastructure.pattern.TreePattern;
 import org.apache.iotdb.db.auth.AuthorityChecker;
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.event.common.PipeInsertionEvent;
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
@@ -144,12 +143,9 @@ public class TsFileInsertionEventScanParser extends 
TsFileInsertionEventParser {
     filter = Objects.nonNull(timeFilterExpression) ? 
timeFilterExpression.getFilter() : null;
 
     this.allocatedMemoryBlockForBatchData =
-        PipeDataNodeResourceManager.memory()
-            .forceAllocateForTabletWithRetry(
-                
IoTDBDescriptor.getInstance().getConfig().getPipeDataStructureTabletSizeInBytes());
+        
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
     this.allocatedMemoryBlockForChunk =
-        PipeDataNodeResourceManager.memory()
-            
.forceAllocateForTabletWithRetry(PipeConfig.getInstance().getPipeMaxReaderChunkSize());
+        
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
 
     try {
       currentModifications =
@@ -225,6 +221,9 @@ public class TsFileInsertionEventScanParser extends 
TsFileInsertionEventParser {
                     throw new NoSuchElementException();
                   }
 
+                  // Release the previous parser-owned tablet buffer before 
allocating the next
+                  // tablet.
+                  releaseTabletMemoryBlock();
                   // currentIsAligned is initialized when 
TsFileInsertionEventScanParser is
                   // constructed.
                   // When the getNextTablet function is called, 
currentIsAligned may be updated,
@@ -263,6 +262,9 @@ public class TsFileInsertionEventScanParser extends 
TsFileInsertionEventParser {
                             sourceEvent,
                             isLast);
                   } finally {
+                    // The raw event owns the generated tablet; only release 
the parser-side memory
+                    // accounting.
+                    releaseTabletMemoryBlock();
                     if (isLast) {
                       recordParseEndTimeIfNecessary();
                       close();
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/table/TsFileInsertionEventTableParser.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/table/TsFileInsertionEventTableParser.java
index 1aa7140bb75..187d55a22a2 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/table/TsFileInsertionEventTableParser.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/table/TsFileInsertionEventTableParser.java
@@ -22,11 +22,9 @@ package 
org.apache.iotdb.db.pipe.event.common.tsfile.parser.table;
 import org.apache.iotdb.commons.audit.IAuditEntity;
 import org.apache.iotdb.commons.exception.auth.AccessDeniedException;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
-import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.datastructure.pattern.TablePattern;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.metadata.QualifiedObjectName;
 import org.apache.iotdb.db.auth.AuthorityChecker;
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.event.common.PipeInsertionEvent;
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
@@ -94,25 +92,14 @@ public class TsFileInsertionEventTableParser extends 
TsFileInsertionEventParser
       allocatedMemoryBlockForModifications =
           PipeDataNodeResourceManager.memory()
               
.forceAllocateForTabletWithRetry(currentModifications.ramBytesUsed());
-      long tableSize =
-          Math.min(
-              
IoTDBDescriptor.getInstance().getConfig().getPipeDataStructureTabletSizeInBytes(),
-              IoTDBDescriptor.getInstance().getConfig().getTargetChunkSize());
-
       this.allocatedMemoryBlockForChunk =
-          PipeDataNodeResourceManager.memory()
-              .forceAllocateForTabletWithRetry(
-                  PipeConfig.getInstance().getPipeMaxReaderChunkSize());
+          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
       this.allocatedMemoryBlockForBatchData =
-          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(tableSize);
+          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
       this.allocatedMemoryBlockForChunkMeta =
-          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(tableSize);
+          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
       this.allocatedMemoryBlockForTableSchemas =
-          PipeDataNodeResourceManager.memory()
-              .forceAllocateForTabletWithRetry(
-                  IoTDBDescriptor.getInstance()
-                      .getConfig()
-                      .getPipeDataStructureTabletSizeInBytes());
+          
PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(0);
 
       this.startTime = startTime;
       this.endTime = endTime;
@@ -240,6 +227,7 @@ public class TsFileInsertionEventTableParser extends 
TsFileInsertionEventParser
 
                     final Tablet tablet = tabletIterator.next();
                     recordTabletMetrics(tablet);
+                    releaseTabletMemoryBlock();
                     if (!PipeRawTabletInsertionEvent.isTabletEmpty(tablet)) {
                       bufferedTablet = tablet;
                       return true;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
index 8cbe56b4328..4d4ed0c4b42 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/aggregate/AggregateProcessor.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.commons.consensus.index.ProgressIndex;
 import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
 import 
org.apache.iotdb.commons.consensus.index.impl.TimeWindowStateProgressIndex;
 import org.apache.iotdb.commons.exception.IllegalPathException;
+import 
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
 import 
org.apache.iotdb.commons.pipe.config.plugin.env.PipeTaskProcessorRuntimeEnvironment;
 import org.apache.iotdb.commons.pipe.event.EnrichedEvent;
@@ -549,6 +550,8 @@ public class AggregateProcessor implements PipeProcessor {
                 event -> {
                   try {
                     process(event, eventCollector);
+                  } catch (PipeRuntimeOutOfMemoryCriticalException e) {
+                    throw e;
                   } catch (Exception e) {
                     ex.set(e);
                   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/DownSamplingProcessor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/DownSamplingProcessor.java
index a8e0c270570..fcaa0feb058 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/DownSamplingProcessor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/downsampling/DownSamplingProcessor.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.pipe.processor.downsampling;
 
 import org.apache.iotdb.commons.consensus.DataRegionId;
+import 
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
 import 
org.apache.iotdb.commons.pipe.config.plugin.env.PipeTaskProcessorRuntimeEnvironment;
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeInsertNodeTabletInsertionEvent;
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
@@ -156,6 +157,8 @@ public abstract class DownSamplingProcessor implements 
PipeProcessor {
                   event -> {
                     try {
                       process(event, eventCollector);
+                    } catch (PipeRuntimeOutOfMemoryCriticalException e) {
+                      throw e;
                     } catch (Exception e) {
                       ex.set(e);
                     }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManager.java
index eb5e6b9ff0a..3bdda717bef 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManager.java
@@ -328,12 +328,6 @@ public class PipeMemoryManager {
     }
 
     final long oldSize = block.getMemoryUsageInBytes();
-    if (oldSize == 0) {
-      // If the memory block is not registered, we need to register it first.
-      // Otherwise, the memory usage will be inconsistent.
-      // See registerMemoryBlock for more details.
-      allocatedBlocks.add(block);
-    }
 
     if (oldSize >= targetSize) {
       memoryBlock.release(oldSize - targetSize);
@@ -349,6 +343,8 @@ public class PipeMemoryManager {
       if (targetSize == 0) {
         allocatedBlocks.remove(block);
       }
+
+      this.notifyAll();
       return;
     }
 
@@ -358,6 +354,12 @@ public class PipeMemoryManager {
       if (getTotalNonFloatingMemorySizeInBytes() - 
memoryBlock.getUsedMemoryInBytes()
           >= sizeInBytes) {
         memoryBlock.forceAllocateWithoutLimitation(sizeInBytes);
+        if (oldSize == 0) {
+          // If the memory block is not registered, we need to register it 
first.
+          // Otherwise, the memory usage will be inconsistent.
+          // See registerMemoryBlock for more details.
+          allocatedBlocks.add(block);
+        }
         if (block instanceof PipeTabletMemoryBlock) {
           usedMemorySizeInBytesOfTablets += sizeInBytes;
         }
@@ -485,6 +487,9 @@ public class PipeMemoryManager {
     if (getTotalNonFloatingMemorySizeInBytes() - 
memoryBlock.getUsedMemoryInBytes()
         >= memoryInBytesNeededToBeAllocated) {
       
memoryBlock.forceAllocateWithoutLimitation(memoryInBytesNeededToBeAllocated);
+      if (block.getMemoryUsageInBytes() == 0) {
+        allocatedBlocks.add(block);
+      }
       if (block instanceof PipeTabletMemoryBlock) {
         usedMemorySizeInBytesOfTablets += memoryInBytesNeededToBeAllocated;
       }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventBatch.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventBatch.java
index aede0e994d9..b850de896a1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventBatch.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventBatch.java
@@ -61,8 +61,7 @@ public abstract class PipeTabletEventBatch implements 
AutoCloseable {
 
     // limit in buffer size
     this.maxBatchSizeInBytes = requestMaxBatchSizeInBytes;
-    this.allocatedMemoryBlock =
-        
PipeDataNodeResourceManager.memory().forceAllocate(requestMaxBatchSizeInBytes);
+    this.allocatedMemoryBlock = 
PipeDataNodeResourceManager.memory().forceAllocate(0);
     if (recordMetric != null) {
       this.recordMetric = recordMetric;
     } else {
@@ -97,6 +96,10 @@ public abstract class PipeTabletEventBatch implements 
AutoCloseable {
             events.add((EnrichedEvent) event);
           }
         } catch (final Exception e) {
+          if (events.isEmpty()) {
+            clearBatchData();
+            resetMemoryUsage();
+          }
           // If the event is not added to the batch, we need to decrease the 
reference count.
           ((EnrichedEvent) event)
               
.decreaseReferenceCount(PipeTransferBatchReqBuilder.class.getName(), false);
@@ -126,7 +129,27 @@ public abstract class PipeTabletEventBatch implements 
AutoCloseable {
   protected abstract boolean constructBatch(final TabletInsertionEvent event)
       throws WALPipeException, IOException;
 
+  protected void increaseTotalBufferSizeAndUpdateMemoryBlock(final long 
bufferSize) {
+    if (bufferSize <= 0) {
+      return;
+    }
+
+    final long newTotalBufferSize = Math.min(totalBufferSize + bufferSize, 
maxBatchSizeInBytes);
+    PipeDataNodeResourceManager.memory().forceResize(allocatedMemoryBlock, 
newTotalBufferSize);
+    totalBufferSize = newTotalBufferSize;
+  }
+
+  protected void releaseAllocatedMemoryBlock() {
+    PipeDataNodeResourceManager.memory().forceResize(allocatedMemoryBlock, 0);
+  }
+
+  protected void clearBatchData() {}
+
   public boolean shouldEmit() {
+    if (events.isEmpty()) {
+      return false;
+    }
+
     final long diff = System.currentTimeMillis() - firstEventProcessingTime;
     if (totalBufferSize >= maxBatchSizeInBytes || diff >= maxDelayInMs) {
       recordMetric.accept(diff, totalBufferSize, events.size());
@@ -138,23 +161,26 @@ public abstract class PipeTabletEventBatch implements 
AutoCloseable {
   public synchronized void onSuccess() {
     events.clear();
 
-    totalBufferSize = 0;
-
-    firstEventProcessingTime = Long.MIN_VALUE;
+    resetMemoryUsage();
   }
 
   @Override
   public synchronized void close() {
+    if (isClosed) {
+      return;
+    }
     isClosed = true;
 
     clearEventsReferenceCount(PipeTabletEventBatch.class.getName());
     events.clear();
+    clearBatchData();
+    resetMemoryUsage();
     allocatedMemoryBlock.close();
   }
 
   /**
-   * Discard all events of the given pipe. This method only clears the 
reference count of the events
-   * and discard them, but do not modify other objects (such as buffers) for 
simplicity.
+   * Discard all events of the given pipe. This method only clears the 
reference count of the
+   * events. If some events remain, cached batch data is kept unchanged for 
simplicity.
    */
   public synchronized void discardEventsOfPipe(
       final String pipeNameToDrop, final long creationTimeToDrop, final int 
regionId) {
@@ -162,14 +188,27 @@ public abstract class PipeTabletEventBatch implements 
AutoCloseable {
   }
 
   public synchronized void discardEventsOfPipe(final CommitterKey 
committerKey) {
-    events.removeIf(
-        event -> {
-          if (isEventFromPipe(event, committerKey)) {
-            
event.clearReferenceCount(IoTDBDataRegionAsyncSink.class.getName());
-            return true;
-          }
-          return false;
-        });
+    final boolean hasDiscardedEvents =
+        events.removeIf(
+            event -> {
+              if (isEventFromPipe(event, committerKey)) {
+                
event.clearReferenceCount(IoTDBDataRegionAsyncSink.class.getName());
+                return true;
+              }
+              return false;
+            });
+    if (hasDiscardedEvents && events.isEmpty()) {
+      clearBatchData();
+      resetMemoryUsage();
+    }
+  }
+
+  private void resetMemoryUsage() {
+    totalBufferSize = 0;
+
+    releaseAllocatedMemoryBlock();
+
+    firstEventProcessingTime = Long.MIN_VALUE;
   }
 
   private static boolean isEventFromPipe(
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventPlainBatch.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventPlainBatch.java
index abb9ae26608..bdf6ee1874a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventPlainBatch.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventPlainBatch.java
@@ -75,7 +75,6 @@ public class PipeTabletEventPlainBatch extends 
PipeTabletEventBatch {
   @Override
   protected boolean constructBatch(final TabletInsertionEvent event) throws 
IOException {
     final long bufferSize = buildTabletInsertionBuffer(event);
-    totalBufferSize += bufferSize;
     pipe2BytesAccumulated.compute(
         new Pair<>(
             ((EnrichedEvent) event).getPipeName(), ((EnrichedEvent) 
event).getCreationTime()),
@@ -86,8 +85,13 @@ public class PipeTabletEventPlainBatch extends 
PipeTabletEventBatch {
 
   @Override
   public synchronized void onSuccess() {
+    clearBatchData();
+
     super.onSuccess();
+  }
 
+  @Override
+  protected void clearBatchData() {
     insertNodeBuffers.clear();
     tabletBuffers.clear();
 
@@ -162,24 +166,21 @@ public class PipeTabletEventPlainBatch extends 
PipeTabletEventBatch {
       final InsertNode insertNode = 
pipeInsertNodeTabletInsertionEvent.getInsertNode();
       if (!(insertNode instanceof RelationalInsertTabletNode)) {
         buffer = insertNode.serializeToByteBuffer();
+        final String databaseName =
+            pipeInsertNodeTabletInsertionEvent.isTableModelEvent()
+                ? 
pipeInsertNodeTabletInsertionEvent.getTableModelDatabaseName()
+                : 
pipeInsertNodeTabletInsertionEvent.getTreeModelDatabaseName();
+        estimateSize = RamUsageEstimator.sizeOf(databaseName) + buffer.limit();
+        increaseTotalBufferSizeAndUpdateMemoryBlock(estimateSize);
         insertNodeBuffers.add(buffer);
-        if (pipeInsertNodeTabletInsertionEvent.isTableModelEvent()) {
-          final String databaseName =
-              pipeInsertNodeTabletInsertionEvent.getTableModelDatabaseName();
-          estimateSize = RamUsageEstimator.sizeOf(databaseName);
-          insertNodeDataBases.add(databaseName);
-        } else {
-          final String databaseName = 
pipeInsertNodeTabletInsertionEvent.getTreeModelDatabaseName();
-          estimateSize = RamUsageEstimator.sizeOf(databaseName);
-          insertNodeDataBases.add(databaseName);
-        }
-        estimateSize += buffer.limit();
+        insertNodeDataBases.add(databaseName);
       } else {
-        for (final Tablet tablet :
-            ((PipeInsertNodeTabletInsertionEvent) event).convertToTablets()) {
-          estimateSize +=
-              constructTabletBatch(
-                  tablet, 
pipeInsertNodeTabletInsertionEvent.getTableModelDatabaseName());
+        final List<Tablet> tablets = 
pipeInsertNodeTabletInsertionEvent.convertToTablets();
+        estimateSize = calculateTabletsSizeInBytes(tablets);
+        increaseTotalBufferSizeAndUpdateMemoryBlock(estimateSize);
+        for (final Tablet tablet : tablets) {
+          constructTabletBatchWithoutMemoryReservation(
+              tablet, 
pipeInsertNodeTabletInsertionEvent.getTableModelDatabaseName());
         }
       }
     } else {
@@ -199,6 +200,7 @@ public class PipeTabletEventPlainBatch extends 
PipeTabletEventBatch {
         }
         final String databaseName = 
pipeRawTabletInsertionEvent.getTreeModelDatabaseName();
         estimateSize = RamUsageEstimator.sizeOf(databaseName) + buffer.limit();
+        increaseTotalBufferSizeAndUpdateMemoryBlock(estimateSize);
         tabletBuffers.add(buffer);
         tabletDataBases.add(databaseName);
       }
@@ -208,12 +210,27 @@ public class PipeTabletEventPlainBatch extends 
PipeTabletEventBatch {
   }
 
   private long constructTabletBatch(final Tablet tablet, final String 
databaseName) {
+    final long estimateSize = calculateTabletSizeInBytes(tablet);
+    increaseTotalBufferSizeAndUpdateMemoryBlock(estimateSize);
+    constructTabletBatchWithoutMemoryReservation(tablet, databaseName);
+    return estimateSize;
+  }
+
+  private void constructTabletBatchWithoutMemoryReservation(
+      final Tablet tablet, final String databaseName) {
     final Pair<Integer, List<Tablet>> currentBatch =
         tableModelTabletMap
             .computeIfAbsent(databaseName, k -> new HashMap<>())
             .computeIfAbsent(tablet.getTableName(), k -> new Pair<>(0, new 
ArrayList<>()));
     currentBatch.setLeft(currentBatch.getLeft() + tablet.getRowSize());
     currentBatch.getRight().add(tablet);
+  }
+
+  private long calculateTabletsSizeInBytes(final List<Tablet> tablets) {
+    return 
tablets.stream().mapToLong(PipeTabletEventPlainBatch::calculateTabletSizeInBytes).sum();
+  }
+
+  private static long calculateTabletSizeInBytes(final Tablet tablet) {
     return PipeMemoryWeightUtil.calculateTabletSizeInBytes(tablet) + 4;
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventTsFileBatch.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventTsFileBatch.java
index 7b511e23fc6..053b42b2c78 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventTsFileBatch.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/payload/evolvable/batch/PipeTabletEventTsFileBatch.java
@@ -86,6 +86,7 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
           (PipeInsertNodeTabletInsertionEvent) event;
       final boolean isTableModel = 
insertNodeTabletInsertionEvent.isTableModelEvent();
       final List<Tablet> tablets = 
insertNodeTabletInsertionEvent.convertToTablets();
+      
increaseTotalBufferSizeAndUpdateMemoryBlock(calculateTabletsSizeInBytes(tablets));
       for (int i = 0; i < tablets.size(); ++i) {
         final Tablet tablet = tablets.get(i);
         if (isTabletEmpty(tablet)) {
@@ -114,6 +115,7 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
       if (isTabletEmpty(tablet)) {
         return true;
       }
+      
increaseTotalBufferSizeAndUpdateMemoryBlock(calculateTabletSizeInBytes(tablet));
       if (rawTabletInsertionEvent.isTableModelEvent()) {
         // table Model
         bufferTableModelTablet(
@@ -139,6 +141,17 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
     return true;
   }
 
+  private long calculateTabletsSizeInBytes(final List<Tablet> tablets) {
+    return tablets.stream()
+        .filter(tablet -> !isTabletEmpty(tablet))
+        .mapToLong(PipeTabletEventTsFileBatch::calculateTabletSizeInBytes)
+        .sum();
+  }
+
+  private static long calculateTabletSizeInBytes(final Tablet tablet) {
+    return PipeMemoryWeightUtil.calculateTabletSizeInBytes(tablet) * 2;
+  }
+
   private void bufferTreeModelTablet(
       final String pipeName,
       final long creationTime,
@@ -146,11 +159,6 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
       final boolean isAligned) {
     new 
PipeTreeModelTabletEventSorter(tablet).deduplicateAndSortTimestampsIfNecessary();
 
-    // TODO: Currently, PipeTreeModelTsFileBuilderV2 still uses 
PipeTreeModelTsFileBuilder as a
-    // fallback builder, so memory table writing and storing temporary tablets 
require double the
-    // memory.
-    totalBufferSize += PipeMemoryWeightUtil.calculateTabletSizeInBytes(tablet) 
* 2;
-
     pipeName2WeightMap.compute(
         new Pair<>(pipeName, creationTime),
         (pipe, weight) -> Objects.nonNull(weight) ? ++weight : 1);
@@ -162,11 +170,6 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
       final String pipeName, final long creationTime, final Tablet tablet, 
final String dataBase) {
     new 
PipeTableModelTabletEventSorter(tablet).sortAndDeduplicateByDevIdTimestamp();
 
-    // TODO: Currently, PipeTableModelTsFileBuilderV2 still uses 
PipeTableModelTsFileBuilder as a
-    // fallback builder, so memory table writing and storing temporary tablets 
require double the
-    // memory.
-    totalBufferSize += PipeMemoryWeightUtil.calculateTabletSizeInBytes(tablet) 
* 2;
-
     pipeName2WeightMap.compute(
         new Pair<>(pipeName, creationTime),
         (pipe, weight) -> Objects.nonNull(weight) ? ++weight : 1);
@@ -209,8 +212,13 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
 
   @Override
   public synchronized void onSuccess() {
+    clearBatchData();
+
     super.onSuccess();
+  }
 
+  @Override
+  protected void clearBatchData() {
     pipeName2WeightMap.clear();
     tableModeTsFileBuilder.onSuccess();
     treeModeTsFileBuilder.onSuccess();
@@ -220,8 +228,6 @@ public class PipeTabletEventTsFileBatch extends 
PipeTabletEventBatch {
   public synchronized void close() {
     super.close();
 
-    pipeName2WeightMap.clear();
-
     tableModeTsFileBuilder.close();
     treeModeTsFileBuilder.close();
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/airgap/IoTDBDataRegionAirGapSink.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/airgap/IoTDBDataRegionAirGapSink.java
index f082a352182..8689069334e 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/airgap/IoTDBDataRegionAirGapSink.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/airgap/IoTDBDataRegionAirGapSink.java
@@ -33,6 +33,8 @@ import 
org.apache.iotdb.db.pipe.event.common.terminate.PipeTerminateEvent;
 import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent;
 import org.apache.iotdb.db.pipe.metric.overview.PipeResourceMetrics;
 import org.apache.iotdb.db.pipe.metric.sink.PipeDataRegionSinkMetrics;
+import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
+import org.apache.iotdb.db.pipe.resource.memory.PipeTsFileMemoryBlock;
 import 
org.apache.iotdb.db.pipe.sink.payload.evolvable.batch.PipeTabletEventBatch;
 import 
org.apache.iotdb.db.pipe.sink.payload.evolvable.batch.PipeTabletEventPlainBatch;
 import 
org.apache.iotdb.db.pipe.sink.payload.evolvable.batch.PipeTabletEventTsFileBatch;
@@ -502,10 +504,13 @@ public class IoTDBDataRegionAirGapSink extends 
IoTDBDataNodeAirGapSink {
       final AirGapSocket socket,
       final boolean isMultiFile)
       throws PipeException, IOException {
-    final int readFileBufferSize = PIPE_CONFIG.getPipeSinkReadFileBufferSize();
-    final byte[] readBuffer = new byte[readFileBufferSize];
-    long position = 0;
-    try (final RandomAccessFile reader = new RandomAccessFile(file, "r")) {
+    final int readFileBufferSize = getReadFileBufferSize(file);
+    try (final PipeTsFileMemoryBlock ignored =
+            PipeDataNodeResourceManager.memory()
+                .forceAllocateForTsFileWithRetry(readFileBufferSize);
+        final RandomAccessFile reader = new RandomAccessFile(file, "r")) {
+      final byte[] readBuffer = new byte[readFileBufferSize];
+      long position = 0;
       while (true) {
         mayLimitRateAndRecordIO(readFileBufferSize);
         final int readLength = reader.read(readBuffer);
@@ -537,6 +542,11 @@ public class IoTDBDataRegionAirGapSink extends 
IoTDBDataNodeAirGapSink {
     }
   }
 
+  private int getReadFileBufferSize(final File file) {
+    return (int)
+        Math.min((long) PIPE_CONFIG.getPipeSinkReadFileBufferSize(), 
Math.max(file.length(), 1L));
+  }
+
   private boolean sendBatch(
       final AirGapSocket socket,
       byte[] bytes,
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/IoTConsensusV2SyncSink.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/IoTConsensusV2SyncSink.java
index 7f82ce8e9a9..5b47b92b092 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/IoTConsensusV2SyncSink.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/IoTConsensusV2SyncSink.java
@@ -39,6 +39,8 @@ import 
org.apache.iotdb.db.pipe.consensus.metric.IoTConsensusV2SinkMetrics;
 import org.apache.iotdb.db.pipe.event.common.deletion.PipeDeleteDataNodeEvent;
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeInsertNodeTabletInsertionEvent;
 import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent;
+import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
+import org.apache.iotdb.db.pipe.resource.memory.PipeTsFileMemoryBlock;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.payload.builder.IoTConsensusV2SyncBatchReqBuilder;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.payload.request.IoTConsensusV2DeleteNodeReq;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.payload.request.IoTConsensusV2TabletInsertNodeReq;
@@ -439,10 +441,13 @@ public class IoTConsensusV2SyncSink extends IoTDBSink {
       final TCommitId tCommitId,
       final TConsensusGroupId tConsensusGroupId)
       throws PipeException, IOException {
-    final int readFileBufferSize = 
PipeConfig.getInstance().getPipeSinkReadFileBufferSize();
-    final byte[] readBuffer = new byte[readFileBufferSize];
-    long position = 0;
-    try (final RandomAccessFile reader = new RandomAccessFile(file, "r")) {
+    final int readFileBufferSize = getReadFileBufferSize(file);
+    try (final PipeTsFileMemoryBlock ignored =
+            PipeDataNodeResourceManager.memory()
+                .forceAllocateForTsFileWithRetry(readFileBufferSize);
+        final RandomAccessFile reader = new RandomAccessFile(file, "r")) {
+      final byte[] readBuffer = new byte[readFileBufferSize];
+      long position = 0;
       while (true) {
         final int readLength = reader.read(readBuffer);
         if (readLength == -1) {
@@ -508,6 +513,13 @@ public class IoTConsensusV2SyncSink extends IoTDBSink {
     }
   }
 
+  private int getReadFileBufferSize(final File file) {
+    return (int)
+        Math.min(
+            (long) PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
+            Math.max(file.length(), 1L));
+  }
+
   private TEndPoint getFollowerUrl() {
     // In current iotConsensusV2 design, one connector corresponds to one 
follower, so the peers is
     // actually a singleton list
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/handler/IoTConsensusV2TsFileInsertionEventHandler.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/handler/IoTConsensusV2TsFileInsertionEventHandler.java
index 4e269aaa7e8..52815e645bf 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/handler/IoTConsensusV2TsFileInsertionEventHandler.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/handler/IoTConsensusV2TsFileInsertionEventHandler.java
@@ -31,6 +31,8 @@ import 
org.apache.iotdb.consensus.iotconsensusv2.thrift.TIoTConsensusV2TransferR
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.consensus.metric.IoTConsensusV2SinkMetrics;
 import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent;
+import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
+import org.apache.iotdb.db.pipe.resource.memory.PipeTsFileMemoryBlock;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.IoTConsensusV2AsyncSink;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.payload.request.IoTConsensusV2TsFilePieceReq;
 import 
org.apache.iotdb.db.pipe.sink.protocol.iotconsensusv2.payload.request.IoTConsensusV2TsFilePieceWithModReq;
@@ -70,7 +72,8 @@ public class IoTConsensusV2TsFileInsertionEventHandler
   private final boolean transferMod;
 
   private final int readFileBufferSize;
-  private final byte[] readBuffer;
+  private PipeTsFileMemoryBlock memoryBlock;
+  private byte[] readBuffer;
   private long position;
 
   private RandomAccessFile reader;
@@ -106,8 +109,15 @@ public class IoTConsensusV2TsFileInsertionEventHandler
     transferMod = event.isWithMod();
     currentFile = transferMod ? modFile : tsFile;
 
-    readFileBufferSize = 
PipeConfig.getInstance().getPipeSinkReadFileBufferSize();
-    readBuffer = new byte[readFileBufferSize];
+    final long maxFileLength =
+        transferMod && Objects.nonNull(modFile)
+            ? Math.max(tsFile.length(), modFile.length())
+            : tsFile.length();
+    readFileBufferSize =
+        (int)
+            Math.min(
+                (long) 
PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
+                Math.max(maxFileLength, 1L));
     position = 0;
 
     reader =
@@ -128,6 +138,12 @@ public class IoTConsensusV2TsFileInsertionEventHandler
     this.client = client;
     client.setShouldReturnSelf(false);
 
+    if (readBuffer == null) {
+      memoryBlock =
+          
PipeDataNodeResourceManager.memory().forceAllocateForTsFileWithRetry(readFileBufferSize);
+      readBuffer = new byte[readFileBufferSize];
+    }
+
     final int readLength = reader.read(readBuffer);
     if (readLength == -1) {
       if (currentFile == modFile) {
@@ -246,6 +262,8 @@ public class IoTConsensusV2TsFileInsertionEventHandler
           client.returnSelf();
         }
 
+        releaseReadBufferMemoryBlock();
+
         long duration = System.nanoTime() - createTime;
         metric.recordConnectorTsFileTransferTimer(duration);
       }
@@ -330,10 +348,20 @@ public class IoTConsensusV2TsFileInsertionEventHandler
       connector.addFailureEventToRetryQueue(event);
       metric.recordRetryCounter();
 
+      releaseReadBufferMemoryBlock();
+
       if (client != null) {
         client.setShouldReturnSelf(true);
         client.returnSelf();
       }
     }
   }
+
+  private void releaseReadBufferMemoryBlock() {
+    if (memoryBlock != null) {
+      memoryBlock.close();
+      memoryBlock = null;
+      readBuffer = null;
+    }
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/payload/builder/IoTConsensusV2TransferBatchReqBuilder.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/payload/builder/IoTConsensusV2TransferBatchReqBuilder.java
index d8fece99a3e..fc387084a00 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/payload/builder/IoTConsensusV2TransferBatchReqBuilder.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/iotconsensusv2/payload/builder/IoTConsensusV2TransferBatchReqBuilder.java
@@ -70,6 +70,7 @@ public abstract class IoTConsensusV2TransferBatchReqBuilder 
implements AutoClose
   protected long firstEventProcessingTime = Long.MIN_VALUE;
 
   // limit in buffer size
+  protected final long maxBatchSizeInBytes;
   protected final PipeMemoryBlock allocatedMemoryBlock;
   protected long totalBufferSize = 0;
 
@@ -92,36 +93,12 @@ public abstract class IoTConsensusV2TransferBatchReqBuilder 
implements AutoClose
     this.consensusGroupId = consensusGroupId;
     this.thisDataNodeId = thisDataNodeId;
 
-    final long requestMaxBatchSizeInBytes =
+    maxBatchSizeInBytes =
         parameters.getLongOrDefault(
             Arrays.asList(CONNECTOR_IOTDB_BATCH_SIZE_KEY, 
SINK_IOTDB_BATCH_SIZE_KEY),
             CONNECTOR_IOTDB_PLAIN_BATCH_SIZE_DEFAULT_VALUE);
 
-    allocatedMemoryBlock =
-        PipeDataNodeResourceManager.memory()
-            .tryAllocate(requestMaxBatchSizeInBytes)
-            .setShrinkMethod(oldMemory -> Math.max(oldMemory / 2, 0))
-            .setShrinkCallback(
-                (oldMemory, newMemory) ->
-                    LOGGER.info(
-                        
DataNodePipeMessages.THE_BATCH_SIZE_LIMIT_HAS_SHRUNK_FROM,
-                        oldMemory,
-                        newMemory))
-            .setExpandMethod(
-                oldMemory -> Math.min(Math.max(oldMemory, 1) * 2, 
requestMaxBatchSizeInBytes))
-            .setExpandCallback(
-                (oldMemory, newMemory) ->
-                    LOGGER.info(
-                        
DataNodePipeMessages.THE_BATCH_SIZE_LIMIT_HAS_EXPANDED_FROM,
-                        oldMemory,
-                        newMemory));
-
-    if (getMaxBatchSizeInBytes() != requestMaxBatchSizeInBytes) {
-      LOGGER.info(
-          
DataNodePipeMessages.IOTCONSENSUSV2TRANSFERBATCHREQBUILDER_THE_MAX_BATCH_SIZE_IS_ADJUSTED,
-          requestMaxBatchSizeInBytes,
-          getMaxBatchSizeInBytes());
-    }
+    allocatedMemoryBlock = 
PipeDataNodeResourceManager.memory().forceAllocate(0);
   }
 
   /**
@@ -136,27 +113,74 @@ public abstract class 
IoTConsensusV2TransferBatchReqBuilder implements AutoClose
       return false;
     }
 
-    final long requestCommitId = ((EnrichedEvent) 
event).getReplicateIndexForIoTV2();
+    final EnrichedEvent enrichedEvent = (EnrichedEvent) event;
+    final long requestCommitId = enrichedEvent.getReplicateIndexForIoTV2();
 
     // The deduplication logic here is to avoid the accumulation of the same 
event in a batch when
     // retrying.
     if ((events.isEmpty() || !events.get(events.size() - 1).equals(event))) {
-      events.add((EnrichedEvent) event);
-      requestCommitIds.add(requestCommitId);
-      final int bufferSize = buildTabletInsertionBuffer(event);
-
-      ((EnrichedEvent) event)
-          
.increaseReferenceCount(IoTConsensusV2TransferBatchReqBuilder.class.getName());
+      if (!enrichedEvent.increaseReferenceCount(
+          IoTConsensusV2TransferBatchReqBuilder.class.getName())) {
+        
LOGGER.warn(DataNodePipeMessages.CANNOT_INCREASE_REFERENCE_COUNT_FOR_EVENT_IGNORE,
 event);
+        return shouldEmit();
+      }
 
-      if (firstEventProcessingTime == Long.MIN_VALUE) {
-        firstEventProcessingTime = System.currentTimeMillis();
+      final int previousEventsSize = events.size();
+      final int previousRequestCommitIdsSize = requestCommitIds.size();
+      final int previousBatchReqsSize = batchReqs.size();
+      try {
+        events.add(enrichedEvent);
+        requestCommitIds.add(requestCommitId);
+        final int bufferSize = buildTabletInsertionBuffer(event);
+        increaseTotalBufferSizeAndUpdateMemoryBlock(bufferSize);
+
+        if (firstEventProcessingTime == Long.MIN_VALUE) {
+          firstEventProcessingTime = System.currentTimeMillis();
+        }
+      } catch (final Exception e) {
+        rollbackTo(previousEventsSize, previousRequestCommitIdsSize, 
previousBatchReqsSize);
+        if (events.isEmpty()) {
+          resetMemoryUsage();
+        }
+        enrichedEvent.decreaseReferenceCount(
+            IoTConsensusV2TransferBatchReqBuilder.class.getName(), false);
+        throw e;
       }
+    }
+
+    return shouldEmit();
+  }
+
+  private boolean shouldEmit() {
+    return !events.isEmpty()
+        && (totalBufferSize >= getMaxBatchSizeInBytes()
+            || System.currentTimeMillis() - firstEventProcessingTime >= 
maxDelayInMs);
+  }
 
-      totalBufferSize += bufferSize;
+  private void increaseTotalBufferSizeAndUpdateMemoryBlock(final long 
bufferSize) {
+    if (bufferSize <= 0) {
+      return;
     }
 
-    return totalBufferSize >= getMaxBatchSizeInBytes()
-        || System.currentTimeMillis() - firstEventProcessingTime >= 
maxDelayInMs;
+    final long newTotalBufferSize =
+        Math.min(totalBufferSize + bufferSize, getMaxBatchSizeInBytes());
+    PipeDataNodeResourceManager.memory().forceResize(allocatedMemoryBlock, 
newTotalBufferSize);
+    totalBufferSize = newTotalBufferSize;
+  }
+
+  private void rollbackTo(
+      final int previousEventsSize,
+      final int previousRequestCommitIdsSize,
+      final int previousBatchReqsSize) {
+    events.subList(previousEventsSize, events.size()).clear();
+    requestCommitIds.subList(previousRequestCommitIdsSize, 
requestCommitIds.size()).clear();
+    batchReqs.subList(previousBatchReqsSize, batchReqs.size()).clear();
+  }
+
+  private void resetMemoryUsage() {
+    firstEventProcessingTime = Long.MIN_VALUE;
+    totalBufferSize = 0;
+    PipeDataNodeResourceManager.memory().forceResize(allocatedMemoryBlock, 0);
   }
 
   public synchronized void onSuccess() {
@@ -165,9 +189,7 @@ public abstract class IoTConsensusV2TransferBatchReqBuilder 
implements AutoClose
     events.clear();
     requestCommitIds.clear();
 
-    firstEventProcessingTime = Long.MIN_VALUE;
-
-    totalBufferSize = 0;
+    resetMemoryUsage();
   }
 
   public IoTConsensusV2TabletBatchReq toTIoTConsensusV2BatchTransferReq() 
throws IOException {
@@ -175,7 +197,7 @@ public abstract class IoTConsensusV2TransferBatchReqBuilder 
implements AutoClose
   }
 
   protected long getMaxBatchSizeInBytes() {
-    return allocatedMemoryBlock.getMemoryUsageInBytes();
+    return maxBatchSizeInBytes;
   }
 
   public boolean isEmpty() {
@@ -219,6 +241,9 @@ public abstract class IoTConsensusV2TransferBatchReqBuilder 
implements AutoClose
         ((EnrichedEvent) event).clearReferenceCount(this.getClass().getName());
       }
     }
+    batchReqs.clear();
+    events.clear();
+    requestCommitIds.clear();
     allocatedMemoryBlock.close();
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/legacy/IoTDBLegacyPipeSink.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/legacy/IoTDBLegacyPipeSink.java
index 5f850bc311f..bf37e02c8d5 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/legacy/IoTDBLegacyPipeSink.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/legacy/IoTDBLegacyPipeSink.java
@@ -37,6 +37,8 @@ import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeInsertNodeTabletInsertio
 import 
org.apache.iotdb.db.pipe.event.common.tablet.PipeRawTabletInsertionEvent;
 import org.apache.iotdb.db.pipe.event.common.terminate.PipeTerminateEvent;
 import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent;
+import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
+import org.apache.iotdb.db.pipe.resource.memory.PipeTsFileMemoryBlock;
 import org.apache.iotdb.db.pipe.sink.payload.legacy.TsFilePipeData;
 import org.apache.iotdb.db.storageengine.StorageEngine;
 import org.apache.iotdb.db.storageengine.dataregion.DataRegion;
@@ -421,8 +423,12 @@ public class IoTDBLegacyPipeSink implements PipeConnector {
     long position = 0;
 
     // Try small piece to rebase the file position.
-    final byte[] buffer = new 
byte[PipeConfig.getInstance().getPipeSinkReadFileBufferSize()];
-    try (final RandomAccessFile randomAccessFile = new RandomAccessFile(file, 
"r")) {
+    final int readFileBufferSize = getReadFileBufferSize(file);
+    try (final PipeTsFileMemoryBlock ignored =
+            PipeDataNodeResourceManager.memory()
+                .forceAllocateForTsFileWithRetry(readFileBufferSize);
+        final RandomAccessFile randomAccessFile = new RandomAccessFile(file, 
"r")) {
+      final byte[] buffer = new byte[readFileBufferSize];
       while (true) {
         final int dataLength = randomAccessFile.read(buffer);
         if (dataLength == -1) {
@@ -463,6 +469,13 @@ public class IoTDBLegacyPipeSink implements PipeConnector {
     }
   }
 
+  private int getReadFileBufferSize(final File file) {
+    return (int)
+        Math.min(
+            (long) PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
+            Math.max(file.length(), 1L));
+  }
+
   @Override
   public void close() throws Exception {
     if (client != null) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/handler/PipeTransferTsFileHandler.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/handler/PipeTransferTsFileHandler.java
index a0ee47f09e1..700f241eb1f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/handler/PipeTransferTsFileHandler.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/async/handler/PipeTransferTsFileHandler.java
@@ -124,11 +124,15 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
     // the memory of the TsFile event is not released, so the memory is not 
enough for slicing. This
     // will cause a deadlock.
     waitForResourceEnough4Slicing((long) ((1 + Math.random()) * 20 * 1000)); 
// 20 - 40 seconds
+    final long maxFileLength =
+        transferMod && Objects.nonNull(modFile)
+            ? Math.max(tsFile.length(), modFile.length())
+            : tsFile.length();
     readFileBufferSize =
         (int)
             Math.min(
-                PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
-                transferMod ? Math.max(tsFile.length(), modFile.length()) : 
tsFile.length());
+                (long) 
PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
+                Math.max(maxFileLength, 1L));
     position = 0;
 
     isSealSignalSent = new AtomicBoolean(false);
@@ -142,21 +146,6 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
       final IoTDBDataNodeAsyncClientManager clientManager,
       final AsyncPipeDataTransferServiceClient client)
       throws TException, IOException {
-    // Delay creation of resources to avoid OOM or too many open files
-    if (readBuffer == null) {
-      memoryBlock =
-          PipeDataNodeResourceManager.memory()
-              .forceAllocateForTsFileWithRetry(
-                  
PipeConfig.getInstance().isPipeSinkReadFileBufferMemoryControlEnabled()
-                      ? readFileBufferSize
-                      : 0);
-      readBuffer = new byte[readFileBufferSize];
-    }
-
-    if (reader == null) {
-      reader = transferMod ? new RandomAccessFile(modFile, "r") : new 
RandomAccessFile(tsFile, "r");
-    }
-
     this.clientManager = clientManager;
     this.client = client;
 
@@ -173,6 +162,17 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
       return;
     }
 
+    // Delay creation of resources to avoid OOM or too many open files
+    if (readBuffer == null) {
+      memoryBlock =
+          
PipeDataNodeResourceManager.memory().forceAllocateForTsFileWithRetry(readFileBufferSize);
+      readBuffer = new byte[readFileBufferSize];
+    }
+
+    if (reader == null) {
+      reader = transferMod ? new RandomAccessFile(modFile, "r") : new 
RandomAccessFile(tsFile, "r");
+    }
+
     client.setShouldReturnSelf(false);
     client.setTimeoutDynamically(clientManager.getConnectionTimeout());
 
@@ -256,6 +256,7 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
       super.onComplete(response);
     } finally {
       if (sink.isClosed()) {
+        releaseReadBufferMemoryBlock();
         returnClientIfNecessary();
       }
     }
@@ -319,6 +320,7 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
               referenceCount);
         }
 
+        releaseReadBufferMemoryBlock();
         returnClientIfNecessary();
       }
 
@@ -361,6 +363,7 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
     try {
       super.onError(exception);
     } finally {
+      releaseReadBufferMemoryBlock();
       returnClientIfNecessary();
     }
   }
@@ -412,6 +415,7 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
       LOGGER.warn(DataNodePipeMessages.FAILED_TO_CLOSE_FILE_READER_OR_DELETE, 
e);
     } finally {
       try {
+        releaseReadBufferMemoryBlock();
         returnClientIfNecessary();
       } finally {
         if (eventsHadBeenAddedToRetryQueue.compareAndSet(false, true)) {
@@ -473,10 +477,14 @@ public class PipeTransferTsFileHandler extends 
PipeTransferTrackableHandler {
   @Override
   public void close() {
     super.close();
+    releaseReadBufferMemoryBlock();
+  }
 
+  private void releaseReadBufferMemoryBlock() {
     if (memoryBlock != null) {
       memoryBlock.close();
       memoryBlock = null;
+      readBuffer = null;
     }
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/sync/IoTDBDataRegionSyncSink.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/sync/IoTDBDataRegionSyncSink.java
index 72703aa57f2..d13b9664f8a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/sync/IoTDBDataRegionSyncSink.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/thrift/sync/IoTDBDataRegionSyncSink.java
@@ -22,10 +22,12 @@ package org.apache.iotdb.db.pipe.sink.protocol.thrift.sync;
 import org.apache.iotdb.common.rpc.thrift.TEndPoint;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.pipe.agent.task.progress.CommitterKey;
+import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.event.EnrichedEvent;
 import org.apache.iotdb.commons.pipe.sink.client.IoTDBSyncClient;
 import org.apache.iotdb.commons.pipe.sink.limiter.TsFileSendRateLimiter;
 import 
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferFilePieceReq;
+import 
org.apache.iotdb.commons.pipe.sink.payload.thrift.response.PipeTransferFilePieceResp;
 import org.apache.iotdb.commons.utils.RetryUtils;
 import org.apache.iotdb.db.i18n.DataNodePipeMessages;
 import org.apache.iotdb.db.pipe.event.common.deletion.PipeDeleteDataNodeEvent;
@@ -36,6 +38,8 @@ import 
org.apache.iotdb.db.pipe.event.common.terminate.PipeTerminateEvent;
 import org.apache.iotdb.db.pipe.event.common.tsfile.PipeTsFileInsertionEvent;
 import org.apache.iotdb.db.pipe.metric.overview.PipeResourceMetrics;
 import org.apache.iotdb.db.pipe.metric.sink.PipeDataRegionSinkMetrics;
+import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
+import org.apache.iotdb.db.pipe.resource.memory.PipeTsFileMemoryBlock;
 import org.apache.iotdb.db.pipe.sink.client.IoTDBDataNodeSyncClientManager;
 import 
org.apache.iotdb.db.pipe.sink.payload.evolvable.batch.PipeTabletEventBatch;
 import 
org.apache.iotdb.db.pipe.sink.payload.evolvable.batch.PipeTabletEventPlainBatch;
@@ -71,6 +75,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.file.NoSuchFileException;
 import java.util.Arrays;
 import java.util.Collections;
@@ -610,6 +615,137 @@ public class IoTDBDataRegionSyncSink extends 
IoTDBDataNodeSyncSink {
     LOGGER.info(DataNodePipeMessages.SUCCESSFULLY_TRANSFERRED_FILE, tsFile);
   }
 
+  @Override
+  protected void transferFilePieces(
+      final Map<Pair<String, Long>, Double> pipe2WeightMap,
+      final File file,
+      final Pair<IoTDBSyncClient, Boolean> clientAndStatus,
+      final boolean isMultiFile)
+      throws PipeException, IOException {
+    final int readFileBufferSize = getReadFileBufferSize(file);
+    try (final PipeTsFileMemoryBlock ignored =
+            PipeDataNodeResourceManager.memory()
+                .forceAllocateForTsFileWithRetry(readFileBufferSize);
+        final RandomAccessFile reader = new RandomAccessFile(file, "r")) {
+      final byte[] readBuffer = new byte[readFileBufferSize];
+      long position = 0;
+      int readLength;
+      while ((readLength = readNextFilePiece(reader, readBuffer, 
readFileBufferSize)) != -1) {
+        position =
+            transferFilePiece(
+                pipe2WeightMap,
+                file,
+                clientAndStatus,
+                isMultiFile,
+                readBuffer,
+                position,
+                readLength,
+                reader);
+      }
+    }
+  }
+
+  private int readNextFilePiece(
+      final RandomAccessFile reader, final byte[] readBuffer, final int 
readFileBufferSize)
+      throws IOException {
+    mayLimitRateAndRecordIO(readFileBufferSize);
+    return reader.read(readBuffer);
+  }
+
+  private long transferFilePiece(
+      final Map<Pair<String, Long>, Double> pipe2WeightMap,
+      final File file,
+      final Pair<IoTDBSyncClient, Boolean> clientAndStatus,
+      final boolean isMultiFile,
+      final byte[] readBuffer,
+      final long position,
+      final int readLength,
+      final RandomAccessFile reader)
+      throws PipeException, IOException {
+    final byte[] payLoad = buildFilePiecePayload(readBuffer, readLength);
+    final PipeTransferFilePieceResp resp =
+        doTransferFilePiece(pipe2WeightMap, file, clientAndStatus, 
isMultiFile, payLoad, position);
+    return handleTransferFilePieceResp(file, clientAndStatus, reader, position 
+ readLength, resp);
+  }
+
+  private byte[] buildFilePiecePayload(final byte[] readBuffer, final int 
readLength) {
+    return readLength == readBuffer.length
+        ? readBuffer
+        : Arrays.copyOfRange(readBuffer, 0, readLength);
+  }
+
+  private PipeTransferFilePieceResp doTransferFilePiece(
+      final Map<Pair<String, Long>, Double> pipe2WeightMap,
+      final File file,
+      final Pair<IoTDBSyncClient, Boolean> clientAndStatus,
+      final boolean isMultiFile,
+      final byte[] payLoad,
+      final long position)
+      throws PipeException, IOException {
+    try {
+      final TPipeTransferReq req =
+          compressIfNeeded(
+              isMultiFile
+                  ? getTransferMultiFilePieceReq(file.getName(), position, 
payLoad)
+                  : getTransferSingleFilePieceReq(file.getName(), position, 
payLoad));
+      pipe2WeightMap.forEach(
+          (namePair, weight) ->
+              rateLimitIfNeeded(
+                  namePair.getLeft(),
+                  namePair.getRight(),
+                  clientAndStatus.getLeft().getEndPoint(),
+                  (long) (req.getBody().length * weight)));
+      return PipeTransferFilePieceResp.fromTPipeTransferResp(
+          clientAndStatus.getLeft().pipeTransfer(req));
+    } catch (final Exception e) {
+      clientAndStatus.setRight(false);
+      throw new PipeConnectionException(
+          String.format(
+              DataNodePipeMessages
+                  
.PIPE_EXCEPTION_NETWORK_ERROR_WHEN_TRANSFER_FILE_S_BECAUSE_S_3C673B7A,
+              file,
+              e.getMessage()),
+          e);
+    }
+  }
+
+  private long handleTransferFilePieceResp(
+      final File file,
+      final Pair<IoTDBSyncClient, Boolean> clientAndStatus,
+      final RandomAccessFile reader,
+      final long nextPosition,
+      final PipeTransferFilePieceResp resp)
+      throws PipeException, IOException {
+    final TSStatus status = resp.getStatus();
+    if (status.getCode() == 
TSStatusCode.PIPE_TRANSFER_FILE_OFFSET_RESET.getStatusCode()) {
+      reader.seek(resp.getEndWritingOffset());
+      LOGGER.info(DataNodePipeMessages.REDIRECT_FILE_POSITION_TO, 
resp.getEndWritingOffset());
+      return resp.getEndWritingOffset();
+    }
+
+    if (status.getCode() == 
TSStatusCode.PIPE_CONFIG_RECEIVER_HANDSHAKE_NEEDED.getStatusCode()) {
+      getClientManager().sendHandshakeReq(clientAndStatus);
+    }
+
+    if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()
+        && status.getCode() != 
TSStatusCode.REDIRECTION_RECOMMEND.getStatusCode()) {
+      receiverStatusHandler.handle(
+          resp.getStatus(),
+          String.format(
+              
DataNodePipeMessages.MESSAGE_TRANSFER_FILE_ARG_ERROR_RESULT_STATUS_ARG_E565D9FD,
+              file,
+              resp.getStatus()),
+          file.getName());
+    }
+    return nextPosition;
+  }
+
+  private int getReadFileBufferSize(final File file) {
+    return (int)
+        Math.min(
+            PipeConfig.getInstance().getPipeSinkReadFileBufferSize(), 
Math.max(file.length(), 1L));
+  }
+
   @Override
   public TPipeTransferReq compressIfNeeded(final TPipeTransferReq req) throws 
IOException {
     if (Objects.isNull(compressionTimer) && Objects.nonNull(sinkTaskId)) {
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java
index 0582a94170d..d3249933aa1 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java
@@ -23,19 +23,13 @@ import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta;
 import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta;
-import org.apache.iotdb.commons.pipe.config.PipeConfig;
-import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant;
-import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant;
 import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
-import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters;
 import org.apache.iotdb.pipe.api.exception.PipeException;
 
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.lang.reflect.Method;
 import java.util.HashMap;
-import java.util.Map;
 
 public class PipeDataNodeTaskAgentTest {
 
@@ -74,86 +68,4 @@ public class PipeDataNodeTaskAgentTest {
           
.setPipeTotalFloatingMemoryProportion(originalPipeTotalFloatingMemoryProportion);
     }
   }
-
-  @Test
-  public void testPlainBatchMemoryIncludesLeaderCacheEndpointShards() throws 
Exception {
-    final Map<String, String> sinkAttributes = new HashMap<>();
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.CONNECTOR_FORMAT_TABLET_VALUE);
-    sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_BATCH_SIZE_KEY, 
"1024");
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_IOTDB_NODE_URLS_KEY, "127.0.0.1:6667, 
127.0.0.2:6667");
-    sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_IP_KEY, "127.0.0.3");
-    sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_PORT_KEY, "6667");
-
-    Assert.assertEquals(
-        4 * 1024L, invokeCalculateSinkBatchMemory(new 
PipeParameters(sinkAttributes)));
-
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_LEADER_CACHE_ENABLE_KEY, 
Boolean.FALSE.toString());
-    Assert.assertEquals(1024L, invokeCalculateSinkBatchMemory(new 
PipeParameters(sinkAttributes)));
-  }
-
-  @Test
-  public void testTsFileBatchMemoryIgnoresLeaderCacheEndpointShards() throws 
Exception {
-    final Map<String, String> sinkAttributes = new HashMap<>();
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.CONNECTOR_FORMAT_TS_FILE_VALUE);
-    sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_BATCH_SIZE_KEY, 
"2048");
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_IOTDB_NODE_URLS_KEY, 
"127.0.0.1:6667,127.0.0.2:6667");
-
-    Assert.assertEquals(2048L, invokeCalculateSinkBatchMemory(new 
PipeParameters(sinkAttributes)));
-  }
-
-  @Test
-  public void testPlainBatchMemoryReturnsZeroWhenBatchModeIsDisabled() throws 
Exception {
-    final Map<String, String> sinkAttributes = new HashMap<>();
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.CONNECTOR_FORMAT_TABLET_VALUE);
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_IOTDB_BATCH_MODE_ENABLE_KEY, 
Boolean.FALSE.toString());
-    sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_BATCH_SIZE_KEY, 
"1024");
-
-    Assert.assertEquals(0L, invokeCalculateSinkBatchMemory(new 
PipeParameters(sinkAttributes)));
-  }
-
-  @Test
-  public void testSendTsFileReadBufferMemoryUsesSinkReadFileBufferSize() 
throws Exception {
-    final Map<String, String> sourceAttributes = new HashMap<>();
-    sourceAttributes.put(PipeSourceConstant.EXTRACTOR_HISTORY_ENABLE_KEY, 
Boolean.FALSE.toString());
-
-    final Map<String, String> sinkAttributes = new HashMap<>();
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.CONNECTOR_FORMAT_TABLET_VALUE);
-    Assert.assertEquals(
-        0L,
-        invokeCalculateSendTsFileReadBufferMemory(
-            new PipeParameters(sourceAttributes), new 
PipeParameters(sinkAttributes)));
-
-    sinkAttributes.put(
-        PipeSinkConstant.CONNECTOR_FORMAT_KEY, 
PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE);
-    Assert.assertEquals(
-        PipeConfig.getInstance().getPipeSinkReadFileBufferSize(),
-        invokeCalculateSendTsFileReadBufferMemory(
-            new PipeParameters(sourceAttributes), new 
PipeParameters(sinkAttributes)));
-  }
-
-  private long invokeCalculateSinkBatchMemory(final PipeParameters 
sinkParameters)
-      throws Exception {
-    final Method method =
-        PipeDataNodeTaskAgent.class.getDeclaredMethod(
-            "calculateSinkBatchMemory", PipeParameters.class);
-    method.setAccessible(true);
-    return (long) method.invoke(null, sinkParameters);
-  }
-
-  private long invokeCalculateSendTsFileReadBufferMemory(
-      final PipeParameters sourceParameters, final PipeParameters 
sinkParameters) throws Exception {
-    final Method method =
-        PipeDataNodeTaskAgent.class.getDeclaredMethod(
-            "calculateSendTsFileReadBufferMemory", PipeParameters.class, 
PipeParameters.class);
-    method.setAccessible(true);
-    return (long) method.invoke(null, sourceParameters, sinkParameters);
-  }
 }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/TsFileInsertionEventParserTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/TsFileInsertionEventParserTest.java
index 91b6d7d56c5..3d81e143ac8 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/TsFileInsertionEventParserTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/event/TsFileInsertionEventParserTest.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.pipe.event;
 
 import org.apache.iotdb.commons.conf.CommonDescriptor;
+import 
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.pipe.config.PipeConfig;
 import org.apache.iotdb.commons.pipe.datastructure.pattern.IoTDBTreePattern;
@@ -155,6 +156,87 @@ public class TsFileInsertionEventParserTest {
     System.out.println(System.currentTimeMillis() - startTime);
   }
 
+  @Test
+  public void testScanParserReleasesTabletMemoryAfterRawTabletGenerated() 
throws Exception {
+    nonalignedTsFile =
+        TsFileGeneratorUtils.generateNonAlignedTsFile(
+            "nonaligned-release-tablet-memory.tsfile", 1, 1, 10, 0, 100, 10, 
10);
+
+    try (final TsFileInsertionEventScanParser parser =
+        new TsFileInsertionEventScanParser(
+            nonalignedTsFile,
+            new PrefixTreePattern("root"),
+            Long.MIN_VALUE,
+            Long.MAX_VALUE,
+            null,
+            null,
+            false)) {
+      final Iterator<TabletInsertionEvent> iterator = 
parser.toTabletInsertionEvents().iterator();
+
+      Assert.assertTrue(iterator.hasNext());
+      final TabletInsertionEvent event = iterator.next();
+      Assert.assertTrue(event instanceof PipeRawTabletInsertionEvent);
+      Assert.assertEquals(0, 
getAllocatedTabletMemory(parser).getMemoryUsageInBytes());
+
+      ((PipeRawTabletInsertionEvent) 
event).clearReferenceCount(getClass().getName());
+    }
+  }
+
+  @Test
+  public void 
testConsumeTabletInsertionEventsWithRetryReleasesParserOnOutOfMemory()
+      throws Exception {
+    nonalignedTsFile =
+        TsFileGeneratorUtils.generateNonAlignedTsFile(
+            "nonaligned-consume-oom.tsfile", 1, 1, 10, 0, 100, 10, 10);
+    resource = new TsFileResource(nonalignedTsFile);
+    resource.setStatusForTest(TsFileResourceStatus.NORMAL);
+
+    // The TsFile generator only creates the file, so mark the resource 
non-empty explicitly.
+    final IDeviceID deviceID = 
IDeviceID.Factory.DEFAULT_FACTORY.create("root.testsg.d0");
+    resource.updateStartTime(deviceID, 0);
+    resource.updateEndTime(deviceID, 9);
+
+    final PipeTsFileInsertionEvent event =
+        new PipeTsFileInsertionEvent(
+            false,
+            "root",
+            resource,
+            null,
+            false,
+            false,
+            false,
+            null,
+            null,
+            0,
+            null,
+            new PrefixTreePattern("root"),
+            null,
+            null,
+            null,
+            null,
+            true,
+            Long.MIN_VALUE,
+            Long.MAX_VALUE);
+    final AtomicReference<PipeRawTabletInsertionEvent> parsedEventReference =
+        new AtomicReference<>();
+
+    final PipeRuntimeOutOfMemoryCriticalException exception =
+        Assert.assertThrows(
+            PipeRuntimeOutOfMemoryCriticalException.class,
+            () ->
+                event.consumeTabletInsertionEventsWithRetry(
+                    parsedEvent -> {
+                      parsedEventReference.set(parsedEvent);
+                      throw new 
PipeRuntimeOutOfMemoryCriticalException("expected oom");
+                    },
+                    "test"));
+
+    Assert.assertEquals("expected oom", exception.getMessage());
+    Assert.assertNotNull(parsedEventReference.get());
+    Assert.assertTrue(parsedEventReference.get().isReleased());
+    Assert.assertNull(getEventParser(event).get());
+  }
+
   @Test
   public void 
testScanParserSplitNonAlignedSinglePageChunkByEstimatedPageMemory() throws 
Exception {
     final long originalPipeMaxReaderChunkSize =
@@ -1995,6 +2077,22 @@ public class TsFileInsertionEventParserTest {
     return (PipeMemoryBlock) field.get(parser);
   }
 
+  private PipeMemoryBlock getAllocatedTabletMemory(final 
TsFileInsertionEventParser parser)
+      throws NoSuchFieldException, IllegalAccessException {
+    final Field field =
+        
TsFileInsertionEventParser.class.getDeclaredField("allocatedMemoryBlockForTablet");
+    field.setAccessible(true);
+    return (PipeMemoryBlock) field.get(parser);
+  }
+
+  @SuppressWarnings("unchecked")
+  private AtomicReference<TsFileInsertionEventParser> getEventParser(
+      final PipeTsFileInsertionEvent event) throws NoSuchFieldException, 
IllegalAccessException {
+    final Field field = 
PipeTsFileInsertionEvent.class.getDeclaredField("eventParser");
+    field.setAccessible(true);
+    return (AtomicReference<TsFileInsertionEventParser>) field.get(event);
+  }
+
   private long calculatePipeMaxReaderChunkSizeForSinglePageAlignedChunk(final 
File tsFile)
       throws Exception {
     try (final TsFileSequenceReader reader = new 
TsFileSequenceReader(tsFile.getAbsolutePath())) {

Reply via email to