This is an automated email from the ASF dual-hosted git repository. jt2594838 pushed a commit to branch delete_with_attribute in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 63303aada1be6fcd173cb93ff7d0923054e34cfe Author: Tian Jiang <[email protected]> AuthorDate: Fri Jun 26 14:44:49 2026 +0800 temp1 --- .../iotdb/db/i18n/StorageEngineMessages.java | 2 + .../iotdb/db/i18n/StorageEngineMessages.java | 2 + .../DirectBufferMemoryAllocationException.java | 35 ++++++++ .../iotdb/db/storageengine/StorageEngine.java | 7 ++ .../db/storageengine/dataregion/DataRegion.java | 17 +--- .../iotdb/db/storageengine/StorageEngineTest.java | 97 ++++++++++++++++++++++ 6 files changed, 147 insertions(+), 13 deletions(-) diff --git a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java index cc708e7e48c..a7a5bc6c9c3 100644 --- a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java +++ b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/StorageEngineMessages.java @@ -80,6 +80,8 @@ public final class StorageEngineMessages { public static final String INTERRUPTED_WAITING_THREAD_POOL_EXIT = "Interrupted while waiting {} thread pool to exit. "; public static final String BUFFERED_ARRAY_SIZE_THRESHOLD = "BufferedArraySizeThreshold is {}"; public static final String CURRENT_SG_COST = "Current Sg cost is {}"; + public static final String DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED = + "Total allocated memory for direct buffer will be %d, which is greater than limit mem cost: %d"; public static final String FORCE_DEGRADE_TSFILE_RESOURCE = "Force degrade tsfile resource {}"; public static final String CANNOT_DEGRADE_TIME_INDEX_ALL_FILE_LEVEL = "Can't degrade time index any more because all time index are file level."; public static final String DEGRADE_TSFILE_RESOURCE = "Degrade tsfile resource {}"; diff --git a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java index 792909a97e0..4addaeeaa6c 100644 --- a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java +++ b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/StorageEngineMessages.java @@ -80,6 +80,8 @@ public final class StorageEngineMessages { public static final String INTERRUPTED_WAITING_THREAD_POOL_EXIT = "等待 {} 线程池退出时被中断。 "; public static final String BUFFERED_ARRAY_SIZE_THRESHOLD = "BufferedArraySizeThreshold 为 {}"; public static final String CURRENT_SG_COST = "当前存储组内存开销为 {}"; + public static final String DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED = + "DirectBuffer 总分配内存将达到 %d,超过内存限制:%d"; public static final String FORCE_DEGRADE_TSFILE_RESOURCE = "强制降级 TsFile 资源 {}"; public static final String CANNOT_DEGRADE_TIME_INDEX_ALL_FILE_LEVEL = "无法继续降级时间索引,所有时间索引已为文件级别。"; public static final String DEGRADE_TSFILE_RESOURCE = "降级 TsFile 资源 {}"; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java new file mode 100644 index 00000000000..0e29e76b880 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/DirectBufferMemoryAllocationException.java @@ -0,0 +1,35 @@ +/* + * 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.exception; + +import org.apache.iotdb.db.i18n.StorageEngineMessages; + +public class DirectBufferMemoryAllocationException extends DataRegionException { + + private static final long serialVersionUID = -8268138833145785933L; + + public DirectBufferMemoryAllocationException( + long allocatedDirectBufferMemory, long directBufferMemoryLimit) { + super( + String.format( + StorageEngineMessages.DIRECT_BUFFER_MEMORY_ALLOCATION_FAILED, + allocatedDirectBufferMemory, + directBufferMemoryLimit)); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java index 532b6878577..389297ac401 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/StorageEngine.java @@ -49,6 +49,7 @@ import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBConfig; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.exception.DataRegionException; +import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException; import org.apache.iotdb.db.exception.StorageEngineException; import org.apache.iotdb.db.exception.TsFileProcessorException; import org.apache.iotdb.db.exception.WriteProcessRejectException; @@ -260,6 +261,12 @@ public class StorageEngine implements IService { } catch (DataRegionException e) { LOGGER.error( "Failed to recover data region {}[{}]", sgName, dataRegionId.getId(), e); + WALRecoverManager.getInstance() + .getAllDataRegionScannedLatch() + .countDownWithException(e.getMessage()); + if (e instanceof DirectBufferMemoryAllocationException) { + throw e; + } return null; } dataRegionMap.put(dataRegionId, dataRegion); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java index 03807c60d74..f98ba5ad577 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java @@ -61,6 +61,7 @@ import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; import org.apache.iotdb.db.exception.BatchProcessException; import org.apache.iotdb.db.exception.DataRegionException; import org.apache.iotdb.db.exception.DataTypeInconsistentException; +import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException; import org.apache.iotdb.db.exception.TsFileProcessorException; import org.apache.iotdb.db.exception.WriteProcessException; import org.apache.iotdb.db.exception.WriteProcessRejectException; @@ -594,10 +595,6 @@ public class DataRegion implements IDataRegionForQuery { try { recoverCompaction(); } catch (Exception e) { - // signal wal recover manager to recover this region's files - WALRecoverManager.getInstance() - .getAllDataRegionScannedLatch() - .countDownWithException(e.getMessage()); throw new DataRegionException(e); } @@ -768,10 +765,6 @@ public class DataRegion implements IDataRegionForQuery { updatePartitionFileVersion(partitionNum, resource.getVersion()); } } catch (IOException e) { - // signal wal recover manager to recover this region's files - WALRecoverManager.getInstance() - .getAllDataRegionScannedLatch() - .countDownWithException(e.getMessage()); throw new DataRegionException(e); } @@ -5117,11 +5110,9 @@ public class DataRegion implements IDataRegionForQuery { private void acquireDirectBufferMemory() throws DataRegionException { long acquireDirectBufferMemCost = getAcquireDirectBufferMemCost(); if (!SystemInfo.getInstance().addDirectBufferMemoryCost(acquireDirectBufferMemCost)) { - throw new DataRegionException( - "Total allocated memory for direct buffer will be " - + (SystemInfo.getInstance().getDirectBufferMemoryCost() + acquireDirectBufferMemCost) - + ", which is greater than limit mem cost: " - + SystemInfo.getInstance().getTotalDirectBufferMemorySizeLimit()); + throw new DirectBufferMemoryAllocationException( + SystemInfo.getInstance().getDirectBufferMemoryCost() + acquireDirectBufferMemCost, + SystemInfo.getInstance().getTotalDirectBufferMemorySizeLimit()); } this.directBufferMemoryCost = acquireDirectBufferMemCost; } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java index 4c4ac20e9ae..eb38bdff25a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/StorageEngineTest.java @@ -18,9 +18,13 @@ */ package org.apache.iotdb.db.storageengine; +import org.apache.iotdb.commons.concurrent.ExceptionalCountDownLatch; import org.apache.iotdb.commons.consensus.DataRegionId; import org.apache.iotdb.commons.utils.TimePartitionUtils; +import org.apache.iotdb.db.exception.DataRegionException; +import org.apache.iotdb.db.exception.DirectBufferMemoryAllocationException; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; +import org.apache.iotdb.db.storageengine.dataregion.wal.recover.WALRecoverManager; import com.google.common.collect.Lists; import org.junit.After; @@ -28,12 +32,21 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"}) @RunWith(PowerMockRunner.class) @@ -84,4 +97,88 @@ public class StorageEngineTest { Assert.assertEquals(1, TimePartitionUtils.getTimePartitionId(timePartitionInterval * 2 - 1)); Assert.assertEquals(2, TimePartitionUtils.getTimePartitionId(timePartitionInterval * 2 + 1)); } + + @Test + public void testNotifyWALRecoverManagerWhenDirectBufferAllocationFailed() throws Exception { + StorageEngine spyStorageEngine = PowerMockito.spy(storageEngine); + DirectBufferMemoryAllocationException directBufferMemoryAllocationException = + new DirectBufferMemoryAllocationException(2, 1); + ExecutorService testThreadPool = Executors.newSingleThreadExecutor(); + setCachedThreadPool(spyStorageEngine, testThreadPool); + PowerMockito.doReturn( + Collections.singletonMap("root.sg", Lists.newArrayList(new DataRegionId(0)))) + .when(spyStorageEngine) + .getLocalDataRegionInfo(); + PowerMockito.doThrow(directBufferMemoryAllocationException) + .when(spyStorageEngine) + .buildNewDataRegion( + ArgumentMatchers.eq("root.sg"), ArgumentMatchers.any(DataRegionId.class)); + + List<Future<Void>> futures = new ArrayList<>(); + Method asyncRecoverMethod = StorageEngine.class.getDeclaredMethod("asyncRecover", List.class); + asyncRecoverMethod.setAccessible(true); + try { + asyncRecoverMethod.invoke(spyStorageEngine, futures); + + Assert.assertEquals(1, futures.size()); + try { + futures.get(0).get(); + Assert.fail("Expected data region recovery to fail."); + } catch (ExecutionException e) { + Assert.assertSame(directBufferMemoryAllocationException, e.getCause()); + } + + ExceptionalCountDownLatch latch = + WALRecoverManager.getInstance().getAllDataRegionScannedLatch(); + Assert.assertTrue(latch.hasException()); + Assert.assertEquals( + directBufferMemoryAllocationException.getMessage(), latch.getExceptionMessage()); + } finally { + WALRecoverManager.getInstance().clear(); + testThreadPool.shutdownNow(); + setCachedThreadPool(spyStorageEngine, null); + } + } + + @Test + public void testNotifyWALRecoverManagerButContinueForOtherDataRegionException() throws Exception { + StorageEngine spyStorageEngine = PowerMockito.spy(storageEngine); + DataRegionException dataRegionException = new DataRegionException("other recovery failure"); + ExecutorService testThreadPool = Executors.newSingleThreadExecutor(); + setCachedThreadPool(spyStorageEngine, testThreadPool); + PowerMockito.doReturn( + Collections.singletonMap("root.sg", Lists.newArrayList(new DataRegionId(0)))) + .when(spyStorageEngine) + .getLocalDataRegionInfo(); + PowerMockito.doThrow(dataRegionException) + .when(spyStorageEngine) + .buildNewDataRegion( + ArgumentMatchers.eq("root.sg"), ArgumentMatchers.any(DataRegionId.class)); + + List<Future<Void>> futures = new ArrayList<>(); + Method asyncRecoverMethod = StorageEngine.class.getDeclaredMethod("asyncRecover", List.class); + asyncRecoverMethod.setAccessible(true); + try { + asyncRecoverMethod.invoke(spyStorageEngine, futures); + + Assert.assertEquals(1, futures.size()); + futures.get(0).get(); + + ExceptionalCountDownLatch latch = + WALRecoverManager.getInstance().getAllDataRegionScannedLatch(); + Assert.assertTrue(latch.hasException()); + Assert.assertEquals(dataRegionException.getMessage(), latch.getExceptionMessage()); + } finally { + WALRecoverManager.getInstance().clear(); + testThreadPool.shutdownNow(); + setCachedThreadPool(spyStorageEngine, null); + } + } + + private void setCachedThreadPool(StorageEngine storageEngine, ExecutorService executorService) + throws Exception { + Field cachedThreadPoolField = StorageEngine.class.getDeclaredField("cachedThreadPool"); + cachedThreadPoolField.setAccessible(true); + cachedThreadPoolField.set(storageEngine, executorService); + } }
