This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new db233b76cf0 Pipe: manage ConfigNode receiver temporary memory (#18144)
(#18170)
db233b76cf0 is described below
commit db233b76cf0d12b55be2eeaffafc989162e14ab2
Author: Caideyipi <[email protected]>
AuthorDate: Fri Jul 10 11:18:08 2026 +0800
Pipe: manage ConfigNode receiver temporary memory (#18144) (#18170)
(cherry picked from commit 33a38b6194de81658c3979aaffb2ca3b83d07858)
---
.../agent/runtime/PipeConfigNodeRuntimeAgent.java | 2 +-
.../receiver/protocol/IoTDBConfigNodeReceiver.java | 39 +++++--
.../resource/PipeConfigNodeResourceManager.java | 19 +---
.../memory/PipeConfigNodeMemoryManager.java | 115 +++++++++++++++++++++
.../conf/iotdb-system.properties.template | 3 +-
5 files changed, 156 insertions(+), 22 deletions(-)
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java
index 10c710a64ad..900ccc8c10b 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java
@@ -97,7 +97,7 @@ public class PipeConfigNodeRuntimeAgent implements IService {
private void initPipePeriodicalLogReducer() {
PipePeriodicalLogReducer.setMemoryResizeFunction(
- PipeConfigNodeResourceManager::resizeLogReducerMemory);
+ PipeConfigNodeResourceManager.memory()::resizeLogReducerMemory);
PipeLogger.setLogger(PipePeriodicalLogReducer::log);
}
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
index 64f4a26350c..10999474e9e 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.auth.entity.PrivilegeType;
import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.conf.IoTDBConstant;
+import
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.commons.pipe.datastructure.pattern.IoTDBPipePattern;
@@ -57,6 +58,7 @@ import
org.apache.iotdb.confignode.manager.pipe.event.PipeConfigRegionSnapshotEv
import
org.apache.iotdb.confignode.manager.pipe.metric.receiver.PipeConfigNodeReceiverMetrics;
import
org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanExceptionVisitor;
import
org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanTSStatusVisitor;
+import
org.apache.iotdb.confignode.manager.pipe.resource.PipeConfigNodeResourceManager;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigNodeHandshakeV2Req;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigPlanReq;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigSnapshotPieceReq;
@@ -147,14 +149,21 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
.recordTransferConfigPlanTimer(System.nanoTime() - startTime);
return resp;
case TRANSFER_CONFIG_SNAPSHOT_PIECE:
- resp =
- handleTransferFilePiece(
+ try {
+ try (final AutoCloseable ignored =
+ PipeConfigNodeResourceManager.memory()
+
.tryAllocateReceiverMemory(getRequestBodySizeInBytes(req))) {
+ return handleTransferFilePiece(
PipeTransferConfigSnapshotPieceReq.fromTPipeTransferReq(req),
req instanceof AirGapPseudoTPipeTransferRequest,
false);
- PipeConfigNodeReceiverMetrics.getInstance()
- .recordTransferConfigSnapshotPieceTimer(System.nanoTime() -
startTime);
- return resp;
+ } catch (final PipeRuntimeOutOfMemoryCriticalException e) {
+ return getReceiverTemporaryUnavailableResp(e);
+ }
+ } finally {
+ PipeConfigNodeReceiverMetrics.getInstance()
+ .recordTransferConfigSnapshotPieceTimer(System.nanoTime() -
startTime);
+ }
case TRANSFER_CONFIG_SNAPSHOT_SEAL:
resp =
handleTransferFileSealV2(
@@ -163,7 +172,14 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
.recordTransferConfigSnapshotSealTimer(System.nanoTime() -
startTime);
return resp;
case TRANSFER_COMPRESSED:
- return
receive(PipeTransferCompressedReq.fromTPipeTransferReq(req));
+ try (final AutoCloseable ignored =
+ PipeConfigNodeResourceManager.memory()
+ .tryAllocateReceiverMemory(
+
PipeTransferCompressedReq.getMaxAdditionalDecompressedLengthInBytes(req))) {
+ return
receive(PipeTransferCompressedReq.fromTPipeTransferReq(req));
+ } catch (final PipeRuntimeOutOfMemoryCriticalException e) {
+ return getReceiverTemporaryUnavailableResp(e);
+ }
default:
break;
}
@@ -216,6 +232,17 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
return new TPipeTransferResp(getNotLoggedInStatus());
}
+ private static long getRequestBodySizeInBytes(final TPipeTransferReq req) {
+ return req.getBody() == null ? 0 : req.getBody().length;
+ }
+
+ private static TPipeTransferResp getReceiverTemporaryUnavailableResp(
+ final PipeRuntimeOutOfMemoryCriticalException e) {
+ return new TPipeTransferResp(
+ new
TSStatus(TSStatusCode.PIPE_RECEIVER_TEMPORARY_UNAVAILABLE_EXCEPTION.getStatusCode())
+ .setMessage(e.getMessage()));
+ }
+
private static boolean requiresAuthentication(final PipeRequestType type) {
switch (type) {
case TRANSFER_CONFIG_PLAN:
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/PipeConfigNodeResourceManager.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/PipeConfigNodeResourceManager.java
index 635e6b7b1f8..60fc2a9e6a0 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/PipeConfigNodeResourceManager.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/PipeConfigNodeResourceManager.java
@@ -22,16 +22,14 @@ package org.apache.iotdb.confignode.manager.pipe.resource;
import org.apache.iotdb.commons.pipe.resource.log.PipeLogManager;
import org.apache.iotdb.commons.pipe.resource.ref.PipePhantomReferenceManager;
import
org.apache.iotdb.commons.pipe.resource.snapshot.PipeSnapshotResourceManager;
-import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
+import
org.apache.iotdb.confignode.manager.pipe.resource.memory.PipeConfigNodeMemoryManager;
import
org.apache.iotdb.confignode.manager.pipe.resource.ref.PipeConfigNodePhantomReferenceManager;
import
org.apache.iotdb.confignode.manager.pipe.resource.snapshot.PipeConfigNodeSnapshotResourceManager;
-import java.util.concurrent.atomic.AtomicLong;
-
public class PipeConfigNodeResourceManager {
private final PipeSnapshotResourceManager pipeSnapshotResourceManager;
- private final AtomicLong pipeLogReducerMemoryUsageInBytes = new
AtomicLong(0);
+ private final PipeConfigNodeMemoryManager pipeMemoryManager;
private final PipeLogManager pipeLogManager;
private final PipePhantomReferenceManager pipePhantomReferenceManager;
@@ -40,8 +38,8 @@ public class PipeConfigNodeResourceManager {
.pipeSnapshotResourceManager;
}
- public static long resizeLogReducerMemory(final long targetSizeInBytes) {
- return
PipeResourceManagerHolder.INSTANCE.resizePipeLogReducerMemory(targetSizeInBytes);
+ public static PipeConfigNodeMemoryManager memory() {
+ return
PipeConfigNodeResourceManager.PipeResourceManagerHolder.INSTANCE.pipeMemoryManager;
}
public static PipeLogManager log() {
@@ -54,16 +52,9 @@ public class PipeConfigNodeResourceManager {
///////////////////////////// SINGLETON /////////////////////////////
- private long resizePipeLogReducerMemory(final long targetSizeInBytes) {
- final long pipeMemorySizeInBytes =
-
ConfigNodeDescriptor.getInstance().getMemoryConfig().getPipeMemorySizeInBytes();
- final long resizedSizeInBytes = Math.min(Math.max(0, targetSizeInBytes),
pipeMemorySizeInBytes);
- pipeLogReducerMemoryUsageInBytes.set(resizedSizeInBytes);
- return pipeLogReducerMemoryUsageInBytes.get();
- }
-
private PipeConfigNodeResourceManager() {
pipeSnapshotResourceManager = new PipeConfigNodeSnapshotResourceManager();
+ pipeMemoryManager = new PipeConfigNodeMemoryManager();
pipeLogManager = new PipeLogManager();
pipePhantomReferenceManager = new PipeConfigNodePhantomReferenceManager();
}
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/memory/PipeConfigNodeMemoryManager.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/memory/PipeConfigNodeMemoryManager.java
new file mode 100644
index 00000000000..471a0336710
--- /dev/null
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/resource/memory/PipeConfigNodeMemoryManager.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.confignode.manager.pipe.resource.memory;
+
+import
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
+import org.apache.iotdb.commons.pipe.config.PipeConfig;
+import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
+
+public class PipeConfigNodeMemoryManager {
+
+ private static final PipeConfig PIPE_CONFIG = PipeConfig.getInstance();
+
+ private long usedMemorySizeInBytes;
+
+ private long pipeLogReducerMemoryUsageInBytes;
+
+ public synchronized long resizeLogReducerMemory(final long
targetSizeInBytes) {
+ final long nonNegativeTargetSizeInBytes = Math.max(0, targetSizeInBytes);
+ if (pipeLogReducerMemoryUsageInBytes < nonNegativeTargetSizeInBytes) {
+ final long deltaSizeInBytes = nonNegativeTargetSizeInBytes -
pipeLogReducerMemoryUsageInBytes;
+ if (tryAllocate(deltaSizeInBytes)) {
+ pipeLogReducerMemoryUsageInBytes = nonNegativeTargetSizeInBytes;
+ }
+ } else if (pipeLogReducerMemoryUsageInBytes >
nonNegativeTargetSizeInBytes) {
+ final long deltaSizeInBytes = pipeLogReducerMemoryUsageInBytes -
nonNegativeTargetSizeInBytes;
+ releaseMemory(deltaSizeInBytes);
+ pipeLogReducerMemoryUsageInBytes = nonNegativeTargetSizeInBytes;
+ }
+ return pipeLogReducerMemoryUsageInBytes;
+ }
+
+ public AutoCloseable tryAllocateReceiverMemory(final long
requestedMemorySizeInBytes)
+ throws PipeRuntimeOutOfMemoryCriticalException {
+ final long nonNegativeRequestedMemorySizeInBytes = Math.max(0,
requestedMemorySizeInBytes);
+ if (nonNegativeRequestedMemorySizeInBytes == 0) {
+ return () -> {};
+ }
+
+ forceAllocateWithRetry(nonNegativeRequestedMemorySizeInBytes);
+ return () -> releaseMemory(nonNegativeRequestedMemorySizeInBytes);
+ }
+
+ private synchronized void forceAllocateWithRetry(final long sizeInBytes)
+ throws PipeRuntimeOutOfMemoryCriticalException {
+ final int memoryAllocateMaxRetries =
PIPE_CONFIG.getPipeMemoryAllocateMaxRetries();
+ for (int i = 0; i < memoryAllocateMaxRetries; ++i) {
+ if (tryAllocate(sizeInBytes)) {
+ return;
+ }
+
+ try {
+ final long retryIntervalInMs =
PIPE_CONFIG.getPipeMemoryAllocateRetryIntervalInMs();
+ if (retryIntervalInMs > 0) {
+ wait(retryIntervalInMs);
+ }
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new PipeRuntimeOutOfMemoryCriticalException("Temporarily out of
memory.");
+ }
+ }
+
+ throw new PipeRuntimeOutOfMemoryCriticalException(
+ String.format(
+ "forceAllocate: failed to allocate memory after %d retries, "
+ + "total memory size %d bytes, used memory size %d bytes, "
+ + "requested memory size %d bytes",
+ memoryAllocateMaxRetries,
+ getTotalMemorySizeInBytes(),
+ usedMemorySizeInBytes,
+ sizeInBytes));
+ }
+
+ private boolean tryAllocate(final long sizeInBytes) {
+ if (getFreeMemorySizeInBytes() < sizeInBytes) {
+ return false;
+ }
+
+ usedMemorySizeInBytes += sizeInBytes;
+ return true;
+ }
+
+ private synchronized void releaseMemory(final long sizeInBytes) {
+ usedMemorySizeInBytes = Math.max(0, usedMemorySizeInBytes - sizeInBytes);
+ notifyAll();
+ }
+
+ public synchronized long getUsedMemorySizeInBytes() {
+ return usedMemorySizeInBytes;
+ }
+
+ public synchronized long getFreeMemorySizeInBytes() {
+ return Math.max(0, getTotalMemorySizeInBytes() - usedMemorySizeInBytes);
+ }
+
+ public long getTotalMemorySizeInBytes() {
+ return
ConfigNodeDescriptor.getInstance().getMemoryConfig().getPipeMemorySizeInBytes();
+ }
+}
diff --git
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
index b977ce9de8c..42c0aac0e1a 100644
---
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
+++
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
@@ -731,7 +731,8 @@ disk_space_warning_threshold=0.05
datanode_memory_proportion=3:3:1:1:1:1
# ConfigNode Memory Allocation Ratio: Pipe and Free Memory.
-# The parameter form is a:b, where a and b are integers. Currently, only
PipePeriodicalLogReducer is connected to Pipe memory on ConfigNode.
+# The parameter form is a:b, where a and b are integers. Currently, ConfigNode
Pipe uses
+# this memory for log reducer and receiver temporary buffers.
# effectiveMode: restart
confignode_memory_proportion=1:9