This is an automated email from the ASF dual-hosted git repository.
nizhikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 07307c91527 IGNITE-27380 DataStorageConfiguration#extraSnapshotPaths
added (#12602)
07307c91527 is described below
commit 07307c9152793b9b632218a10e11c012d9026732
Author: Nikolay <[email protected]>
AuthorDate: Wed Dec 24 18:41:02 2025 +0300
IGNITE-27380 DataStorageConfiguration#extraSnapshotPaths added (#12602)
---
.../configuration/DataStorageConfiguration.java | 36 +-
.../org/apache/ignite/internal/IgnitionEx.java | 13 +
.../cache/persistence/filename/NodeFileTree.java | 14 +-
.../persistence/filename/SnapshotFileTree.java | 59 +-
.../CustomCacheStorageConfigurationSelfTest.java | 42 ++
.../SnapshotCreationNonDefaultStoragePathTest.java | 30 +-
.../filename/SnapshotExtraStoragesTest.java | 35 ++
.../filename/SnapshotFileTreeSelfTest.java | 687 +++++++++++++++++++++
.../ignite/testsuites/IgnitePdsTestSuite8.java | 4 +
9 files changed, 889 insertions(+), 31 deletions(-)
diff --git
a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
index b81b0d8bbc7..a6e7a2db23b 100644
---
a/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
+++
b/modules/core/src/main/java/org/apache/ignite/configuration/DataStorageConfiguration.java
@@ -216,14 +216,26 @@ public class DataStorageConfiguration implements
Serializable {
/**
* Additional directories where index and partition files are stored.
- * User may want to use dedicated storage for cache is server has several
physical disks.
+ * User may want to use dedicated storage for cache if server has several
physical disks.
* Spreading load across several disks can improve performance.
*
+ * @see #getStoragePath()
* @see CacheConfiguration#setStoragePaths(String...)
*/
@IgniteExperimental
private String[] extraStoragePaths;
+ /**
+ * Additional directories where snapshot files are stored.
+ * User may want to use dedicated storage for cache if server has several
physical disks.
+ * Spreading snapshot across several disks can improve performance.
+ *
+ * @see IgniteConfiguration#getSnapshotPath()
+ * @see CacheConfiguration#setStoragePaths(String...)
+ */
+ @IgniteExperimental
+ private String[] extraSnapshotPaths;
+
/** Checkpoint frequency. */
private long checkpointFreq = DFLT_CHECKPOINT_FREQ;
@@ -574,6 +586,14 @@ public class DataStorageConfiguration implements
Serializable {
return extraStoragePaths;
}
+ /**
+ * @return Additional directories for snapshots.
+ */
+ @IgniteExperimental
+ public String[] getExtraSnapshotPaths() {
+ return extraSnapshotPaths;
+ }
+
/**
* Sets a path to the root directory where the Persistent Store will
persist data and indexes.
* By default, the Persistent Store's files are located under Ignite work
directory.
@@ -601,6 +621,20 @@ public class DataStorageConfiguration implements
Serializable {
return this;
}
+ /**
+ * Sets a paths to the root directories where the snapshot files stored.
+ * By default, {@link IgniteConfiguration#getSnapshotPath()} used.
+ * Length of {@code extraSnapshotPaths} must be equal to the length of
{@link #getExtraStoragePaths()}.
+ *
+ * @param extraSnapshotPaths Extra snapshot paths where snapshot files can
be stored.
+ * @return {@code this} for chaining.
+ */
+ public DataStorageConfiguration setExtraSnapshotPaths(String...
extraSnapshotPaths) {
+ this.extraSnapshotPaths = extraSnapshotPaths;
+
+ return this;
+ }
+
/**
* Gets checkpoint frequency.
*
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
index ed5e0f55a99..2864009c84a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/IgnitionEx.java
@@ -1959,6 +1959,19 @@ public class IgnitionEx {
throw new IgniteCheckedException("DataStorageConfiguration
contains duplicates " +
"[storagePath=" + dsCfg.getStoragePath() + ",
extraStoragePaths=" + extraStorages + ']');
}
+
+ List<String> extraSnapshotStorages =
F.asList(dsCfg.getExtraSnapshotPaths());
+
+ if (!extraSnapshotStorages.isEmpty() && extraStorages.size()
!= extraSnapshotStorages.size()) {
+ throw new IgniteCheckedException("DataStorageConfiguration
error. " +
+ "Size of extraSnapshotPaths must be equal to
extraStoragePath " +
+ "[extraStoragePaths=" + extraStorages + ",
extraSnapshotPaths=" + extraSnapshotStorages + ']');
+ }
+
+ if (extraSnapshotStorages.size() != new
HashSet<>(extraSnapshotStorages).size()) {
+ throw new IgniteCheckedException("DataStorageConfiguration
contains duplicates " +
+ "[extraSnapshotPaths=" + extraSnapshotStorages + ']');
+ }
}
if (cfg.getMemoryConfiguration() != null ||
cfg.getPersistentStoreConfiguration() != null)
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/NodeFileTree.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/NodeFileTree.java
index dbf07261067..159eeb77db7 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/NodeFileTree.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/NodeFileTree.java
@@ -403,10 +403,7 @@ public class NodeFileTree extends SharedFileTree {
walCdc = rootRelative(DFLT_WAL_CDC_PATH);
}
- extraStorages = extraStorages(
- dsCfg,
- storagePath -> resolveDirectory(Path.of(storagePath,
DB_DIR).toString())
- );
+ extraStorages = extraStorages(dsCfg);
}
/** @return Node storage directory. */
@@ -989,12 +986,15 @@ public class NodeFileTree extends SharedFileTree {
* @return Node storages.
* @see DataStorageConfiguration#setExtraStoragePaths(String...)
*/
- private Map<String, File> extraStorages(@Nullable DataStorageConfiguration
dsCfg, Function<String, File> resolver) {
+ private Map<String, File> extraStorages(@Nullable DataStorageConfiguration
dsCfg) {
if (dsCfg == null || F.isEmpty(dsCfg.getExtraStoragePaths()))
return Collections.emptyMap();
return Arrays.stream(dsCfg.getExtraStoragePaths())
- .collect(Collectors.toMap(Function.identity(), resolver));
+ .collect(Collectors.toMap(
+ Function.identity(),
+ p -> resolveDirectory(Path.of(p, DB_DIR).toString()))
+ );
}
/**
@@ -1129,7 +1129,7 @@ public class NodeFileTree extends SharedFileTree {
* @param cfg Configured directory path.
* @return Initialized directory.
*/
- private File resolveDirectory(String cfg) {
+ protected File resolveDirectory(String cfg) {
File sharedDir = new File(cfg);
return sharedDir.isAbsolute()
diff --git
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTree.java
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTree.java
index 52b12a4d0db..f53abc94ba4 100644
---
a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTree.java
+++
b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTree.java
@@ -134,7 +134,7 @@ public class SnapshotFileTree extends NodeFileTree {
this.path = path;
this.consId = consId;
- Map<String, File> snpExtraStorages = snapshotExtraStorages(ft,
cfg.getSnapshotPath());
+ Map<String, File> snpExtraStorages = snapshotExtraStorages(ft, cfg);
if (snpExtraStorages.isEmpty())
extraStorages.clear();
@@ -380,27 +380,54 @@ public class SnapshotFileTree extends NodeFileTree {
* This will distribute workload to all physical device on host.
*
* @param ft Node file tree.
- * @param snpDfltPath Snapshot default path.
+ * @param cfg Ignite configuration.
*/
- private Map<String, File> snapshotExtraStorages(NodeFileTree ft, String
snpDfltPath) {
+ private Map<String, File> snapshotExtraStorages(NodeFileTree ft,
IgniteConfiguration cfg) {
+ DataStorageConfiguration dsCfg = cfg.getDataStorageConfiguration();
+
// If path provided then create snapshot inside it, only.
- // Same rule applies if absolute path to the snapshot root dir
configured.
- if (path != null || new File(snpDfltPath).isAbsolute())
+ if (path != null || dsCfg == null)
+ return Collections.emptyMap();
+
+ String[] extraStorages = dsCfg.getExtraStoragePaths();
+
+ if (extraStorages == null)
return Collections.emptyMap();
- Map<String, File> snpExtraStorages = new HashMap<>();
+ String[] extraSnpPaths = dsCfg.getExtraSnapshotPaths();
+
+ boolean hasExtraSnpPaths = extraSnpPaths != null &&
extraSnpPaths.length > 0;
- ft.extraStorages().forEach((cfgStoragePath, storagePath) -> {
- // In case we want to make snapshot in several folders the paths
will be the following:
- // {storage_path}/db/{folder_name} - node cache storage.
- // {storage_path}/snapshots/{snp_name}/db/{folder_name} - snapshot
cache storage.
- snpExtraStorages.put(
- cfgStoragePath,
- new File(storagePath.getParentFile().getParentFile(),
Path.of(snpDfltPath, name, DB_DIR, folderName()).toString())
- );
- });
+ String snpDfltPath = cfg.getSnapshotPath();
+
+ boolean snpDfltPathAbsolute = new File(snpDfltPath).isAbsolute();
+
+ // If absolute path to the snapshot root dir configured and no extra
snapshot paths, then use snapshot root, only.
+ if (snpDfltPathAbsolute && !hasExtraSnpPaths)
+ return Collections.emptyMap();
+
+ Map<String, File> snpRoots = new HashMap<>();
+
+ String snpUniqSuffix = snpDfltPathAbsolute
+ ? Path.of(name, DB_DIR, folderName()).toString()
+ : Path.of(snpDfltPath, name, DB_DIR, folderName()).toString();
+
+ for (int i = 0; i < extraStorages.length; i++) {
+ File snpRoot = hasExtraSnpPaths
+ // Extra snapshot paths configured as "root" related.
+ // folderName can be different from local (NodeFileTree ft)
one in case snapshot restored on smaller topology
+ // and data from some node copied to local.
+ // resolveDirectory returns in form
{root}/{extra_snp_path}/{folder_name} so parent required.
+ ? ft.resolveDirectory(extraSnpPaths[i]).getParentFile()
+ // In case we want to make snapshot in several folders the
paths will be the following:
+ // {storage_path}/db/{folder_name} - node cache storage.
+ // {storage_path} - snapshot root.
+ :
ft.extraStorages.get(extraStorages[i]).getParentFile().getParentFile();
+
+ snpRoots.put(extraStorages[i], new File(snpRoot, snpUniqSuffix));
+ }
- return snpExtraStorages;
+ return snpRoots;
}
/**
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CustomCacheStorageConfigurationSelfTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CustomCacheStorageConfigurationSelfTest.java
index 7a0527f7931..d908e83c925 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CustomCacheStorageConfigurationSelfTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CustomCacheStorageConfigurationSelfTest.java
@@ -112,6 +112,48 @@ public class CustomCacheStorageConfigurationSelfTest
extends GridCommonAbstractT
);
}
+ /** */
+ @Test
+ public void testIncorrectSnapshotPathsThrows() {
+ assertThrows(
+ log,
+ () -> startGrid(new
IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(myPath.getAbsolutePath())
+ .setExtraStoragePaths(myPath2.getAbsolutePath())
+ .setExtraSnapshotPaths("snppath1", "snppath2"))),
+ IgniteCheckedException.class,
+ "DataStorageConfiguration error. Size of extraSnapshotPaths must
be equal to extraStoragePath"
+ );
+
+ assertThrows(
+ log,
+ () -> startGrid(new
IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(myPath.getAbsolutePath())
+ .setExtraSnapshotPaths("snppath1"))),
+ IgniteCheckedException.class,
+ "DataStorageConfiguration error. Size of extraSnapshotPaths must
be equal to extraStoragePath"
+ );
+
+ assertThrows(
+ log,
+ () -> startGrid(new
IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(myPath.getAbsolutePath())
+ .setExtraSnapshotPaths("snppath1", "snppath2"))),
+ IgniteCheckedException.class,
+ "DataStorageConfiguration error. Size of extraSnapshotPaths must
be equal to extraStoragePath"
+ );
+
+ assertThrows(
+ log,
+ () -> startGrid(new
IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(myPath.getAbsolutePath())
+ .setExtraStoragePaths(myPath2.getAbsolutePath(),
myPath3.getAbsolutePath())
+ .setExtraSnapshotPaths("snppath1", "snppath1"))),
+ IgniteCheckedException.class,
+ "DataStorageConfiguration contains duplicates [extraSnapshotPaths="
+ );
+ }
+
/** */
@Test
public void testCacheUnknownStoragePathThrows() throws Exception {
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotCreationNonDefaultStoragePathTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotCreationNonDefaultStoragePathTest.java
index b670774e5b1..739f23d3c2a 100644
---
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotCreationNonDefaultStoragePathTest.java
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotCreationNonDefaultStoragePathTest.java
@@ -39,6 +39,19 @@ import static
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCaus
* Test snapshot can be created when {@link
DataStorageConfiguration#setStoragePath(String)} used.
*/
public class SnapshotCreationNonDefaultStoragePathTest extends
AbstractDataRegionRelativeStoragePathTest {
+ /** */
+ protected String[] extraSnpPaths = new String[] {
+ STORAGE_PATH_2,
+ IDX_PATH
+ };
+
+ /** {@inheritDoc} */
+ @Override protected void beforeTest() throws Exception {
+ super.beforeTest();
+
+ U.delete(new File(U.defaultWorkDirectory()));
+ }
+
/** {@inheritDoc} */
@Override protected DataStorageConfiguration dataStorageConfiguration() {
return new DataStorageConfiguration()
@@ -121,12 +134,14 @@ public class SnapshotCreationNonDefaultStoragePathTest
extends AbstractDataRegio
srv.context().cache().context().snapshotMgr().createSnapshot("mysnp2",
fullPathSnp.getAbsolutePath(), false, false).get();
- String grid1ConsId = consId(grid(1).configuration());
+ NodeFileTree ft = grid(1).context().pdsFolderResolver().fileTree();
stopGrid(1);
resetBaselineTopology();
+ ft.allStorages().forEach(U::delete);
+
BiConsumer<String, String> check = (name, path) -> {
for (CacheConfiguration<?, ?> ccfg : ccfgs())
grid(0).destroyCache(ccfg.getName());
@@ -155,15 +170,16 @@ public class SnapshotCreationNonDefaultStoragePathTest
extends AbstractDataRegio
"No snapshot metadatas found for the baseline nodes with
consistent ids: "
);
- Path[] copyStoppedNodeData = new Path[] {
- Path.of(DFLT_SNAPSHOT_DIRECTORY, "mysnp"),
- Path.of(STORAGE_PATH_2, DFLT_SNAPSHOT_DIRECTORY, "mysnp"),
- Path.of(IDX_PATH, DFLT_SNAPSHOT_DIRECTORY, "mysnp")
- };
+ Path[] copyStoppedNodeData = new Path[extraSnpPaths.length + 1];
+
+ copyStoppedNodeData[0] = Path.of(DFLT_SNAPSHOT_DIRECTORY, "mysnp");
+
+ for (int i = 0; i < extraSnpPaths.length; i++)
+ copyStoppedNodeData[i + 1] = Path.of(extraSnpPaths[i],
DFLT_SNAPSHOT_DIRECTORY, "mysnp");
for (Path copyPath : copyStoppedNodeData) {
FileUtils.copyDirectory(
- Path.of(U.defaultWorkDirectory(), grid1ConsId,
copyPath.toString()).toFile(),
+ Path.of(U.defaultWorkDirectory(), ft.folderName(),
copyPath.toString()).toFile(),
Path.of(U.defaultWorkDirectory(),
consId(grid(0).configuration()), copyPath.toString()).toFile()
);
}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotExtraStoragesTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotExtraStoragesTest.java
new file mode 100644
index 00000000000..4250e5dd7f4
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotExtraStoragesTest.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.ignite.internal.processors.cache.persistence.filename;
+
+import org.apache.ignite.configuration.DataStorageConfiguration;
+
+/**
+ * Test snapshot can be created when {@link
DataStorageConfiguration#setStoragePath(String)} used.
+ */
+public class SnapshotExtraStoragesTest extends
SnapshotCreationNonDefaultStoragePathTest {
+ /** {@inheritDoc} */
+ @Override protected DataStorageConfiguration dataStorageConfiguration() {
+ extraSnpPaths = new String[] {
+ "snp_path",
+ "snp_path2"
+ };
+
+ return
super.dataStorageConfiguration().setExtraSnapshotPaths(storagePath("snp_path"),
storagePath("snp_path2"));
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTreeSelfTest.java
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTreeSelfTest.java
new file mode 100644
index 00000000000..99bb32fddcf
--- /dev/null
+++
b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/SnapshotFileTreeSelfTest.java
@@ -0,0 +1,687 @@
+/*
+ * 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.ignite.internal.processors.cache.persistence.filename;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/** */
+public class SnapshotFileTreeSelfTest {
+ /** */
+ private static final String TEST_CONSISTENT_ID = "node1";
+
+ /** */
+ public String workDir;
+
+ /** */
+ public boolean snpAbsPath;
+
+ /** */
+ @Before
+ public void setUp() throws IgniteCheckedException {
+ workDir = U.defaultWorkDirectory();
+ snpAbsPath = false;
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ "/path/to/storage2",
+ "/path/to/storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ "/path/to/storage1/node1/",
+ "/path/to/storage2/db/node1/",
+ "/path/to/storage3/db/node1/"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ "/path/to/storage2/snapshots/snap/db/node1",
+ "/path/to/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ "/path/to/storage2/snapshots/snap/db/node2",
+ "/path/to/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ new String[]{
+ "/path/to/storage2",
+ "/path/to/storage3"
+ },
+ new String[]{
+ "/extra_snp2",
+ "/extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ "/extra_snp2/snapshots/snap/db/node1",
+ "/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ "/extra_snp2/snapshots/snap/db/node2",
+ "/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths2() {
+ IgniteConfiguration cfg = config(
+ "storage1",
+ "storage2",
+ "storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ workDir + "/storage1/node1",
+ workDir + "/storage2/db/node1",
+ workDir + "/storage3/db/node1"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/storage2/snapshots/snap/db/node1",
+ workDir + "/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/storage2/snapshots/snap/db/node2",
+ workDir + "/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths2_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "storage1",
+ new String[]{
+ "storage2",
+ "storage3"
+ },
+ new String[]{
+ "extra_snp2",
+ "extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/extra_snp2/snapshots/snap/db/node1",
+ workDir + "/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/extra_snp2/snapshots/snap/db/node2",
+ workDir + "/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths3() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ "storage2",
+ "storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ "/path/to/storage1/node1/",
+ workDir + "/storage2/db/node1",
+ workDir + "/storage3/db/node1"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/storage2/snapshots/snap/db/node1",
+ workDir + "/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/storage2/snapshots/snap/db/node2",
+ workDir + "/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths3_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ new String[]{
+ "storage2",
+ "storage3"
+ },
+ new String[]{
+ "extra_snp2",
+ "extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/extra_snp2/snapshots/snap/db/node1",
+ workDir + "/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/extra_snp2/snapshots/snap/db/node2",
+ workDir + "/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths4() {
+ IgniteConfiguration cfg = config(
+ "storage1",
+ "/path/to/storage2",
+ "/path/to/storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ workDir + "/storage1/node1/",
+ "/path/to/storage2/db/node1/",
+ "/path/to/storage3/db/node1/"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ "/path/to/storage2/snapshots/snap/db/node1",
+ "/path/to/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ "/path/to/storage2/snapshots/snap/db/node2",
+ "/path/to/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths4_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "storage1",
+ new String[]{
+ "/path/to/storage2",
+ "/path/to/storage3"
+ },
+ new String[]{
+ "/path/to/extra_snp2",
+ "/path/to/extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ "/path/to/extra_snp2/snapshots/snap/db/node1",
+ "/path/to/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ "/path/to/extra_snp2/snapshots/snap/db/node2",
+ "/path/to/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths5() {
+ IgniteConfiguration cfg = config(
+ null,
+ "/path/to/storage2",
+ "/path/to/storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ workDir + "/db/node1/",
+ "/path/to/storage2/db/node1/",
+ "/path/to/storage3/db/node1/"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ "/path/to/storage2/snapshots/snap/db/node1",
+ "/path/to/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ "/path/to/storage2/snapshots/snap/db/node2",
+ "/path/to/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths5_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ null,
+ new String[]{
+ "/path/to/storage2",
+ "/path/to/storage3"
+ },
+ new String[]{
+ "/path/to/extra_snp2",
+ "/path/to/extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ "/path/to/extra_snp2/snapshots/snap/db/node1",
+ "/path/to/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ "/path/to/extra_snp2/snapshots/snap/db/node2",
+ "/path/to/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths6() {
+ IgniteConfiguration cfg = config(
+ null,
+ "storage2",
+ "storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ workDir + "/db/node1/",
+ workDir + "/storage2/db/node1/",
+ workDir + "/storage3/db/node1/"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/storage2/snapshots/snap/db/node1",
+ workDir + "/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/storage2/snapshots/snap/db/node2",
+ workDir + "/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths6_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ null,
+ new String[]{
+ "storage2",
+ "storage3"
+ },
+ new String[]{
+ "extra_snp2",
+ "extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/extra_snp2/snapshots/snap/db/node1",
+ workDir + "/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/extra_snp2/snapshots/snap/db/node2",
+ workDir + "/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths7() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ "storage2",
+ "/path/to/storage3"
+ );
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ "/path/to/storage1/node1/",
+ workDir + "/storage2/db/node1",
+ "/path/to/storage3/db/node1"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/storage2/snapshots/snap/db/node1",
+ "/path/to/storage3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/storage2/snapshots/snap/db/node2",
+ "/path/to/storage3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testNamedSnapshotPaths7_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ new String[]{
+ "storage2",
+ "/path/to/storage3"
+ },
+ new String[]{
+ "extra_snp2",
+ "/path/to/extra_snp3"
+ }
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node1",
+ workDir + "/extra_snp2/snapshots/snap/db/node1",
+ "/path/to/extra_snp3/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ fileTree(cfg),
+ workDir + "/snapshots/snap/db/node2",
+ workDir + "/extra_snp2/snapshots/snap/db/node2",
+ "/path/to/extra_snp3/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testAbsoluteDeafultSnapshotPath() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ "/path/to/storage2",
+ "/path/to/storage3"
+ ).setSnapshotPath("/path/to/snapshots");
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotTempDirs(
+ ft,
+ "/path/to/storage1/node1/",
+ "/path/to/storage2/db/node1/",
+ "/path/to/storage3/db/node1/"
+ );
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ "/path/to/snapshots/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ "/path/to/snapshots/snap/db/node2"
+ );
+ }
+
+ /** */
+ @Test
+ public void testAbsoluteDeafultSnapshotPath_extraSnapshotPaths() {
+ IgniteConfiguration cfg = config(
+ "/path/to/storage1",
+ new String[]{
+ "/path/to/storage2",
+ "/path/to/storage3"
+ },
+ new String[]{
+ "/extra_snp2",
+ "/extra_snp3"
+ }
+ ).setSnapshotPath("/extra_snp");
+
+ NodeFileTree ft = fileTree(cfg);
+
+ checkSnapshotDirs(
+ cfg,
+ ft,
+ "/extra_snp/snap/db/node1",
+ "/extra_snp2/snap/db/node1",
+ "/extra_snp3/snap/db/node1"
+ );
+
+ checkSnapshotDirs(
+ "node2",
+ cfg,
+ ft,
+ "/extra_snp/snap/db/node2",
+ "/extra_snp2/snap/db/node2",
+ "/extra_snp3/snap/db/node2"
+ );
+ }
+
+ /** */
+ private NodeFileTree fileTree(IgniteConfiguration cfg) {
+ NodeFileTree ft = new NodeFileTree(cfg, TEST_CONSISTENT_ID);
+
+ if (new File(cfg.getSnapshotPath()).isAbsolute())
+ assertEquals(new File(cfg.getSnapshotPath()), ft.snapshotsRoot());
+ else
+ assertEquals(new File(workDir, cfg.getSnapshotPath()),
ft.snapshotsRoot());
+
+ return ft;
+ }
+
+ /** */
+ private IgniteConfiguration config(String storagePath, String...
extraPaths) {
+ return new IgniteConfiguration()
+ .setConsistentId(TEST_CONSISTENT_ID)
+ .setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(storagePath)
+ .setExtraStoragePaths(extraPaths)
+ .setDefaultDataRegionConfiguration(new
DataRegionConfiguration().setPersistenceEnabled(true)));
+ }
+
+ /** */
+ private IgniteConfiguration config(String storagePath, String[]
extraPaths, String[] extraSnpPaths) {
+ return new IgniteConfiguration()
+ .setConsistentId(TEST_CONSISTENT_ID)
+ .setDataStorageConfiguration(new DataStorageConfiguration()
+ .setStoragePath(storagePath)
+ .setExtraStoragePaths(extraPaths)
+ .setExtraSnapshotPaths(extraSnpPaths)
+ .setDefaultDataRegionConfiguration(new
DataRegionConfiguration().setPersistenceEnabled(true)));
+ }
+
+ /** */
+ private void checkSnapshotTempDirs(NodeFileTree ft, String...
expStoragesStr) {
+ List<File> expStorages =
Arrays.stream(expStoragesStr).map(File::new).collect(Collectors.toList());
+ Set<File> ftStorages = ft.allStorages().collect(Collectors.toSet());
+
+ assertEquals(expStorages.size(), ftStorages.size());
+ assertTrue(
+ ftStorages + " must contains all " + expStorages,
+ ftStorages.containsAll(expStorages)
+ );
+
+ List<File> snpTmps = ft.snapshotsTempRoots();
+
+ assertEquals(expStorages.size(), snpTmps.size());
+ assertTrue(snpTmps.containsAll(
+ expStorages.stream().map(s -> new File(s,
NodeFileTree.SNAPSHOT_TMP_DIR)).collect(Collectors.toList())));
+ }
+
+ /** */
+ private void checkSnapshotDirs(IgniteConfiguration cfg, NodeFileTree ft,
String... expSnpStoragesStr) {
+ checkSnapshotDirs(ft.folderName(), cfg, ft, expSnpStoragesStr);
+ }
+
+ /** */
+ private void checkSnapshotDirs(String folderName, IgniteConfiguration cfg,
NodeFileTree ft, String... expSnpStoragesStr) {
+ SnapshotFileTree sft = new SnapshotFileTree(cfg, ft, "snap",
snpAbsPath ? "/snpdir" : null, folderName, TEST_CONSISTENT_ID);
+
+ Set<File> snpStorages = sft.allStorages().collect(Collectors.toSet());
+ List<File> expSnpStorages =
Arrays.stream(expSnpStoragesStr).map(File::new).collect(Collectors.toList());
+
+ assertEquals(expSnpStorages.size(), snpStorages.size());
+ assertTrue(expSnpStorages + " must contains all " + snpStorages,
expSnpStorages.containsAll(snpStorages));
+
+ // With absolute path any configuration must return one snapshot dir.
+ if (!snpAbsPath) {
+ snpAbsPath = true;
+
+ try {
+ checkSnapshotDirs(
+ folderName,
+ cfg,
+ ft,
+ "/snpdir/snap/db/" + folderName
+ );
+ }
+ finally {
+ snpAbsPath = false;
+ }
+ }
+ }
+}
diff --git
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite8.java
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite8.java
index 914156bb9af..a15208e8ac1 100644
---
a/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite8.java
+++
b/modules/core/src/test/java/org/apache/ignite/testsuites/IgnitePdsTestSuite8.java
@@ -51,6 +51,8 @@ import
org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelo
import
org.apache.ignite.internal.processors.cache.persistence.filename.CacheConfigStoragePathTest;
import
org.apache.ignite.internal.processors.cache.persistence.filename.CustomCacheStorageConfigurationSelfTest;
import
org.apache.ignite.internal.processors.cache.persistence.filename.SnapshotCreationNonDefaultStoragePathTest;
+import
org.apache.ignite.internal.processors.cache.persistence.filename.SnapshotExtraStoragesTest;
+import
org.apache.ignite.internal.processors.cache.persistence.filename.SnapshotFileTreeSelfTest;
import
org.apache.ignite.internal.processors.cache.persistence.filename.SnapshotRestoreIndexPathTest;
import
org.apache.ignite.internal.processors.cache.warmup.LoadAllWarmUpStrategySelfTest;
import org.apache.ignite.internal.processors.cache.warmup.WarmUpSelfTest;
@@ -115,8 +117,10 @@ public class IgnitePdsTestSuite8 {
GridTestUtils.addTestIfNeeded(suite,
IgnitePdsCheckpointRecoveryTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, CacheConfigStoragePathTest.class,
ignoredTests);
GridTestUtils.addTestIfNeeded(suite,
SnapshotCreationNonDefaultStoragePathTest.class, ignoredTests);
+ GridTestUtils.addTestIfNeeded(suite, SnapshotExtraStoragesTest.class,
ignoredTests);
GridTestUtils.addTestIfNeeded(suite,
SnapshotRestoreIndexPathTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite,
CustomCacheStorageConfigurationSelfTest.class, ignoredTests);
+ GridTestUtils.addTestIfNeeded(suite, SnapshotFileTreeSelfTest.class,
ignoredTests);
return suite;
}