You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ju...@apache.org on 2026/10/22 11:06:15 UTC
(iotdb) 01/01: [Pipe] Unify TsFile parser allocation and resize quotas (#18984)
This is an automated email from the ASF dual-hosted git repository.
Caideyipi pushed a commit to branch cp-b626c602-dev-1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 59f37967386900f93789cfae11cfd6bf10a3e6a5
Author: Caideyipi <87...@users.noreply.github.com>
AuthorDate: Thu Oct 22 16:29:42 2026 +0800
[Pipe] Unify TsFile parser allocation and resize quotas (#18984)
(cherry picked from commit b626c602ead0ba8d96744eac8324d948c19a4e30)
---
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 6 +
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 6 +
.../TsFileInsertionEventParserMemoryManager.java | 15 +--
.../pipe/resource/memory/PipeMemoryBlockType.java | 1 +
.../db/pipe/resource/memory/PipeMemoryManager.java | 71 ++++++++--
.../memory/PipeTsFileParserMemoryBlock.java | 27 ++++
.../memory/PipeMemoryManagerResizeTest.java | 145 +++++++++++++++++++++
7 files changed, 254 insertions(+), 17 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 db51ff09b00..978279cc6d9 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
@@ -23,6 +23,12 @@ public final class DataNodePipeMessages {
private DataNodePipeMessages() {}
+ public static final String
+ EXCEPTION_FORCERESIZE_FAILED_AFTER_ARG_RETRIES_TOTAL_ARG_BYTES_USED_ARG_BYTES_USEDTABLETS_ARG_BYTES_USEDTSFILES_ARG_BYTES_TABLETLIMIT_ARG_BYTES_TSFILELIMIT_ARG_BYTES_TABLETANDTSFILELIMIT_ARG_BYTES_PROTECTEDPARSERLIMIT_ARG_BYTES_REQUESTED_ARG_BYTES_311C8C7E =
+ "forceResize failed after %d retries: total=%d bytes, used=%d bytes, "
+ + "usedTablets=%d bytes, usedTsFiles=%d bytes, tabletLimit=%d bytes, tsFileLimit=%d bytes, "
+ + "tabletAndTsFileLimit=%d bytes, protectedParserLimit=%d bytes, requested=%d bytes";
+
// ===================== LEGACY PIPE RECEIVER RESPONSES =====================
public static final String MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238 =
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 053fc9cf69f..e3e72db09fb 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
@@ -23,6 +23,12 @@ public final class DataNodePipeMessages {
private DataNodePipeMessages() {}
+ public static final String
+ EXCEPTION_FORCERESIZE_FAILED_AFTER_ARG_RETRIES_TOTAL_ARG_BYTES_USED_ARG_BYTES_USEDTABLETS_ARG_BYTES_USEDTSFILES_ARG_BYTES_TABLETLIMIT_ARG_BYTES_TSFILELIMIT_ARG_BYTES_TABLETANDTSFILELIMIT_ARG_BYTES_PROTECTEDPARSERLIMIT_ARG_BYTES_REQUESTED_ARG_BYTES_311C8C7E =
+ "forceResize 重试 %d 次后仍无法分配内存:total=%d bytes,used=%d bytes,"
+ + "usedTablets=%d bytes,usedTsFiles=%d bytes,tabletLimit=%d bytes,tsFileLimit=%d bytes,"
+ + "tabletAndTsFileLimit=%d bytes,protectedParserLimit=%d bytes,requested=%d bytes";
+
// ===================== LEGACY PIPE RECEIVER RESPONSES =====================
public static final String MESSAGE_PIPE_DATA_TRANSPORT_ERROR_ARG_ECE6A238 = "Pipe 数据传输错误:%s";
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParserMemoryManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParserMemoryManager.java
index abb1dfebe41..5b901ce475a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParserMemoryManager.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/event/common/tsfile/parser/TsFileInsertionEventParserMemoryManager.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.pipe.event.common.tsfile.parser;
import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryBlock;
+import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryManager;
/** Allocates parser working memory from the pool owned by the caller. */
public interface TsFileInsertionEventParserMemoryManager {
@@ -41,7 +42,8 @@ public interface TsFileInsertionEventParserMemoryManager {
public TsFileInsertionEventParserMemoryBlock forceAllocateForTabletWithRetry(
final long sizeInBytes) {
return new PipeBlock(
- PipeDataNodeResourceManager.memory().forceAllocateForTabletWithRetry(sizeInBytes));
+ PipeDataNodeResourceManager.memory()
+ .forceAllocateForTsFileParserWithRetry(sizeInBytes));
}
@Override
@@ -66,15 +68,10 @@ public interface TsFileInsertionEventParserMemoryManager {
@Override
public void forceResize(final long newSizeInBytes) {
final long oldSize = delegate.getMemoryUsageInBytes();
- if (!PipeDataNodeResourceManager.memory().tryResize(delegate, newSizeInBytes)) {
+ final PipeMemoryManager memoryManager = PipeDataNodeResourceManager.memory();
+ if (!memoryManager.tryResize(delegate, newSizeInBytes)) {
throw new PipeRuntimeOutOfMemoryCriticalException(
- String.format(
- "forceResize: failed to allocate memory after %d retries, total memory size %d "
- + "bytes, used memory size %d bytes, requested memory size %d bytes",
- 0,
- PipeDataNodeResourceManager.memory().getTotalNonFloatingMemorySizeInBytes(),
- PipeDataNodeResourceManager.memory().getUsedMemorySizeInBytes(),
- newSizeInBytes - oldSize));
+ memoryManager.getResizeFailureMessage(0, newSizeInBytes - oldSize));
}
}
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryBlockType.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryBlockType.java
index 846fc7dd1ce..78fd9aff670 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryBlockType.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryBlockType.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.pipe.resource.memory;
public enum PipeMemoryBlockType {
NORMAL,
TABLET,
+ TS_FILE_PARSER,
TS_FILE,
BATCH,
WAL
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 8fc3d49536a..a41e1d86cb1 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
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.pipe.resource.memory;
import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
import org.apache.iotdb.commons.pipe.config.PipeConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.i18n.DataNodePipeMessages;
import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
import org.apache.iotdb.db.pipe.metric.overview.PipeResourceMetrics;
import org.apache.iotdb.db.pipe.resource.memory.strategy.ThresholdAllocationStrategy;
@@ -132,6 +133,35 @@ public class PipeMemoryManager {
* totalNonFloatingMemorySizeInBytes;
}
+ private boolean isEnoughForTsFileParserMemory(
+ final long extraMemoryInBytes, final long totalNonFloatingMemorySizeInBytes) {
+ return (double) usedMemorySizeInBytesOfTablets
+ + (double) usedMemorySizeInBytesOfTsFiles
+ + (double) extraMemoryInBytes
+ < EXCEED_PROTECT_THRESHOLD
+ * allowedMaxMemorySizeInBytesOfTabletsAndTsFiles(totalNonFloatingMemorySizeInBytes);
+ }
+
+ /** Reports both global availability and the category limits that can reject a resize. */
+ public synchronized String getResizeFailureMessage(final int retries, final long requestedSize) {
+ final long totalNonFloatingMemorySizeInBytes = getTotalNonFloatingMemorySizeInBytes();
+ final double combinedLimit =
+ allowedMaxMemorySizeInBytesOfTabletsAndTsFiles(totalNonFloatingMemorySizeInBytes);
+ return String.format(
+ DataNodePipeMessages
+ .EXCEPTION_FORCERESIZE_FAILED_AFTER_ARG_RETRIES_TOTAL_ARG_BYTES_USED_ARG_BYTES_USEDTABLETS_ARG_BYTES_USEDTSFILES_ARG_BYTES_TABLETLIMIT_ARG_BYTES_TSFILELIMIT_ARG_BYTES_TABLETANDTSFILELIMIT_ARG_BYTES_PROTECTEDPARSERLIMIT_ARG_BYTES_REQUESTED_ARG_BYTES_311C8C7E,
+ retries,
+ totalNonFloatingMemorySizeInBytes,
+ usedMemorySizeInBytes,
+ usedMemorySizeInBytesOfTablets,
+ usedMemorySizeInBytesOfTsFiles,
+ Math.round(allowedMaxMemorySizeInBytesOfTablets(totalNonFloatingMemorySizeInBytes)),
+ Math.round(allowedMaxMemorySizeInBytesOfTsTiles(totalNonFloatingMemorySizeInBytes)),
+ Math.round(combinedLimit),
+ Math.round(EXCEED_PROTECT_THRESHOLD * combinedLimit),
+ requestedSize);
+ }
+
private static long getTsFileParserMemorySizeInBytes() {
return Math.max(
PipeConfig.getInstance().getTsFileParserMemory(), MEMORY_ALLOCATE_MIN_SIZE_IN_BYTES);
@@ -537,6 +567,10 @@ public class PipeMemoryManager {
private boolean isHardEnoughForResizing(
final PipeMemoryBlock block, final long extraMemoryInBytes) {
+ if (block instanceof PipeTsFileParserMemoryBlock) {
+ return isEnoughForTsFileParserMemory(
+ extraMemoryInBytes, getTotalNonFloatingMemorySizeInBytes());
+ }
if (!(block instanceof PipeTabletMemoryBlock) && !(block instanceof PipeTsFileMemoryBlock)) {
return true;
}
@@ -663,6 +697,28 @@ public class PipeMemoryManager {
}
}
+ /** Allocates TsFile parser memory against the protected tablet + TsFile combined quota. */
+ public synchronized PipeTsFileParserMemoryBlock forceAllocateForTsFileParserWithRetry(
+ final long parserMemorySizeInBytes) {
+ if (!PIPE_MEMORY_MANAGEMENT_ENABLED) {
+ return new PipeTsFileParserMemoryBlock(0);
+ }
+
+ final PipeTsFileParserMemoryBlock block =
+ (PipeTsFileParserMemoryBlock) registerMemoryBlock(0, PipeMemoryBlockType.TS_FILE_PARSER);
+ if (parserMemorySizeInBytes == 0) {
+ return block;
+ }
+
+ try {
+ forceResize(block, parserMemorySizeInBytes);
+ return block;
+ } catch (final RuntimeException e) {
+ release(block);
+ throw e;
+ }
+ }
+
public PipeModelFixedMemoryBlock forceAllocateForModelFixedMemoryBlock(
long fixedSizeInBytes, PipeMemoryBlockType type)
throws PipeRuntimeOutOfMemoryCriticalException {
@@ -790,18 +846,14 @@ public class PipeMemoryManager {
if (force) {
throw new PipeRuntimeOutOfMemoryCriticalException(
- String.format(
- "forceResize: failed to allocate memory after %d retries, "
- + "total memory size %d bytes, used memory size %d bytes, "
- + "requested memory size %d bytes",
- memoryAllocateMaxRetries,
- getTotalNonFloatingMemorySizeInBytes(),
- usedMemorySizeInBytes,
- sizeInBytes));
+ getResizeFailureMessage(memoryAllocateMaxRetries, sizeInBytes));
}
}
private boolean tryResizeInternal(final PipeMemoryBlock block, final long targetSize) {
+ if (block.isReleased()) {
+ return false;
+ }
if (!PIPE_MEMORY_MANAGEMENT_ENABLED) {
block.setMemoryUsageInBytes(targetSize);
return true;
@@ -977,6 +1029,9 @@ public class PipeMemoryManager {
case TABLET:
returnedMemoryBlock = new PipeTabletMemoryBlock(sizeInBytes);
break;
+ case TS_FILE_PARSER:
+ returnedMemoryBlock = new PipeTsFileParserMemoryBlock(sizeInBytes);
+ break;
case TS_FILE:
returnedMemoryBlock = new PipeTsFileMemoryBlock(sizeInBytes);
break;
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeTsFileParserMemoryBlock.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeTsFileParserMemoryBlock.java
new file mode 100644
index 00000000000..5258d5216b5
--- /dev/null
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/resource/memory/PipeTsFileParserMemoryBlock.java
@@ -0,0 +1,27 @@
+/*
+ * 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.db.pipe.resource.memory;
+
+/** Parser working memory charged to TsFiles and admitted against the protected combined quota. */
+public class PipeTsFileParserMemoryBlock extends PipeTsFileMemoryBlock {
+
+ public PipeTsFileParserMemoryBlock(final long memoryUsageInBytes) {
+ super(memoryUsageInBytes);
+ }
+}
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManagerResizeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManagerResizeTest.java
index 08effc87e86..ecb0cf8fbe6 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManagerResizeTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/resource/memory/PipeMemoryManagerResizeTest.java
@@ -28,6 +28,13 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class PipeMemoryManagerResizeTest {
@@ -98,6 +105,144 @@ public class PipeMemoryManagerResizeTest {
}
}
+ @Test
+ public void testTsFileParserAllocationAndResizeShareProtectedCombinedLimit() {
+ final AtomicLong floatingMemoryUsageInBytes = new AtomicLong(0);
+ final PipeMemoryManager manager =
+ new PipeMemoryManager(TOTAL_MEMORY_SIZE_IN_BYTES, floatingMemoryUsageInBytes::get);
+ final PipeTsFileParserMemoryBlock parser = manager.forceAllocateForTsFileParserWithRetry(1000);
+
+ try {
+ Assert.assertTrue(manager.tryResize(parser, 1100));
+ Assert.assertFalse(manager.tryResize(parser, 1140));
+ Assert.assertEquals(1100, parser.getMemoryUsageInBytes());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(1100, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(1100, manager.getUsedMemorySizeInBytes());
+
+ final PipeRuntimeOutOfMemoryCriticalException allocationException =
+ Assert.assertThrows(
+ PipeRuntimeOutOfMemoryCriticalException.class,
+ () -> manager.forceAllocateForTsFileParserWithRetry(40));
+ Assert.assertTrue(
+ allocationException.getMessage(),
+ allocationException.getMessage().contains("usedTsFiles=1100 bytes"));
+ Assert.assertTrue(
+ allocationException.getMessage(),
+ allocationException.getMessage().contains("tabletLimit=900 bytes"));
+ Assert.assertTrue(
+ allocationException.getMessage().contains("tabletAndTsFileLimit=1200 bytes"));
+ Assert.assertTrue(
+ allocationException.getMessage().contains("protectedParserLimit=1140 bytes"));
+ // Parser working memory must not consume the tablet-only quota: the generated tablet
+ // still needs to be retained and sent while the parser owns its decoded pages/chunks.
+ final PipeTabletMemoryBlock output = manager.forceAllocateForTabletWithRetry(50);
+ try {
+ Assert.assertTrue(manager.tryResize(output, 90));
+ Assert.assertEquals(90, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(1100, manager.getUsedMemorySizeInBytesOfTsFiles());
+ } finally {
+ manager.release(output);
+ }
+ } finally {
+ manager.release(parser);
+ }
+
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytes());
+ }
+
+ @Test
+ public void testZeroSizedTsFileParserCanGrowBeyondTabletLimitAndRelease() {
+ final PipeMemoryManager manager = new PipeMemoryManager(TOTAL_MEMORY_SIZE_IN_BYTES, () -> 0);
+ final PipeTsFileParserMemoryBlock parser = manager.forceAllocateForTsFileParserWithRetry(0);
+ try {
+ Assert.assertTrue(manager.tryResize(parser, 1000));
+ Assert.assertEquals(1000, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(1000, manager.getUsedMemorySizeInBytes());
+
+ Assert.assertTrue(manager.tryResize(parser, 100));
+ Assert.assertEquals(100, manager.getUsedMemorySizeInBytesOfTsFiles());
+ manager.release(parser);
+ Assert.assertTrue(parser.isReleased());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytes());
+ } finally {
+ manager.release(parser);
+ }
+ }
+
+ @Test
+ public void testTsFileParserSharesQuotaWithTabletsAndTsFilesAndHonorsGlobalLimit() {
+ final PipeMemoryManager manager = new PipeMemoryManager(TOTAL_MEMORY_SIZE_IN_BYTES, () -> 0);
+ final PipeTabletMemoryBlock tablet = manager.forceAllocateForTabletWithRetry(200);
+ final PipeTsFileMemoryBlock tsFile = manager.forceAllocateForTsFileWithRetry(500);
+ final PipeTsFileParserMemoryBlock parser = manager.forceAllocateForTsFileParserWithRetry(400);
+ final PipeMemoryBlock sink = manager.forceAllocate(900);
+ try {
+ Assert.assertFalse(manager.tryResize(parser, 440));
+ Assert.assertFalse(manager.tryResize(parser, 401));
+ Assert.assertEquals(200, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(900, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(2000, manager.getUsedMemorySizeInBytes());
+
+ manager.release(sink);
+ Assert.assertTrue(manager.tryResize(parser, 439));
+ Assert.assertFalse(manager.tryResize(parser, 440));
+ Assert.assertEquals(200, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(939, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(1139, manager.getUsedMemorySizeInBytes());
+ } finally {
+ manager.release(tablet);
+ manager.release(tsFile);
+ manager.release(parser);
+ manager.release(sink);
+ }
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytes());
+ }
+
+ @Test
+ public void testConcurrentTsFileParserAllocationsCannotOvershootQuota() throws Exception {
+ final PipeMemoryManager manager = new PipeMemoryManager(TOTAL_MEMORY_SIZE_IN_BYTES, () -> 0);
+ final ExecutorService executor = Executors.newFixedThreadPool(4);
+ final CountDownLatch start = new CountDownLatch(1);
+ final List<Future<PipeTsFileParserMemoryBlock>> futures = new ArrayList<>();
+ final List<PipeTsFileParserMemoryBlock> allocated = new ArrayList<>();
+ try {
+ for (int i = 0; i < 20; i++) {
+ futures.add(
+ executor.submit(
+ () -> {
+ start.await();
+ try {
+ return manager.forceAllocateForTsFileParserWithRetry(300);
+ } catch (final PipeRuntimeOutOfMemoryCriticalException e) {
+ return null;
+ }
+ }));
+ }
+ start.countDown();
+ for (final Future<PipeTsFileParserMemoryBlock> future : futures) {
+ final PipeTsFileParserMemoryBlock block = future.get(10, TimeUnit.SECONDS);
+ if (block != null) {
+ allocated.add(block);
+ }
+ }
+ Assert.assertEquals(3, allocated.size());
+ Assert.assertEquals(900, manager.getUsedMemorySizeInBytes());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTablets());
+ Assert.assertEquals(900, manager.getUsedMemorySizeInBytesOfTsFiles());
+ } finally {
+ executor.shutdownNow();
+ allocated.forEach(manager::release);
+ }
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytes());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTsFiles());
+ Assert.assertEquals(0, manager.getUsedMemorySizeInBytesOfTablets());
+ }
+
@Test
public void testTabletResizeLeavesMemoryForSinkForwardProgress() {
final PipeMemoryManager manager = new PipeMemoryManager();