This is an automated email from the ASF dual-hosted git repository.
JackieTien97 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 a76341d9408 Enable show queries can be executed when the available
memory for Operators is insufficient (#18052)
a76341d9408 is described below
commit a76341d940880b0f11102c09bf8a10183315cd94
Author: Weihao Li <[email protected]>
AuthorDate: Tue Jun 30 14:07:27 2026 +0800
Enable show queries can be executed when the available memory for Operators
is insufficient (#18052)
---
.../planner/memory/MemoryReservationManager.java | 6 +
.../fragment/FragmentInstanceContext.java | 3 +
.../execution/operator/OperatorContext.java | 5 +
.../source/relational/CteScanOperator.java | 2 +-
.../plan/planner/LocalExecutionPlanner.java | 96 +++++++-----
.../memory/FakedMemoryReservationManager.java | 3 +
.../NotThreadSafeMemoryReservationManager.java | 111 ++++++++++----
.../memory/ThreadSafeMemoryReservationManager.java | 5 +
.../apache/iotdb/db/utils/cte/MemoryReader.java | 18 ++-
.../fragment/QueryModificationLoaderTest.java | 3 +
.../execution/operator/CteScanOperatorTest.java | 2 +-
.../LocalExecutionPlannerOperatorsMemoryTest.java | 164 +++++++++++++++++++++
.../queryengine/utils/cte/CteDataStore.java | 14 ++
13 files changed, 364 insertions(+), 68 deletions(-)
diff --git
a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/memory/MemoryReservationManager.java
b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/memory/MemoryReservationManager.java
index b52534b4ec2..f0420330652 100644
---
a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/memory/MemoryReservationManager.java
+++
b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/plan/planner/memory/MemoryReservationManager.java
@@ -80,4 +80,10 @@ public interface MemoryReservationManager {
* @param bytesAlreadyReserved the amount of memory that has already been
reserved
*/
void reserveMemoryVirtually(final long bytesToBeReserved, final long
bytesAlreadyReserved);
+
+ /**
+ * Mark this manager as highest-priority (e.g. SHOW QUERIES). When operators
memory is
+ * insufficient, allocation will fall back to zero bytes instead of failing.
+ */
+ void setHighestPriority(boolean isHighestPriority);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceContext.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceContext.java
index 81dd89387bf..98a385d3a38 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceContext.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/fragment/FragmentInstanceContext.java
@@ -1365,6 +1365,9 @@ public class FragmentInstanceContext extends QueryContext
{
public void setHighestPriority(boolean highestPriority) {
this.highestPriority = highestPriority;
+ if (memoryReservationManager != null) {
+ memoryReservationManager.setHighestPriority(highestPriority);
+ }
}
public boolean isSingleSourcePath() {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorContext.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorContext.java
index 584cb4fe74c..0ff922f8894 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorContext.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/OperatorContext.java
@@ -71,6 +71,11 @@ public class OperatorContext extends CommonOperatorContext {
return getInstanceContext().getSessionInfo();
}
+ public boolean isHighestPriority() {
+ FragmentInstanceContext instanceContext = getInstanceContext();
+ return instanceContext != null && instanceContext.isHighestPriority();
+ }
+
@Override
public void recordScanAggregationFromRawDataCost(long costTimeInNanos) {
if (driverContext != null && driverContext.getFragmentInstanceContext() !=
null) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/CteScanOperator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/CteScanOperator.java
index c25fe15d93e..c995ab62bca 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/CteScanOperator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/CteScanOperator.java
@@ -59,7 +59,7 @@ public class CteScanOperator implements SourceOperator {
requireNonNull(dataStore, "dataStore is null");
this.operatorContext = operatorContext;
this.sourceId = sourceId;
- this.dataReader = new MemoryReader(dataStore, queryId);
+ this.dataReader = new MemoryReader(dataStore, queryId,
operatorContext.isHighestPriority());
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
index 95f28052c1f..cfdd39c15b3 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlanner.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.commons.memory.MemoryBlockType;
import org.apache.iotdb.commons.path.IFullPath;
import org.apache.iotdb.commons.queryengine.common.SqlDialect;
import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.commons.utils.TestOnly;
import org.apache.iotdb.db.conf.DataNodeMemoryConfig;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
@@ -118,7 +119,7 @@ public class LocalExecutionPlanner {
context.invalidateParentPlanNodeIdToMemoryEstimator();
// check whether current free memory is enough to execute current query
- long estimatedMemorySize = checkMemory(memoryEstimator,
instanceContext.getStateMachine());
+ long estimatedMemorySize = checkMemory(memoryEstimator, instanceContext);
context.addPipelineDriverFactory(root, context.getDriverContext(),
estimatedMemorySize);
@@ -157,7 +158,7 @@ public class LocalExecutionPlanner {
context.invalidateParentPlanNodeIdToMemoryEstimator();
// check whether current free memory is enough to execute current query
- checkMemory(memoryEstimator, instanceContext.getStateMachine());
+ checkMemory(memoryEstimator, instanceContext);
context.addPipelineDriverFactory(root, context.getDriverContext(), 0);
@@ -193,7 +194,7 @@ public class LocalExecutionPlanner {
}
private long checkMemory(
- final PipelineMemoryEstimator memoryEstimator,
FragmentInstanceStateMachine stateMachine)
+ final PipelineMemoryEstimator memoryEstimator, FragmentInstanceContext
instanceContext)
throws MemoryNotEnoughException {
// if it is disabled, just return
@@ -206,14 +207,9 @@ public class LocalExecutionPlanner {
QueryRelatedResourceMetricSet.getInstance().updateEstimatedMemory(estimatedMemorySize);
- if (OPERATORS_MEMORY_BLOCK.allocate(estimatedMemorySize)) {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug(
- "[ConsumeMemory] consume: {}, current remaining memory: {}",
- estimatedMemorySize,
- OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes());
- }
- } else {
+ long reservedBytes =
+ allocateOperatorsMemory(estimatedMemorySize,
instanceContext.isHighestPriority());
+ if (reservedBytes < 0) {
throw new MemoryNotEnoughException(
String.format(
"There is not enough memory to execute current fragment
instance, "
@@ -221,22 +217,55 @@ public class LocalExecutionPlanner {
+ "estimated memory usage for current fragment instance is
%dB",
OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes(),
estimatedMemorySize));
}
- stateMachine.addStateChangeListener(
- newState -> {
- if (newState.isDone()) {
- try (SetThreadName fragmentInstanceName =
- new
SetThreadName(stateMachine.getFragmentInstanceId().getFullId())) {
- OPERATORS_MEMORY_BLOCK.release(estimatedMemorySize);
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug(
- "[ReleaseMemory] release: {}, current remaining memory:
{}",
- estimatedMemorySize,
- OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes());
+ FragmentInstanceStateMachine stateMachine =
instanceContext.getStateMachine();
+ if (reservedBytes > 0) {
+ stateMachine.addStateChangeListener(
+ newState -> {
+ if (newState.isDone()) {
+ try (SetThreadName fragmentInstanceName =
+ new
SetThreadName(stateMachine.getFragmentInstanceId().getFullId())) {
+ OPERATORS_MEMORY_BLOCK.release(reservedBytes);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(
+ "[ReleaseMemory] release: {}, current remaining memory:
{}",
+ reservedBytes,
+ OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes());
+ }
}
}
- }
- });
- return estimatedMemorySize;
+ });
+ }
+ return reservedBytes;
+ }
+
+ /**
+ * Try to reserve bytes from the operators memory block.
+ *
+ * @return allocated bytes on success ({@code > 0}), {@code 0} if nothing to
allocate or
+ * highest-priority fallback applies, {@code -1} if allocation failed
+ */
+ private long allocateOperatorsMemory(final long memoryInBytes, final boolean
isHighestPriority) {
+ if (memoryInBytes <= 0) {
+ return 0L;
+ }
+ if (OPERATORS_MEMORY_BLOCK.allocate(memoryInBytes)) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(
+ "[ConsumeMemory] consume: {}, current remaining memory: {}",
+ memoryInBytes,
+ OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes());
+ }
+ return memoryInBytes;
+ }
+ if (isHighestPriority) {
+ return 0L;
+ }
+ return -1L;
+ }
+
+ @TestOnly
+ long allocateOperatorsMemoryForTest(final long memoryInBytes, final boolean
isHighestPriority) {
+ return allocateOperatorsMemory(memoryInBytes, isHighestPriority);
}
private QueryDataSourceType getQueryDataSourceType(DataDriverContext
dataDriverContext) {
@@ -291,23 +320,19 @@ public class LocalExecutionPlanner {
}
}
- public void reserveFromFreeMemoryForOperators(
+ public long reserveFromFreeMemoryForOperators(
final long memoryInBytes,
final long reservedBytes,
final String queryId,
- final String contextHolder) {
+ final String contextHolder,
+ final boolean isHighestPriority)
+ throws MemoryNotEnoughException {
if (memoryInBytes <= 0) {
throw new IllegalArgumentException(
"Bytes to reserve from free memory for operators should be larger
than 0");
}
- if (OPERATORS_MEMORY_BLOCK.allocate(memoryInBytes)) {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug(
- "[ConsumeMemory] consume: {}, current remaining memory: {}",
- memoryInBytes,
- OPERATORS_MEMORY_BLOCK.getFreeMemoryInBytes());
- }
- } else {
+ long allocated = allocateOperatorsMemory(memoryInBytes, isHighestPriority);
+ if (allocated < 0) {
throw new MemoryNotEnoughException(
String.format(
"There is not enough memory for Query %s, the contextHolder is
%s,"
@@ -320,6 +345,7 @@ public class LocalExecutionPlanner {
reservedBytes,
memoryInBytes));
}
+ return allocated;
}
public void releaseToFreeMemoryForOperators(final long memoryInBytes) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/FakedMemoryReservationManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/FakedMemoryReservationManager.java
index 7cee8034a05..d1c34e365ef 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/FakedMemoryReservationManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/FakedMemoryReservationManager.java
@@ -48,4 +48,7 @@ public class FakedMemoryReservationManager implements
MemoryReservationManager {
@Override
public void reserveMemoryVirtually(
final long bytesToBeReserved, final long bytesAlreadyReserved) {}
+
+ @Override
+ public void setHighestPriority(boolean isHighestPriority) {}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java
index d156628532c..71924894c7c 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/NotThreadSafeMemoryReservationManager.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.queryengine.plan.planner.memory;
import org.apache.iotdb.calc.plan.planner.memory.MemoryReservationManager;
+import org.apache.iotdb.commons.utils.TestOnly;
import org.apache.iotdb.db.queryengine.common.QueryId;
import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner;
@@ -27,6 +28,8 @@ import org.apache.tsfile.utils.Pair;
import javax.annotation.concurrent.NotThreadSafe;
+import static com.google.common.base.Preconditions.checkState;
+
@NotThreadSafe
public class NotThreadSafeMemoryReservationManager implements
MemoryReservationManager {
// To avoid reserving memory too frequently, we choose to do it in batches.
This is the lower
@@ -39,8 +42,16 @@ public class NotThreadSafeMemoryReservationManager
implements MemoryReservationM
private final String contextHolder;
+ private boolean isHighestPriority;
+
private long reservedBytesInTotal = 0;
+ /**
+ * Bytes logically reserved but not taken from the operators pool due to
highest-priority
+ * fallback.
+ */
+ private long fallbackBytesInTotal = 0;
+
private long bytesToBeReserved = 0;
private long bytesToBeReleased = 0;
@@ -50,6 +61,21 @@ public class NotThreadSafeMemoryReservationManager
implements MemoryReservationM
this.contextHolder = contextHolder;
}
+ @Override
+ public void setHighestPriority(boolean isHighestPriority) {
+ this.isHighestPriority = isHighestPriority;
+ }
+
+ @TestOnly
+ public long getReservedBytesInTotalForTest() {
+ return reservedBytesInTotal;
+ }
+
+ @TestOnly
+ public long getFallbackBytesInTotalForTest() {
+ return fallbackBytesInTotal;
+ }
+
@Override
public void reserveMemoryCumulatively(final long size) {
bytesToBeReserved += size;
@@ -61,9 +87,18 @@ public class NotThreadSafeMemoryReservationManager
implements MemoryReservationM
@Override
public void reserveMemoryImmediately() {
if (bytesToBeReserved != 0) {
- LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
- bytesToBeReserved, reservedBytesInTotal, queryId.getId(),
contextHolder);
- reservedBytesInTotal += bytesToBeReserved;
+ long actualReserved =
+ LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
+ bytesToBeReserved,
+ reservedBytesInTotal,
+ queryId.getId(),
+ contextHolder,
+ isHighestPriority);
+ if (actualReserved == 0) {
+ fallbackBytesInTotal += bytesToBeReserved;
+ } else {
+ reservedBytesInTotal += actualReserved;
+ }
bytesToBeReserved = 0;
}
}
@@ -71,51 +106,73 @@ public class NotThreadSafeMemoryReservationManager
implements MemoryReservationM
@Override
public void reserveMemoryImmediately(final long size) {
if (size != 0) {
- LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
- size, reservedBytesInTotal, queryId.getId(), contextHolder);
- reservedBytesInTotal += size;
+ long actualReserved =
+ LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
+ size, reservedBytesInTotal, queryId.getId(), contextHolder,
isHighestPriority);
+ if (actualReserved == 0) {
+ fallbackBytesInTotal += size;
+ } else {
+ reservedBytesInTotal += actualReserved;
+ }
}
}
@Override
public void releaseMemoryCumulatively(final long size) {
+ if (size <= 0) {
+ return;
+ }
bytesToBeReleased += size;
if (bytesToBeReleased >= MEMORY_BATCH_THRESHOLD) {
- long bytesToRelease;
- if (bytesToBeReleased <= bytesToBeReserved) {
- bytesToBeReserved -= bytesToBeReleased;
- } else {
- bytesToRelease = bytesToBeReleased - bytesToBeReserved;
- bytesToBeReserved = 0;
-
LOCAL_EXECUTION_PLANNER.releaseToFreeMemoryForOperators(bytesToRelease);
- reservedBytesInTotal -= bytesToRelease;
- }
+ releaseBytesImmediately(bytesToBeReleased);
bytesToBeReleased = 0;
}
}
+ private void releaseBytesImmediately(final long size) {
+ long poolBytes = deductReleaseAccounting(size);
+ if (poolBytes > 0) {
+ LOCAL_EXECUTION_PLANNER.releaseToFreeMemoryForOperators(poolBytes);
+ }
+ }
+
+ /** Deduct release size from pending reserve, fallback quota, then pool
reservation in order. */
+ private long deductReleaseAccounting(final long size) {
+ long remaining = size;
+ if (remaining <= bytesToBeReserved) {
+ bytesToBeReserved -= remaining;
+ return 0L;
+ }
+ remaining -= bytesToBeReserved;
+ bytesToBeReserved = 0;
+
+ if (remaining <= fallbackBytesInTotal) {
+ fallbackBytesInTotal -= remaining;
+ return 0L;
+ }
+ remaining -= fallbackBytesInTotal;
+ fallbackBytesInTotal = 0;
+
+ reservedBytesInTotal -= remaining;
+ checkState(reservedBytesInTotal >= 0, "Released bytes has been larger than
reserved!");
+ return remaining;
+ }
+
@Override
public void releaseAllReservedMemory() {
if (reservedBytesInTotal != 0) {
LOCAL_EXECUTION_PLANNER.releaseToFreeMemoryForOperators(reservedBytesInTotal);
reservedBytesInTotal = 0;
- bytesToBeReserved = 0;
- bytesToBeReleased = 0;
}
+ fallbackBytesInTotal = 0;
+ bytesToBeReserved = 0;
+ bytesToBeReleased = 0;
}
@Override
public Pair<Long, Long> releaseMemoryVirtually(final long size) {
- if (bytesToBeReserved >= size) {
- bytesToBeReserved -= size;
- return new Pair<>(size, 0L);
- } else {
- long releasedBytesInReserved = bytesToBeReserved;
- long releasedBytesInTotal = size - bytesToBeReserved;
- bytesToBeReserved = 0;
- reservedBytesInTotal -= releasedBytesInTotal;
- return new Pair<>(releasedBytesInReserved, releasedBytesInTotal);
- }
+ long poolBytes = deductReleaseAccounting(size);
+ return new Pair<>(size - poolBytes, poolBytes);
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/ThreadSafeMemoryReservationManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/ThreadSafeMemoryReservationManager.java
index 2a544421f3f..0a1c6eee418 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/ThreadSafeMemoryReservationManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/memory/ThreadSafeMemoryReservationManager.java
@@ -66,4 +66,9 @@ public class ThreadSafeMemoryReservationManager extends
NotThreadSafeMemoryReser
final long bytesToBeReserved, final long bytesAlreadyReserved) {
super.reserveMemoryVirtually(bytesToBeReserved, bytesAlreadyReserved);
}
+
+ @Override
+ public synchronized void setHighestPriority(boolean isHighestPriority) {
+ super.setHighestPriority(isHighestPriority);
+ }
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/cte/MemoryReader.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/cte/MemoryReader.java
index 7daad8d46e4..b4e8d973529 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/cte/MemoryReader.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/cte/MemoryReader.java
@@ -38,12 +38,18 @@ public class MemoryReader implements CteDataReader {
private final CteDataStore dataStore;
private int tsBlockIndex;
- public MemoryReader(CteDataStore dataStore, QueryId queryId) {
+ public MemoryReader(CteDataStore dataStore, QueryId queryId, boolean
isHighestPriority) {
this.dataStore = dataStore;
this.tsBlockIndex = 0;
if (dataStore.incrementAndGetCount() == 1) {
- LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
- dataStore.ramBytesUsed(), 0L, queryId.getId(),
MemoryReader.class.getName());
+ long actualReserved =
+ LOCAL_EXECUTION_PLANNER.reserveFromFreeMemoryForOperators(
+ dataStore.ramBytesUsed(),
+ 0L,
+ queryId.getId(),
+ MemoryReader.class.getName(),
+ isHighestPriority);
+ dataStore.setActualReservedBytes(actualReserved);
}
}
@@ -63,7 +69,11 @@ public class MemoryReader implements CteDataReader {
@Override
public void close() throws IoTDBException {
if (dataStore.decrementAndGetCount() == 0) {
-
LOCAL_EXECUTION_PLANNER.releaseToFreeMemoryForOperators(dataStore.ramBytesUsed());
+ long reservedBytes = dataStore.getActualReservedBytes();
+ if (reservedBytes > 0) {
+ LOCAL_EXECUTION_PLANNER.releaseToFreeMemoryForOperators(reservedBytes);
+ }
+ dataStore.setActualReservedBytes(0L);
}
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/QueryModificationLoaderTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/QueryModificationLoaderTest.java
index 39b9c41447c..65cbc0f5b9f 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/QueryModificationLoaderTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/fragment/QueryModificationLoaderTest.java
@@ -339,6 +339,9 @@ public class QueryModificationLoaderTest {
reservedBytes += bytesToBeReserved + bytesAlreadyReserved;
}
+ @Override
+ public void setHighestPriority(boolean isHighestPriority) {}
+
private long getReservedBytes() {
return reservedBytes;
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java
index 7309e2e06ad..ab476f61ee9 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/CteScanOperatorTest.java
@@ -177,7 +177,7 @@ public class CteScanOperatorTest {
new CteScanOperator(operatorContext, planNodeId, cteDataStore,
queryId);
assertEquals(2, cteDataStore.getCount());
- assertEquals(896, cteDataStore.ramBytesUsed());
+ assertEquals(904, cteDataStore.ramBytesUsed());
// Both operators should be able to read data
assertTrue(operator1.hasNext());
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlannerOperatorsMemoryTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlannerOperatorsMemoryTest.java
new file mode 100644
index 00000000000..6d0cabb0443
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/LocalExecutionPlannerOperatorsMemoryTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.queryengine.plan.planner;
+
+import org.apache.iotdb.db.queryengine.common.QueryId;
+import
org.apache.iotdb.db.queryengine.plan.planner.memory.NotThreadSafeMemoryReservationManager;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LocalExecutionPlannerOperatorsMemoryTest {
+
+ private static final LocalExecutionPlanner PLANNER =
LocalExecutionPlanner.getInstance();
+
+ private long bytesHeldByTest = 0L;
+
+ @After
+ public void tearDown() {
+ if (bytesHeldByTest > 0) {
+ PLANNER.releaseToFreeMemoryForOperators(bytesHeldByTest);
+ bytesHeldByTest = 0L;
+ }
+ }
+
+ @Test
+ public void testAllocateOperatorsMemoryFailsWhenInsufficient() {
+ long free = PLANNER.getFreeMemoryForOperators();
+ Assert.assertEquals(-1L, PLANNER.allocateOperatorsMemoryForTest(free +
1024L, false));
+ }
+
+ @Test
+ public void testAllocateOperatorsMemorySucceedsWhenAvailable() {
+ long request = Math.min(1024L, PLANNER.getFreeMemoryForOperators());
+ long reserved = PLANNER.allocateOperatorsMemoryForTest(request, false);
+ Assert.assertEquals(request, reserved);
+ bytesHeldByTest = reserved;
+ }
+
+ @Test
+ public void testHighestPriorityAllocatesWhenPoolHasRoom() {
+ long request = Math.min(1024L, PLANNER.getFreeMemoryForOperators());
+ if (request <= 0) {
+ return;
+ }
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+
+ long reserved = PLANNER.allocateOperatorsMemoryForTest(request, true);
+ Assert.assertEquals(request, reserved);
+ Assert.assertEquals(freeBefore - request,
PLANNER.getFreeMemoryForOperators());
+ bytesHeldByTest = reserved;
+ }
+
+ @Test
+ public void testHighestPriorityFallbackWhenPoolInsufficient() {
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+ long request = freeBefore + 1024L;
+
+ Assert.assertEquals(0L, PLANNER.allocateOperatorsMemoryForTest(request,
true));
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+ }
+
+ @Test
+ public void
testMemoryReservationManagerHighestPriorityAllocatesWhenPoolHasRoom() {
+ long request = Math.min(1024L, PLANNER.getFreeMemoryForOperators());
+ if (request <= 0) {
+ return;
+ }
+
+ NotThreadSafeMemoryReservationManager manager =
+ new NotThreadSafeMemoryReservationManager(new QueryId("show_queries"),
"test");
+ manager.setHighestPriority(true);
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+
+ manager.reserveMemoryImmediately(request);
+ Assert.assertEquals(request, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(freeBefore - request,
PLANNER.getFreeMemoryForOperators());
+
+ manager.releaseAllReservedMemory();
+ Assert.assertEquals(0L, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+ }
+
+ @Test
+ public void
testMemoryReservationManagerHighestPriorityFallbackWhenPoolInsufficient() {
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+ long request = freeBefore + 1024L;
+
+ NotThreadSafeMemoryReservationManager manager =
+ new NotThreadSafeMemoryReservationManager(new QueryId("show_queries"),
"test");
+ manager.setHighestPriority(true);
+
+ manager.reserveMemoryImmediately(request);
+ Assert.assertEquals(0L, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(request, manager.getFallbackBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+
+ manager.releaseMemoryCumulatively(request);
+ Assert.assertEquals(0L, manager.getFallbackBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+
+ manager.releaseAllReservedMemory();
+ Assert.assertEquals(0L, manager.getFallbackBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+ }
+
+ @Test
+ public void
testMemoryReservationManagerHighestPriorityFallbackReleaseViaBatchThreshold() {
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+ long request = freeBefore + MEMORY_BATCH_THRESHOLD;
+
+ NotThreadSafeMemoryReservationManager manager =
+ new NotThreadSafeMemoryReservationManager(new QueryId("show_queries"),
"test");
+ manager.setHighestPriority(true);
+
+ manager.reserveMemoryImmediately(request);
+ Assert.assertEquals(0L, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(request, manager.getFallbackBytesInTotalForTest());
+
+ manager.releaseMemoryCumulatively(request);
+ Assert.assertEquals(0L, manager.getFallbackBytesInTotalForTest());
+ Assert.assertEquals(0L, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+ }
+
+ private static final long MEMORY_BATCH_THRESHOLD = 1024L * 1024L;
+
+ @Test
+ public void testMemoryReservationManagerNormalPriorityReserveAndRelease() {
+ long request = Math.min(1024L, PLANNER.getFreeMemoryForOperators());
+ if (request <= 0) {
+ return;
+ }
+
+ NotThreadSafeMemoryReservationManager manager =
+ new NotThreadSafeMemoryReservationManager(new QueryId("normal_query"),
"test");
+ long freeBefore = PLANNER.getFreeMemoryForOperators();
+
+ manager.reserveMemoryImmediately(request);
+ Assert.assertEquals(request, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(freeBefore - request,
PLANNER.getFreeMemoryForOperators());
+
+ manager.releaseAllReservedMemory();
+ Assert.assertEquals(0L, manager.getReservedBytesInTotalForTest());
+ Assert.assertEquals(freeBefore, PLANNER.getFreeMemoryForOperators());
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/utils/cte/CteDataStore.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/utils/cte/CteDataStore.java
index d2cbcc17fa5..81484abfe0b 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/utils/cte/CteDataStore.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/utils/cte/CteDataStore.java
@@ -48,6 +48,12 @@ public class CteDataStore implements Accountable {
// reference count by CteScanReader
private final AtomicInteger count;
+ /**
+ * Bytes actually reserved from the operators free-memory pool for this CTE
data store. May be
+ * {@code 0} if reservation fell back (e.g. highest-priority query with
insufficient pool).
+ */
+ private long actualReservedBytes;
+
public CteDataStore(TableSchema tableSchema, List<Integer>
columnIndex2TsBlockColumnIndexList) {
this.tableSchema = tableSchema;
this.columnIndex2TsBlockColumnIndexList =
columnIndex2TsBlockColumnIndexList;
@@ -106,4 +112,12 @@ public class CteDataStore implements Accountable {
public int getCount() {
return count.get();
}
+
+ public long getActualReservedBytes() {
+ return actualReservedBytes;
+ }
+
+ public void setActualReservedBytes(long actualReservedBytes) {
+ this.actualReservedBytes = actualReservedBytes;
+ }
}