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 c98bc5a0327 Use MinFolderOccupiedSpaceFirstStrategy for IoTConsensus
recv and all… (#18020)
c98bc5a0327 is described below
commit c98bc5a0327c0308a40a7c63df94301fd358f360
Author: Hongzhi Gao <[email protected]>
AuthorDate: Thu Jun 25 14:48:49 2026 +0800
Use MinFolderOccupiedSpaceFirstStrategy for IoTConsensus recv and all…
(#18020)
* Use MinFolderOccupiedSpaceFirstStrategy for IoTConsensus recv and allow
it in cluster
- IoTConsensus snapshot receive folder now always uses
MinFolderOccupiedSpaceFirstStrategy, balancing received files by least
occupied space independent of the global dn_multi_dir_strategy.
- Allow MinFolderOccupiedSpaceFirstStrategy in cluster mode
(CLUSTER_ALLOWED_MULTI_DIR_STRATEGIES) so it can be configured for
DataNodes.
- Fix NPE in the dn_multi_dir_strategy validation rollback path when the
previous value is null, so the informative RuntimeException surfaces.
- IT: add DataNodeConfig.setDnMultiDirStrategy and exercise the strategy in
IoTDBRegionMigrateWithDeletionMultiDataDirIT.
* Fix multi-recv snapshot loading to keep tsfile and mods together
When IoTConsensus spreads snapshot fragments across recv folders, disable
keepSameDiskWhenLoading so fileTarget groups companion files on one data dir.
* Group snapshot companion files into one receive folder on IoTConsensus
receiver
receiveSnapshotFragment now selects the receive folder once per TsFile group
(fileKey = file name before the first '.', matching SnapshotLoader) via a
per-snapshotId ConcurrentHashMap.computeIfAbsent, so a TsFile and its
.resource/.mods companions land in the same receive folder and the load
phase
relinks them within one data dir instead of falling back to a cross-disk
copy.
The mapping is dropped once the snapshot is loaded or cleaned up.
Convert IoTDBRegionMigrateWithDeletionMultiDataDirIT to the tree model and
add
the table-model twin IoTDBRegionMigrateWithDeletionMultiDataDirTableIT
(@TableClusterIT) so both cluster CI profiles cover the snapshot mods
transfer.
---
.../it/env/cluster/config/MppDataNodeConfig.java | 6 ++
.../it/env/remote/config/RemoteDataNodeConfig.java | 5 ++
.../apache/iotdb/itbase/env/DataNodeConfig.java | 2 +
...TDBRegionMigrateWithDeletionMultiDataDirIT.java | 48 +++++++-------
...ionMigrateWithDeletionMultiDataDirTableIT.java} | 11 +++-
.../consensus/iot/IoTConsensusServerImpl.java | 75 ++++++++++++++++++----
.../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 4 +-
.../org/apache/iotdb/db/conf/IoTDBDescriptor.java | 3 +-
.../db/consensus/DataRegionConsensusImpl.java | 5 +-
.../dataregion/snapshot/SnapshotLoader.java | 20 ++++--
.../dataregion/snapshot/IoTDBSnapshotTest.java | 5 +-
11 files changed, 131 insertions(+), 53 deletions(-)
diff --git
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
index 4d86b481cbc..8399955a5c5 100644
---
a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
+++
b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/config/MppDataNodeConfig.java
@@ -174,4 +174,10 @@ public class MppDataNodeConfig extends MppBaseConfig
implements DataNodeConfig {
setProperty("dn_data_dirs", dnDataDirs);
return this;
}
+
+ @Override
+ public DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy) {
+ setProperty("dn_multi_dir_strategy", multiDirStrategy);
+ return this;
+ }
}
diff --git
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
index 1142054d132..a76608e4851 100644
---
a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
+++
b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/config/RemoteDataNodeConfig.java
@@ -120,4 +120,9 @@ public class RemoteDataNodeConfig implements DataNodeConfig
{
public DataNodeConfig setDnDataDirs(String dnDataDirs) {
return this;
}
+
+ @Override
+ public DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy) {
+ return this;
+ }
}
diff --git
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
index 1df0d54b211..bc045c9ba2f 100644
---
a/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
+++
b/integration-test/src/main/java/org/apache/iotdb/itbase/env/DataNodeConfig.java
@@ -63,4 +63,6 @@ public interface DataNodeConfig {
DataNodeConfig setQueryCostStatWindow(int queryCostStatWindow);
DataNodeConfig setDnDataDirs(String dnDataDirs);
+
+ DataNodeConfig setDnMultiDirStrategy(String multiDirStrategy);
}
diff --git
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
index 43b2d1bc992..8214bf621cc 100644
---
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
@@ -20,12 +20,10 @@
package org.apache.iotdb.confignode.it.regionmigration.pass.daily.iotv1;
import org.apache.iotdb.consensus.ConsensusFactory;
-import org.apache.iotdb.isession.SessionConfig;
import org.apache.iotdb.it.env.EnvFactory;
import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
import org.apache.iotdb.it.framework.IoTDBTestRunner;
import org.apache.iotdb.itbase.category.ClusterIT;
-import org.apache.iotdb.itbase.env.BaseEnv;
import org.apache.tsfile.utils.Pair;
import org.awaitility.Awaitility;
@@ -47,6 +45,13 @@ import java.util.concurrent.TimeUnit;
import static
org.apache.iotdb.confignode.it.regionmigration.IoTDBRegionOperationReliabilityITFramework.getAllDataNodes;
import static
org.apache.iotdb.confignode.it.regionmigration.IoTDBRegionOperationReliabilityITFramework.getDataRegionMapWithLeader;
+/**
+ * Tree-model coverage for IoTConsensus region migration over multiple data
dirs: a deletion (mods)
+ * must survive the snapshot transfer to the migrated peer. With several data
dirs the snapshot
+ * fragments of one TsFile can be received into different folders, so the
receiver groups companion
+ * files and the loader relinks them into one data dir; if that breaks, the
migrated replica loses
+ * the deletion. See the table-model twin {@link
IoTDBRegionMigrateWithDeletionMultiDataDirTableIT}.
+ */
@RunWith(IoTDBTestRunner.class)
@Category({ClusterIT.class})
public class IoTDBRegionMigrateWithDeletionMultiDataDirIT {
@@ -72,21 +77,20 @@ public class IoTDBRegionMigrateWithDeletionMultiDataDirIT {
@Test
public void testRegionMigratePreservesDeletionWithMultiDataDirs() throws
Exception {
- try (Connection connection = EnvFactory.getEnv().getTableConnection();
+ try (Connection connection = EnvFactory.getEnv().getConnection();
Statement statement = connection.createStatement()) {
- statement.execute("CREATE DATABASE test");
- statement.execute("USE test");
- statement.execute("CREATE TABLE t1 (s1 INT64 FIELD)");
- statement.execute("INSERT INTO t1 (time, s1) VALUES (100, 100), (200,
200), (300, 300)");
+ statement.execute("CREATE DATABASE root.db");
+ statement.execute(
+ "INSERT INTO root.db.d1(timestamp, s1) VALUES (100, 100), (200,
200), (300, 300)");
statement.execute("FLUSH");
- statement.execute("DELETE FROM t1 WHERE time <= 200");
+ statement.execute("DELETE FROM root.db.d1.s1 WHERE time <= 200");
statement.execute("FLUSH");
Map<Integer, Pair<Integer, Set<Integer>>> dataRegionMapWithLeader =
getDataRegionMapWithLeader(statement);
int dataRegionIdForTest =
dataRegionMapWithLeader.keySet().stream().max(Integer::compareTo).orElseThrow();
- assertDeletionVisibleOnAllReplicas(statement, dataRegionIdForTest, 1);
+ assertDeletionVisibleOnAllReplicas(dataRegionIdForTest, 1);
Pair<Integer, Set<Integer>> leaderAndNodes =
dataRegionMapWithLeader.get(dataRegionIdForTest);
Set<Integer> allDataNodes = getAllDataNodes(statement);
@@ -123,13 +127,17 @@ public class IoTDBRegionMigrateWithDeletionMultiDataDirIT
{
}
});
- assertDeletionVisibleOnAllReplicas(statement, dataRegionIdForTest, 1);
+ assertDeletionVisibleOnAllReplicas(dataRegionIdForTest, 1);
}
}
- private void assertDeletionVisibleOnAllReplicas(
- Statement statement, int dataRegionId, int expectedCount) throws
Exception {
- Set<Integer> replicaDataNodeIds = getReplicaDataNodeIds(statement,
dataRegionId);
+ private void assertDeletionVisibleOnAllReplicas(int dataRegionId, int
expectedCount)
+ throws Exception {
+ Set<Integer> replicaDataNodeIds;
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ replicaDataNodeIds = getReplicaDataNodeIds(statement, dataRegionId);
+ }
for (int dataNodeId : replicaDataNodeIds) {
DataNodeWrapper dataNodeWrapper =
EnvFactory.getEnv().dataNodeIdToWrapper(dataNodeId).orElseThrow();
@@ -143,21 +151,15 @@ public class IoTDBRegionMigrateWithDeletionMultiDataDirIT
{
private void assertDeletionVisibleOnReplica(DataNodeWrapper dataNodeWrapper,
int expectedCount)
throws Exception {
- try (Connection connection =
- EnvFactory.getEnv()
- .getConnection(
- dataNodeWrapper,
- SessionConfig.DEFAULT_USER,
- SessionConfig.DEFAULT_PASSWORD,
- BaseEnv.TABLE_SQL_DIALECT);
+ try (Connection connection =
EnvFactory.getEnv().getConnection(dataNodeWrapper);
Statement dataNodeStatement = connection.createStatement()) {
- dataNodeStatement.execute("USE test");
- try (ResultSet countResultSet = dataNodeStatement.executeQuery("SELECT
COUNT(s1) FROM t1")) {
+ try (ResultSet countResultSet =
+ dataNodeStatement.executeQuery("SELECT COUNT(s1) FROM root.db.d1")) {
Assert.assertTrue(countResultSet.next());
Assert.assertEquals(expectedCount, countResultSet.getLong(1));
}
try (ResultSet deletedRangeResultSet =
- dataNodeStatement.executeQuery("SELECT s1 FROM t1 WHERE time <=
200")) {
+ dataNodeStatement.executeQuery("SELECT s1 FROM root.db.d1 WHERE time
<= 200")) {
Assert.assertFalse(deletedRangeResultSet.next());
}
}
diff --git
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirTableIT.java
similarity index 93%
copy from
integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
copy to
integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirTableIT.java
index 43b2d1bc992..59d5a18ccf4 100644
---
a/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/confignode/it/regionmigration/pass/daily/iotv1/IoTDBRegionMigrateWithDeletionMultiDataDirTableIT.java
@@ -24,7 +24,7 @@ import org.apache.iotdb.isession.SessionConfig;
import org.apache.iotdb.it.env.EnvFactory;
import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
import org.apache.iotdb.it.framework.IoTDBTestRunner;
-import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.TableClusterIT;
import org.apache.iotdb.itbase.env.BaseEnv;
import org.apache.tsfile.utils.Pair;
@@ -47,9 +47,14 @@ import java.util.concurrent.TimeUnit;
import static
org.apache.iotdb.confignode.it.regionmigration.IoTDBRegionOperationReliabilityITFramework.getAllDataNodes;
import static
org.apache.iotdb.confignode.it.regionmigration.IoTDBRegionOperationReliabilityITFramework.getDataRegionMapWithLeader;
+/**
+ * Table-model twin of {@link IoTDBRegionMigrateWithDeletionMultiDataDirIT}: a
deletion (mods) must
+ * survive IoTConsensus region migration across multiple data dirs, asserted
through the relational
+ * (table) SQL dialect so the table-model cluster CI covers the same snapshot
mods-transfer path.
+ */
@RunWith(IoTDBTestRunner.class)
-@Category({ClusterIT.class})
-public class IoTDBRegionMigrateWithDeletionMultiDataDirIT {
+@Category({TableClusterIT.class})
+public class IoTDBRegionMigrateWithDeletionMultiDataDirTableIT {
private static final String MULTI_DATA_DIRS =
"data/datanode/data/disk0,data/datanode/data/disk1,data/datanode/data/disk2";
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java
index 0feb0098621..91b06a4f3f4 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/iot/IoTConsensusServerImpl.java
@@ -130,6 +130,18 @@ public class IoTConsensusServerImpl {
private final Condition stateMachineCondition =
stateMachineLock.newCondition();
private final String storageDir;
private FolderManager recvFolderManager = null;
+
+ /**
+ * Per-snapshotId map of TsFile group key ({@code fileKey}) to the chosen
receive folder. It keeps
+ * all companion files of one TsFile ({@code .tsfile}/{@code
.tsfile.resource}/{@code
+ * .tsfile.mods2}/...) in the same receive folder, so the load phase can
hard-link them inside a
+ * single data dir instead of falling back to a cross-disk copy. The {@code
fileKey} rule matches
+ * {@code SnapshotLoader#createLinksFromSnapshotToSourceDir} so grouping is
consistent end to end.
+ * Entries are removed once the snapshot is loaded or cleaned up.
+ */
+ private final ConcurrentHashMap<String, ConcurrentHashMap<String, String>>
+ snapshotReceiveFolderMap = new ConcurrentHashMap<>();
+
private final TreeSet<Peer> configuration;
private final AtomicLong searchIndex;
private final LogDispatcher logDispatcher;
@@ -447,11 +459,32 @@ public class IoTConsensusServerImpl {
return;
}
- recvFolderManager.getNextWithRetry(
- folder -> {
- writeSnapshotFragment(getSnapshotPath(folder, targetFilePath),
fileChunk, fileOffset);
- return null;
- });
+ // Place every companion file of the same TsFile into one receive
folder. The fileKey rule
+ // (filename before the first '.') matches SnapshotLoader so the group
stays together. The
+ // folder is selected at most once per fileKey via computeIfAbsent,
which is safe under the
+ // concurrent IoTConsensusRPC-Processor receivers.
+ String fileKey = getSnapshotFileKey(targetFilePath);
+ ConcurrentHashMap<String, String> folderMap =
+ snapshotReceiveFolderMap.computeIfAbsent(snapshotId, k -> new
ConcurrentHashMap<>());
+ String folder;
+ try {
+ folder =
+ folderMap.computeIfAbsent(
+ fileKey,
+ k -> {
+ try {
+ return recvFolderManager.getNextFolder();
+ } catch (DiskSpaceInsufficientException ex) {
+ throw new RuntimeException(ex);
+ }
+ });
+ } catch (RuntimeException re) {
+ if (re.getCause() instanceof DiskSpaceInsufficientException) {
+ throw (DiskSpaceInsufficientException) re.getCause();
+ }
+ throw re;
+ }
+ writeSnapshotFragment(getSnapshotPath(folder, targetFilePath),
fileChunk, fileOffset);
} catch (IOException e) {
throw new ConsensusGroupModifyPeerException(
String.format(IoTConsensusMessages.ERROR_RECEIVING_SNAPSHOT,
snapshotId), e);
@@ -492,6 +525,14 @@ public class IoTConsensusServerImpl {
return originalFilePath.substring(originalFilePath.indexOf(snapshotId));
}
+ /**
+ * Groups companion files of one TsFile. Uses the same rule as {@code
+ * SnapshotLoader#createLinksFromSnapshotToSourceDir}: the file name up to
the first {@code '.'}.
+ */
+ private String getSnapshotFileKey(String targetFilePath) {
+ return new File(targetFilePath).getName().split("\\.")[0];
+ }
+
private void clearOldSnapshot() {
File directory = new File(storageDir);
File[] versionFiles = directory.listFiles((dir, name) ->
name.startsWith(SNAPSHOT_DIR_NAME));
@@ -525,17 +566,22 @@ public class IoTConsensusServerImpl {
// Note: an empty region produces a snapshot with zero fragments, so none
of the receive folders
// contains it. That is a legitimate (no-op) load, not a failure, so an
absent snapshot must not
// be reported as failure here.
- List<File> snapshotDirs = new ArrayList<>();
- for (String dir : recvFolderManager.getFolders()) {
- File snapshotDir = getSnapshotPath(dir, snapshotId);
- if (snapshotDir.exists()) {
- snapshotDirs.add(snapshotDir);
+ try {
+ List<File> snapshotDirs = new ArrayList<>();
+ for (String dir : recvFolderManager.getFolders()) {
+ File snapshotDir = getSnapshotPath(dir, snapshotId);
+ if (snapshotDir.exists()) {
+ snapshotDirs.add(snapshotDir);
+ }
}
+ if (snapshotDirs.isEmpty()) {
+ return true;
+ }
+ return stateMachine.loadSnapshot(snapshotDirs);
+ } finally {
+ // Receiving is finished for this snapshot; drop its receive-folder
mapping.
+ snapshotReceiveFolderMap.remove(snapshotId);
}
- if (snapshotDirs.isEmpty()) {
- return true;
- }
- return stateMachine.loadSnapshot(snapshotDirs);
}
private File getSnapshotPath(String curStorageDir, String
snapshotRelativePath) {
@@ -1177,6 +1223,7 @@ public class IoTConsensusServerImpl {
}
public void cleanupSnapshot(String snapshotId) throws
ConsensusGroupModifyPeerException {
+ snapshotReceiveFolderMap.remove(snapshotId);
List<String> allDirs = new
ArrayList<>(Collections.singletonList(storageDir));
allDirs.addAll(recvFolderManager.getFolders());
for (String dir : allDirs) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index c20122bcf50..44ac962aaea 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -89,7 +89,9 @@ public class IoTDBConfig {
private static final Logger logger =
LoggerFactory.getLogger(IoTDBConfig.class);
private static final String MULTI_DIR_STRATEGY_PREFIX =
"org.apache.iotdb.commons.disk.strategy.";
private static final String[] CLUSTER_ALLOWED_MULTI_DIR_STRATEGIES =
- new String[] {"SequenceStrategy", "MaxDiskUsableSpaceFirstStrategy"};
+ new String[] {
+ "SequenceStrategy", "MaxDiskUsableSpaceFirstStrategy",
"MinFolderOccupiedSpaceFirstStrategy"
+ };
private static final String DEFAULT_MULTI_DIR_STRATEGY = "SequenceStrategy";
private static final String STORAGE_GROUP_MATCHER =
"([a-zA-Z0-9`_.\\-\\u2E80-\\u9FFF]+)";
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
index 59878f68e0d..7acda29f959 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java
@@ -369,7 +369,8 @@ public class IoTDBDescriptor {
try {
conf.checkMultiDirStrategyClassName();
} catch (Exception e) {
- conf.setMultiDirStrategyClassName(oldMultiDirStrategyClassName.trim());
+ conf.setMultiDirStrategyClassName(
+ oldMultiDirStrategyClassName == null ? null :
oldMultiDirStrategyClassName.trim());
throw e;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
index a0735084fa5..22bab53a8d2 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/DataRegionConsensusImpl.java
@@ -142,8 +142,9 @@ public class DataRegionConsensusImpl {
.setThisNode(new TEndPoint(CONF.getInternalAddress(),
CONF.getDataRegionConsensusPort()))
.setStorageDir(CONF.getDataRegionConsensusDir())
.setRecvSnapshotDirs(Arrays.asList(CONF.getLocalDataDirs()))
- .setDirectoryStrategyType(
-
DirectoryStrategyType.fromClassName(CONF.getMultiDirStrategyClassName()))
+ // IoTConsensus always balances received snapshot files by least
occupied space,
+ // independent of the global dn_multi_dir_strategy.
+
.setDirectoryStrategyType(DirectoryStrategyType.MIN_FOLDER_OCCUPIED_SPACE_FIRST_STRATEGY)
.setConsensusGroupType(TConsensusGroupType.DataRegion)
.setIoTConsensusConfig(
IoTConsensusConfig.newBuilder()
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
index 588b0e21956..573d8fc293d 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/SnapshotLoader.java
@@ -153,7 +153,9 @@ public class SnapshotLoader {
Map<String, String> fileTarget = new HashMap<>();
for (String path : snapshotPaths) {
File snapshotDir = new File(path);
- createLinksFromSnapshotDirToDataDirWithoutLog(snapshotDir, fileTarget);
+ // IoTConsensus fragments arrive under different recv folders; do not
map each
+ // fragment back to the same disk as its recv path, rely on fileTarget
instead.
+ createLinksFromSnapshotDirToDataDirWithoutLog(snapshotDir, fileTarget,
false);
loadCompressionRatio(snapshotDir);
}
return loadSnapshot();
@@ -175,7 +177,7 @@ public class SnapshotLoader {
}
LOGGER.info(StorageEngineMessages.MOVING_SNAPSHOT_FILE_TO_DATA_DIRS);
File snapshotDir = new File(snapshotPath);
- createLinksFromSnapshotDirToDataDirWithoutLog(snapshotDir, new
HashMap<>());
+ createLinksFromSnapshotDirToDataDirWithoutLog(snapshotDir, new
HashMap<>(), true);
loadCompressionRatio(snapshotDir);
return loadSnapshot();
} catch (IOException | DiskSpaceInsufficientException e) {
@@ -300,7 +302,7 @@ public class SnapshotLoader {
}
private void createLinksFromSnapshotDirToDataDirWithoutLog(
- File sourceDir, Map<String, String> fileTarget)
+ File sourceDir, Map<String, String> fileTarget, boolean
preferKeepSameDiskWhenLoading)
throws IOException, DiskSpaceInsufficientException {
if (!sourceDir.exists()) {
throw new IOException(
@@ -346,7 +348,8 @@ public class SnapshotLoader {
+ dataRegionId
+ File.separator
+ timePartitionFolder.getName();
- createLinksFromSnapshotToSourceDir(targetSuffix, files, folderManager,
fileTarget);
+ createLinksFromSnapshotToSourceDir(
+ targetSuffix, files, folderManager, fileTarget,
preferKeepSameDiskWhenLoading);
}
}
@@ -365,7 +368,8 @@ public class SnapshotLoader {
+ dataRegionId
+ File.separator
+ timePartitionFolder.getName();
- createLinksFromSnapshotToSourceDir(targetSuffix, files, folderManager,
fileTarget);
+ createLinksFromSnapshotToSourceDir(
+ targetSuffix, files, folderManager, fileTarget,
preferKeepSameDiskWhenLoading);
}
}
}
@@ -415,7 +419,8 @@ public class SnapshotLoader {
String targetSuffix,
File[] files,
FolderManager folderManager,
- Map<String, String> fileTarget)
+ Map<String, String> fileTarget,
+ boolean preferKeepSameDiskWhenLoading)
throws IOException {
for (File file : files) {
checkTsFileResourceExists(file);
@@ -430,7 +435,8 @@ public class SnapshotLoader {
try {
String firstFolderOfSameDisk =
-
IoTDBDescriptor.getInstance().getConfig().isKeepSameDiskWhenLoadingSnapshot()
+ preferKeepSameDiskWhenLoading
+ &&
IoTDBDescriptor.getInstance().getConfig().isKeepSameDiskWhenLoadingSnapshot()
?
folderManager.getFirstFolderOfSameDisk(file.getAbsolutePath())
: null;
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
index ba7c9310945..2eedca1807f 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/snapshot/IoTDBSnapshotTest.java
@@ -597,14 +597,15 @@ public class IoTDBSnapshotTest {
String.class,
File[].class,
FolderManager.class,
- Map.class);
+ Map.class,
+ boolean.class);
method.setAccessible(true);
SnapshotLoader loader = new SnapshotLoader("dummy", "root.testsg", "0");
// Tracks fileKey -> chosen data dir, so files sharing a fileKey land in
the same dir.
Map<String, String> fileTarget = new HashMap<>();
- method.invoke(loader, targetSuffix, files, folderManager, fileTarget);
+ method.invoke(loader, targetSuffix, files, folderManager, fileTarget,
true);
// The shared fileKey must be recorded exactly once, pointing at one of
the data dirs.
String fileKey = tsFile.getName().split("\\.")[0];