You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ni...@apache.org on 2025/05/20 08:07:33 UTC

(ignite) branch master updated: IGNITE-25305 Per cache custom storagePath configuration (#12060)

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 34493a1168b IGNITE-25305 Per cache custom storagePath configuration (#12060)
34493a1168b is described below

commit 34493a1168b93d1be6f142dfdabfb40959e5230b
Author: Nikolay <ni...@apache.org>
AuthorDate: Tue May 20 11:07:26 2025 +0300

    IGNITE-25305 Per cache custom storagePath configuration (#12060)
---
 .../ignite/configuration/CacheConfiguration.java   |  31 ++++
 .../configuration/DataRegionConfiguration.java     |  23 ---
 .../configuration/DataStorageConfiguration.java    |  33 ++++
 .../org/apache/ignite/internal/IgnitionEx.java     |  21 ++-
 .../processors/cache/ClusterCachesInfo.java        |   4 +
 .../cache/ValidationOnNodeJoinUtils.java           |  18 ++
 .../cache/persistence/filename/FileTreeUtils.java  |   4 +-
 .../cache/persistence/filename/NodeFileTree.java   | 102 ++++-------
 .../persistence/filename/SnapshotFileTree.java     |  47 +++--
 .../snapshot/IgniteSnapshotManager.java            |  26 +--
 .../snapshot/IncrementalSnapshotFutureTask.java    |   2 +-
 .../persistence/snapshot/SnapshotFutureTask.java   |   8 +-
 .../snapshot/SnapshotResponseRemoteFutureTask.java |   2 +-
 .../cache/persistence/snapshot/SnapshotSender.java |  12 +-
 .../AbstractDataRegionRelativeStoragePathTest.java |  52 +++---
 .../filename/CacheConfigStoragePathTest.java       | 203 +++++++++++++++++++++
 .../CustomCacheStorageConfigurationSelfTest.java   | 172 +++++++++++++++++
 .../DataRegionRelativeStoragePathTest.java         | 144 ---------------
 .../SnapshotCreationNonDefaultStoragePathTest.java |  21 +--
 .../snapshot/AbstractSnapshotSelfTest.java         |   4 +-
 .../snapshot/IgniteClusterSnapshotDeltaTest.java   |   4 +-
 .../snapshot/IgniteSnapshotManagerSelfTest.java    |  12 +-
 .../snapshot/IgniteSnapshotRemoteRequestTest.java  |   8 +-
 .../IgniteSnapshotRestoreFromRemoteTest.java       |   8 +-
 .../snapshot/dump/AbstractCacheDumpTest.java       |  11 +-
 .../snapshot/dump/IgniteCacheDumpSelf2Test.java    |  13 +-
 .../ignite/testsuites/IgnitePdsTestSuite8.java     |   6 +-
 27 files changed, 639 insertions(+), 352 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
index b5a98336d79..4753ac26536 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/CacheConfiguration.java
@@ -434,6 +434,14 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
     /** */
     private Integer diskPageCompressionLevel;
 
+    /**
+     * Root directories where partition files are stored.
+     * @see DataStorageConfiguration#setStoragePath(String)
+     * @see DataStorageConfiguration#setExtraStoragePathes(String[])
+     */
+    @IgniteExperimental
+    @Nullable private String storagePath;
+
     /** Empty constructor (all values are initialized to their defaults). */
     public CacheConfiguration() {
         /* No-op. */
@@ -532,6 +540,7 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         sqlOnheapCache = cc.isSqlOnheapCacheEnabled();
         sqlOnheapCacheMaxSize = cc.getSqlOnheapCacheMaxSize();
         evtsDisabled = cc.isEventsDisabled();
+        storagePath = cc.getStoragePath();
     }
 
     /**
@@ -2457,6 +2466,28 @@ public class CacheConfiguration<K, V> extends MutableConfiguration<K, V> {
         return this;
     }
 
+    /**
+     * @return A path to the root directory where the Persistent Store for cache group will persist data and indexes.
+     */
+    @IgniteExperimental
+    @Nullable public String getStoragePath() {
+        return storagePath;
+    }
+
+    /**
+     * Sets a path to the root directory where the Persistent Store will persist data.
+     * By default, the Persistent Store's files are located under {@link DataStorageConfiguration#getStoragePath()}.
+     *
+     * @param storagePath Persistence store path.
+     * @return {@code this} for chaining.
+     */
+    @IgniteExperimental
+    public CacheConfiguration<K, V> setStoragePath(String storagePath) {
+        this.storagePath = storagePath;
+
+        return this;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(CacheConfiguration.class, this);
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
index 396ca4d6331..1293ed79894 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/DataRegionConfiguration.java
@@ -158,9 +158,6 @@ public final class DataRegionConfiguration implements Serializable {
     /** Change Data Capture enabled flag. */
     private boolean cdcEnabled;
 
-    /** Directory where index and partition files are stored. */
-    @Nullable private String storagePath;
-
     /**
      * Gets data region name.
      *
@@ -583,26 +580,6 @@ public final class DataRegionConfiguration implements Serializable {
         return cdcEnabled;
     }
 
-    /**
-     * @return A path to the root directory where the Persistent Store for data region will persist data and indexes.
-     */
-    @Nullable public String getStoragePath() {
-        return storagePath;
-    }
-
-    /**
-     * 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.
-     *
-     * @param storagePath Persistence store path.
-     * @return {@code this} for chaining.
-     */
-    public DataRegionConfiguration setStoragePath(String storagePath) {
-        this.storagePath = storagePath;
-
-        return this;
-    }
-
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(DataRegionConfiguration.class, this);
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 043976261ea..2f9683ffb2b 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
@@ -27,6 +27,7 @@ import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.internal.A;
 import org.apache.ignite.internal.util.typedef.internal.S;
 import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.lang.IgniteExperimental;
 import org.apache.ignite.mem.MemoryAllocator;
 import org.apache.ignite.mxbean.MetricsMxBean;
 import org.jetbrains.annotations.Nullable;
@@ -213,6 +214,16 @@ public class DataStorageConfiguration implements Serializable {
     /** Directory where index and partition files are stored. */
     private String storagePath;
 
+    /** 
+     * Additional directories where index and partition files are stored.
+     * User may want to use dedicated storage for cache is server has several physical disks.
+     * Spreading load across several disks can improve performance.
+     *
+     * @see CacheConfiguration#setStoragePath(String)
+     */
+    @IgniteExperimental
+    private String[] extraStoragePathes;
+
     /** Checkpoint frequency. */
     private long checkpointFreq = DFLT_CHECKPOINT_FREQ;
 
@@ -555,6 +566,14 @@ public class DataStorageConfiguration implements Serializable {
         return storagePath;
     }
 
+    /**
+     * @return Additional directories where index and partition files are stored.
+     */
+    @IgniteExperimental
+    public String[] getExtraStoragePathes() {
+        return extraStoragePathes;
+    }
+
     /**
      * 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.
@@ -562,12 +581,26 @@ public class DataStorageConfiguration implements Serializable {
      * @param persistenceStorePath Persistence store path.
      * @return {@code this} for chaining.
      */
+    @IgniteExperimental
     public DataStorageConfiguration setStoragePath(String persistenceStorePath) {
         this.storagePath = persistenceStorePath;
 
         return this;
     }
 
+    /**
+     * Sets a pathes to the root directories where the Persistent Store can persist data and indexes.
+     * By default, {@link #getStoragePath()} used.
+     *
+     * @param extraStoragePathes Extra storage pathes where persistent data can be stored.
+     * @return {@code this} for chaining.
+     */
+    public DataStorageConfiguration setExtraStoragePathes(String... extraStoragePathes) {
+        this.extraStoragePathes = extraStoragePathes;
+
+        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 867aaf81f5c..1abb7f34644 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
@@ -1943,11 +1943,22 @@ public class IgnitionEx {
          * @param cfg Ignite configuration.
          */
         private void initializeDataStorageConfiguration(IgniteConfiguration cfg) throws IgniteCheckedException {
-            if (cfg.getDataStorageConfiguration() != null &&
-                (cfg.getMemoryConfiguration() != null || cfg.getPersistentStoreConfiguration() != null)) {
-                throw new IgniteCheckedException("Data storage can be configured with either legacy " +
-                    "(MemoryConfiguration, PersistentStoreConfiguration) or new (DataStorageConfiguration) classes, " +
-                    "but not both.");
+            DataStorageConfiguration dsCfg = cfg.getDataStorageConfiguration();
+
+            if (dsCfg != null) {
+                if (cfg.getMemoryConfiguration() != null || cfg.getPersistentStoreConfiguration() != null) {
+                    throw new IgniteCheckedException("Data storage can be configured with either legacy " +
+                        "(MemoryConfiguration, PersistentStoreConfiguration) or new (DataStorageConfiguration) classes, " +
+                        "but not both.");
+                }
+
+                List<String> extraStorages = F.asList(dsCfg.getExtraStoragePathes());
+
+                if (extraStorages.size() != new HashSet<>(extraStorages).size()
+                    || extraStorages.contains(dsCfg.getStoragePath())) {
+                    throw new IgniteCheckedException("DataStorageConfiguration contains duplicates " +
+                        "[storagePath=" + dsCfg.getStoragePath() + ", extraStoragePathes=" + extraStorages + ']');
+                }
             }
 
             if (cfg.getMemoryConfiguration() != null || cfg.getPersistentStoreConfiguration() != null)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
index cb36cfdb1c7..ae9f5d525a1 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
@@ -2591,6 +2591,10 @@ public class ClusterCachesInfo {
         CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg,
             "diskPageCompressionLevel", "Disk page compression level",
             cfg.getDiskPageCompressionLevel(), startCfg.getDiskPageCompressionLevel(), true);
+
+        CU.validateCacheGroupsAttributesMismatch(log, cfg, startCfg,
+            "storagePath", "Storage path",
+            cfg.getStoragePath(), startCfg.getStoragePath(), true);
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ValidationOnNodeJoinUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ValidationOnNodeJoinUtils.java
index 750f7b96f0e..45e3aa4f695 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ValidationOnNodeJoinUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ValidationOnNodeJoinUtils.java
@@ -389,6 +389,24 @@ public class ValidationOnNodeJoinUtils {
                 throw new IgniteCheckedException("Encryption cannot be used with disk page compression " +
                     cacheSpec.toString());
         }
+
+        if (!F.isEmpty(cc.getStoragePath())) {
+            DataStorageConfiguration dsCfg = c.getDataStorageConfiguration();
+
+            String cs = cc.getStoragePath();
+
+            if (dsCfg == null)
+                throw new IgniteCheckedException("Data storage must be configured when cache storage path set: " + cs);
+
+            List<String> extraNodeStorages = F.asList(dsCfg.getExtraStoragePathes());
+
+            if (!extraNodeStorages.contains(cs) && !Objects.equals(cs, dsCfg.getStoragePath())) {
+                throw new IgniteCheckedException(
+                    "Unknown storage path. Storage path must be from DataStorageConfiguration " +
+                        "[cacheStorage=" + cs + ", nodeStorages=" + extraNodeStorages + ']'
+                );
+            }
+        }
     }
 
     /**
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java
index c6f12258107..ef682ed0ee9 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/filename/FileTreeUtils.java
@@ -41,7 +41,7 @@ public class FileTreeUtils {
     public static void createCacheStorages(NodeFileTree ft, IgniteLogger log) throws IgniteCheckedException {
         createAndCheck(ft.nodeStorage(), "page store work directory", log);
 
-        for (Map.Entry<String, File> e : ft.dataRegionStorages().entrySet())
+        for (Map.Entry<String, File> e : ft.extraStorages().entrySet())
             createAndCheck(e.getValue(), "page store work directory [dataRegion=" + e.getKey() + ']', log);
     }
 
@@ -57,7 +57,7 @@ public class FileTreeUtils {
 
         removeTmpDir(tmpFt.root(), err, log);
 
-        for (File tmpDrStorage : tmpFt.dataRegionStorages().values())
+        for (File tmpDrStorage : tmpFt.extraStorages().values())
             removeTmpDir(tmpDrStorage.getParentFile(), err, log);
     }
 
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 0f975512f30..3e23564255e 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
@@ -23,16 +23,14 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.BiFunction;
+import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.ignite.IgniteException;
 import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.processors.cache.persistence.metastorage.MetaStorage;
@@ -243,18 +241,10 @@ public class NodeFileTree extends SharedFileTree {
     private final File nodeStorage;
 
     /**
-     * Key is the name of data region({@link DataRegionConfiguration#getName()}), value is node storage for this data region.
-     * @see DataRegionConfiguration#setStoragePath(String)
+     * Key is the path from {@link DataStorageConfiguration#getExtraStoragePathes()}, may be relative. Value is storage.
+     * @see DataStorageConfiguration#getExtraStoragePathes()
      */
-    protected final Map<String, File> drStorages;
-
-    /**
-     * Name of the default data region.
-     * @see DataStorageConfiguration#DFLT_DATA_REG_DEFAULT_NAME
-     * @see DataStorageConfiguration#setDefaultDataRegionConfiguration(DataRegionConfiguration)
-     * @see DataRegionConfiguration#setName(String)
-     */
-    private final String dfltDrName;
+    protected final Map<String, File> extraStorages;
 
     /** Path to the checkpoint directory. */
     private final File checkpoint;
@@ -294,8 +284,7 @@ public class NodeFileTree extends SharedFileTree {
         wal = rootRelative(DFLT_WAL_PATH);
         walArchive = rootRelative(DFLT_WAL_ARCHIVE_PATH);
         walCdc = rootRelative(DFLT_WAL_CDC_PATH);
-        dfltDrName = DataStorageConfiguration.DFLT_DATA_REG_DEFAULT_NAME;
-        drStorages = Collections.emptyMap();
+        extraStorages = Collections.emptyMap();
     }
 
     /**
@@ -342,6 +331,7 @@ public class NodeFileTree extends SharedFileTree {
         DataStorageConfiguration dsCfg = cfg.getDataStorageConfiguration();
 
         if (CU.isPersistenceEnabled(cfg) || CU.isCdcEnabled(cfg)) {
+            // Snapshots MUST use root relative node storage path.
             nodeStorage = (dsCfg.getStoragePath() == null || isSnapshot)
                 ? rootRelative(DB_DIR)
                 : resolveDirectory(dsCfg.getStoragePath());
@@ -349,7 +339,6 @@ public class NodeFileTree extends SharedFileTree {
             wal = resolveDirectory(dsCfg.getWalPath());
             walArchive = resolveDirectory(dsCfg.getWalArchivePath());
             walCdc = resolveDirectory(dsCfg.getCdcWalPath());
-            dfltDrName = dsCfg.getDefaultDataRegionConfiguration().getName();
         }
         else {
             nodeStorage = rootRelative(DB_DIR);
@@ -357,12 +346,11 @@ public class NodeFileTree extends SharedFileTree {
             wal = rootRelative(DFLT_WAL_PATH);
             walArchive = rootRelative(DFLT_WAL_ARCHIVE_PATH);
             walCdc = rootRelative(DFLT_WAL_CDC_PATH);
-            dfltDrName = DataStorageConfiguration.DFLT_DATA_REG_DEFAULT_NAME;
         }
 
-        drStorages = dataRegionStorages(
+        extraStorages = extraStorages(
             dsCfg,
-            (drName, drStoragePath) -> resolveDirectory(Path.of(drStoragePath, DB_DIR).toString())
+            storagePath -> resolveDirectory(Path.of(storagePath, DB_DIR).toString())
         );
     }
 
@@ -371,9 +359,9 @@ public class NodeFileTree extends SharedFileTree {
         return nodeStorage;
     }
 
-    /** @return Storages for data regions. */
-    public Map<String, File> dataRegionStorages() {
-        return drStorages;
+    /** @return Extra storages. */
+    public Map<String, File> extraStorages() {
+        return extraStorages;
     }
 
     /** @return Folder name. */
@@ -451,7 +439,7 @@ public class NodeFileTree extends SharedFileTree {
 
     /** @return Path to the directories for temp snapshot files. */
     public List<File> snapshotsTempRoots() {
-        return Stream.concat(Stream.of(nodeStorage), drStorages.values().stream())
+        return Stream.concat(Stream.of(nodeStorage), extraStorages.values().stream())
             .map(this::snapshotTempRoot)
             .collect(Collectors.toList());
     }
@@ -497,7 +485,7 @@ public class NodeFileTree extends SharedFileTree {
      * @return Store dir for given cache.
      */
     public File cacheStorage(CacheConfiguration<?, ?> ccfg) {
-        return new File(dataRegionStorage(ccfg.getDataRegionName()), ccfg.getGroupName() != null
+        return new File(cacheStorage(ccfg.getStoragePath()), ccfg.getGroupName() != null
             ? CACHE_GRP_DIR_PREFIX + ccfg.getGroupName()
             : CACHE_DIR_PREFIX + ccfg.getName());
     }
@@ -574,12 +562,13 @@ public class NodeFileTree extends SharedFileTree {
      * Temporary cache storage and partitions are created while snapshot restoring.
      * Moving to regular cache storage when finished.
      *
-     * @param drName Data region name.
+     * @param storagePath Cache storage path.
      * @param cacheDirName Cache directory name.
      * @return Temp store directory for given cache.
+     * @see CacheConfiguration#getStoragePath()
      */
-    public File tmpCacheStorage(@Nullable String drName, String cacheDirName) {
-        return new File(dataRegionStorage(drName), TMP_CACHE_DIR_PREFIX + cacheDirName);
+    public File tmpCacheStorage(@Nullable String storagePath, String cacheDirName) {
+        return new File(cacheStorage(storagePath), TMP_CACHE_DIR_PREFIX + cacheDirName);
     }
 
     /**
@@ -620,13 +609,14 @@ public class NodeFileTree extends SharedFileTree {
     /**
      * Temporary cache partitions are created while snapshot restoring.
      *
-     * @param drName Data region name.
+     * @param storagePath Cache storage path.
      * @param cacheDirName Cache directory name.
      * @param partId partition id.
      * @return Path to the temp partition file.
+     * @see CacheConfiguration#getStoragePath()
      */
-    public File tmpPartition(@Nullable String drName, String cacheDirName, int partId) {
-        return new File(tmpCacheStorage(drName, cacheDirName), partitionFileName(partId));
+    public File tmpPartition(@Nullable String storagePath, String cacheDirName, int partId) {
+        return new File(tmpCacheStorage(storagePath, cacheDirName), partitionFileName(partId));
     }
 
     /** */
@@ -773,42 +763,28 @@ public class NodeFileTree extends SharedFileTree {
     }
 
     /**
-     * @param drName Data region name.
-     * @return Data region storage.
+     * @param storagePath Value from config.
+     * @return File storage.
+     * @see CacheConfiguration#getStoragePath()
      */
-    private File dataRegionStorage(@Nullable String drName) {
-        return drStorages.getOrDefault(drName == null ? dfltDrName : drName, nodeStorage);
+    private File cacheStorage(@Nullable String storagePath) {
+        return storagePath == null ? nodeStorage : extraStorages.getOrDefault(storagePath, nodeStorage);
     }
 
     /**
-     * Key is data region name.
-     * Value is data region storage.
+     * Key is storage path from config, may be relative.
+     * Value is actual storage path.
      *
      * @param dsCfg Data storage configuration.
-     * @return Data regions storages.
-     * @see DataRegionConfiguration#setStoragePath(String)
+     * @return Node storages.
+     * @see DataStorageConfiguration#setExtraStoragePathes(String...)
      */
-    private Map<String, File> dataRegionStorages(@Nullable DataStorageConfiguration dsCfg, BiFunction<String, String, File> resolver) {
-        if (dsCfg == null)
+    private Map<String, File> extraStorages(@Nullable DataStorageConfiguration dsCfg, Function<String, File> resolver) {
+        if (dsCfg == null || F.isEmpty(dsCfg.getExtraStoragePathes()))
             return Collections.emptyMap();
 
-        Map<String, File> customDsStorages = new HashMap<>();
-
-        if (dsCfg.getDataRegionConfigurations() != null) {
-            for (DataRegionConfiguration drCfg : dsCfg.getDataRegionConfigurations()) {
-                if (drCfg.getStoragePath() == null)
-                    continue;
-
-                customDsStorages.put(drCfg.getName(), resolver.apply(drCfg.getName(), drCfg.getStoragePath()));
-            }
-        }
-
-        DataRegionConfiguration dfltDr = dsCfg.getDefaultDataRegionConfiguration();
-
-        if (dfltDr.getStoragePath() != null)
-            customDsStorages.put(dfltDr.getName(), resolver.apply(dfltDr.getName(), dfltDr.getStoragePath()));
-
-        return customDsStorages;
+        return Arrays.stream(dsCfg.getExtraStoragePathes())
+            .collect(Collectors.toMap(Function.identity(), resolver));
     }
 
     /**
@@ -849,15 +825,15 @@ public class NodeFileTree extends SharedFileTree {
 
     /**
      * @param filter Dir file filter.
-     * @return All files from all {@link #drStorages} matching the filter.
+     * @return All files from all {@link #extraStorages} and {@link #nodeStorage} matching the filter.
      */
     private Stream<File> filesInStorages(FileFilter filter) {
-        return Stream.concat(Stream.of(nodeStorage), drStorages.values().stream()).flatMap(drStorage -> {
-            File[] drStorageFiles = drStorage.listFiles(filter);
+        return Stream.concat(Stream.of(nodeStorage), extraStorages.values().stream()).flatMap(storage -> {
+            File[] storageFiles = storage.listFiles(filter);
 
-            return drStorageFiles == null
+            return storageFiles == null
                 ? Stream.empty()
-                : Arrays.stream(drStorageFiles);
+                : Arrays.stream(storageFiles);
         });
     }
 
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 42a4b91ed05..a819e0b69e1 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
@@ -26,7 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.dump.DumpReader;
 import org.apache.ignite.internal.GridKernalContext;
@@ -133,12 +133,12 @@ public class SnapshotFileTree extends NodeFileTree {
         this.path = path;
         this.consId = consId;
 
-        Map<String, File> snpDrStorages = snapshotDataRegionStorages(ft, cfg.getSnapshotPath());
+        Map<String, File> snpExtraStorages = snapshotExtraStorages(ft, cfg.getSnapshotPath());
 
-        if (snpDrStorages.isEmpty())
-            drStorages.clear();
+        if (snpExtraStorages.isEmpty())
+            extraStorages.clear();
         else
-            drStorages.putAll(snpDrStorages);
+            extraStorages.putAll(snpExtraStorages);
 
         this.tmpFt = null;
     }
@@ -373,37 +373,34 @@ public class SnapshotFileTree extends NodeFileTree {
     }
 
     /**
-     * Modifies {@link #drStorages} for this tree to reflect snapshot options.
+     * Modifies {@link #extraStorages} for this tree to reflect snapshot options.
      * In case {@link IgniteConfiguration#getSnapshotPath()} points to absolute directory or {@link #path} for snapshot provided
      * then all snapshot files must be stored inside one folder.
-     * Otherwise, we use configured by {@link DataRegionConfiguration#getStoragePath()} structure to save snapshot.
+     * Otherwise, we use configured by {@link DataStorageConfiguration#getExtraStoragePathes()} structure to save snapshot.
      * This will distribute workload to all physical device on host.
      *
      * @param ft Node file tree.
      * @param snpDfltPath Snapshot default path.
      */
-    private Map<String, File> snapshotDataRegionStorages(NodeFileTree ft, String snpDfltPath) {
+    private Map<String, File> snapshotExtraStorages(NodeFileTree ft, String snpDfltPath) {
         // 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())
             return Collections.emptyMap();
 
-        Map<String, File> snpDrStorages = new HashMap<>();
-
-        ft.dataRegionStorages().forEach((drName, drStoragePath) -> {
-            // drStorages contains path with the DB and folderName.
-            File drStorage = ft.dataRegionStorages().get(drName);
+        Map<String, File> snpExtraStorages = new HashMap<>();
 
+        ft.extraStorages().forEach((cfgStoragePath, storagePath) -> {
             // In case we want to make snapshot in several folders the pathes will be the following:
-            // {dr_storage_path}/db/{folder_name} - node cache storage.
-            // {dr_storage_path}/snapshots/{snp_name}/db/{folder_name} - snapshot cache storage.
-            snpDrStorages.put(
-                drName,
-                new File(drStorage.getParentFile().getParentFile(), Path.of(snpDfltPath, name, DB_DIR, folderName()).toString())
+            // {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())
             );
         });
 
-        return snpDrStorages;
+        return snpExtraStorages;
     }
 
     /**
@@ -419,16 +416,16 @@ public class SnapshotFileTree extends NodeFileTree {
 
         NodeFileTree res = new NodeFileTree(ctx.config(), new File(ft.snapshotTempRoot(ft.nodeStorage()), name), folderName(), true);
 
-        Map<String, File> snpTmpDrStorages = new HashMap<>();
+        Map<String, File> snpTmpExtraStorages = new HashMap<>();
 
-        // Iterating via snapshot data region storage,
+        // Iterating via snapshot extra storages,
         // because they may differ from the node one in case snapshot created using absolute path.
-        ft.drStorages.forEach((drName, drStoragePath) -> snpTmpDrStorages.put(
-            drName,
-            new File(ft.dataRegionStorages().get(drName), Path.of(SNAPSHOT_TMP_DIR, name).toString())
+        ft.extraStorages.forEach((cfgStoragePath, storagePath) -> snpTmpExtraStorages.put(
+            cfgStoragePath,
+            new File(storagePath, Path.of(SNAPSHOT_TMP_DIR, name).toString())
         ));
 
-        res.drStorages.putAll(snpTmpDrStorages);
+        res.extraStorages.putAll(snpTmpExtraStorages);
 
         return res;
     }
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
index 7afbe4562a6..90dca55b18a 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManager.java
@@ -307,8 +307,11 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
     /** File transmission parameter of a cache directory with is currently sends its partitions. */
     private static final String SNP_CACHE_DIR_NAME_PARAM = "cacheDirName";
 
-    /** File transmission parameter of a data region for given group id. */
-    private static final String SNP_CACHE_DR_NAME_PARAM = "cacheDrName";
+    /**
+     * File transmission parameter of a storage path for given group id.
+     * @see CacheConfiguration#getStoragePath()
+     */
+    private static final String SNP_CACHE_STORAGE_PATH_PARAM = "cacheStoragePath";
 
     /** Snapshot parameter name for a file transmission. */
     private static final String RQ_ID_NAME_PARAM = "rqId";
@@ -3611,7 +3614,7 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
         @Override public String filePath(UUID nodeId, TransmissionMeta fileMeta) {
             Integer partId = (Integer)fileMeta.params().get(SNP_PART_ID_PARAM);
             String cacheDirName = (String)fileMeta.params().get(SNP_CACHE_DIR_NAME_PARAM);
-            String cacheDrName = (String)fileMeta.params().get(SNP_CACHE_DR_NAME_PARAM);
+            String cacheStoragePath = (String)fileMeta.params().get(SNP_CACHE_STORAGE_PATH_PARAM);
             String rqId = (String)fileMeta.params().get(RQ_ID_NAME_PARAM);
             Integer partsCnt = (Integer)fileMeta.params().get(SNP_PARTITIONS_CNT);
 
@@ -3630,9 +3633,9 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
             try {
                 task.partsLeft.compareAndSet(-1, partsCnt);
 
-                U.mkdirs(ft.tmpCacheStorage(cacheDrName, cacheDirName));
+                U.mkdirs(ft.tmpCacheStorage(cacheStoragePath, cacheDirName));
 
-                return ft.tmpPartition(cacheDrName, cacheDirName, partId).getAbsolutePath();
+                return ft.tmpPartition(cacheStoragePath, cacheDirName, partId).getAbsolutePath();
             }
             finally {
                 busyLock.leaveBusy();
@@ -3720,7 +3723,7 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
         }
 
         /** {@inheritDoc} */
-        @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long len) {
+        @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long len) {
             File snpCacheDir = to.getParentFile();
 
             try {
@@ -3728,7 +3731,7 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
                 assert len > 0 : "Requested partitions has incorrect file length " +
                     "[pair=" + pair + ", cacheDirName=" + snpCacheDir.getName() + ']';
 
-                sndr.send(from, 0, len, transmissionParams(rqId, snpCacheDir.getName(), drName, pair), TransmissionPolicy.FILE);
+                sndr.send(from, 0, len, transmissionParams(rqId, snpCacheDir.getName(), storagePath, pair), TransmissionPolicy.FILE);
 
                 if (log.isInfoEnabled()) {
                     log.info("Partition file has been sent [part=" + from.getName() + ", pair=" + pair +
@@ -3757,14 +3760,15 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
         /**
          * @param rqId Request id.
          * @param cacheDirName Cache directory name.
-         * @param drName Data region name.
+         * @param storagePath Storage path.
          * @param pair Cache group id with corresponding partition id.
          * @return Map of params.
+         * @see CacheConfiguration#getStoragePath()
          */
         private Map<String, Serializable> transmissionParams(
             String rqId,
             String cacheDirName,
-            @Nullable String drName,
+            @Nullable String storagePath,
             GroupPartitionId pair
         ) {
             Map<String, Serializable> params = new HashMap<>();
@@ -3772,7 +3776,7 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
             params.put(SNP_GRP_ID_PARAM, pair.getGroupId());
             params.put(SNP_PART_ID_PARAM, pair.getPartitionId());
             params.put(SNP_CACHE_DIR_NAME_PARAM, cacheDirName);
-            params.put(SNP_CACHE_DR_NAME_PARAM, drName);
+            params.put(SNP_CACHE_STORAGE_PATH_PARAM, storagePath);
             params.put(RQ_ID_NAME_PARAM, rqId);
             params.put(SNP_PARTITIONS_CNT, partsCnt);
 
@@ -3885,7 +3889,7 @@ public class IgniteSnapshotManager extends GridCacheSharedManagerAdapter
         }
 
         /** {@inheritDoc} */
-        @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long len) {
+        @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long len) {
             try {
                 if (len == 0)
                     return;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IncrementalSnapshotFutureTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IncrementalSnapshotFutureTask.java
index 3353ccc117c..bb36d85ac9e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IncrementalSnapshotFutureTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IncrementalSnapshotFutureTask.java
@@ -80,7 +80,7 @@ class IncrementalSnapshotFutureTask extends AbstractSnapshotFutureTask<Void> imp
                     // No-op.
                 }
 
-                @Override protected void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                @Override protected void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                     // No-op.
                 }
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotFutureTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotFutureTask.java
index 1ea75c3ce62..073309566db 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotFutureTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotFutureTask.java
@@ -392,7 +392,7 @@ class SnapshotFutureTask extends AbstractCreateSnapshotFutureTask implements Che
                 snpSndr.sendPart(
                     partitionFile(ft, pair),
                     partitionFile(sft, pair),
-                    dataRegionName(pair),
+                    storagePath(pair),
                     pair,
                     partLen);
 
@@ -527,8 +527,8 @@ class SnapshotFutureTask extends AbstractCreateSnapshotFutureTask implements Che
         return ft.partitionFile(gctx.config(), grpAndPart.getPartitionId());
     }
 
-    /** @return Data region name. */
-    private String dataRegionName(GroupPartitionId grpAndPart) throws IgniteCheckedException {
+    /** @return Storage path. */
+    private String storagePath(GroupPartitionId grpAndPart) throws IgniteCheckedException {
         if (grpAndPart.getGroupId() == MetaStorage.METASTORAGE_CACHE_ID)
             return null;
 
@@ -537,7 +537,7 @@ class SnapshotFutureTask extends AbstractCreateSnapshotFutureTask implements Che
         if (gctx == null)
             throw new IgniteCheckedException("Cache group context has not found due to the cache group is stopped.");
 
-        return gctx.config().getDataRegionName();
+        return gctx.config().getStoragePath();
     }
 
     /** {@inheritDoc} */
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotResponseRemoteFutureTask.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotResponseRemoteFutureTask.java
index 2b362563881..393781ad5ad 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotResponseRemoteFutureTask.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotResponseRemoteFutureTask.java
@@ -116,7 +116,7 @@ public class SnapshotResponseRemoteFutureTask extends AbstractSnapshotFutureTask
                         ", pair=" + gp + ']');
                 }
 
-                snpSndr.sendPart(snpPart, sft.partitionFile(ccfg, gp.getPartitionId()), ccfg.getDataRegionName(), gp, snpPart.length());
+                snpSndr.sendPart(snpPart, sft.partitionFile(ccfg, gp.getPartitionId()), ccfg.getStoragePath(), gp, snpPart.length());
             }), snpSndr.executor())
                 .whenComplete((r, t) -> {
                     if (t != null)
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotSender.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotSender.java
index e354b96cb15..5e437a967ae 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotSender.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/SnapshotSender.java
@@ -126,11 +126,12 @@ public abstract class SnapshotSender {
     /**
      * @param from Partition file to send.
      * @param to Destination file.
-     * @param drName Data region name.
+     * @param storagePath Cache storage path.
      * @param pair Group id with partition id pair.
      * @param length Partition length.
+     * @see CacheConfiguration#getStoragePath()
      */
-    public final void sendPart(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+    public final void sendPart(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
         if (!lock.readLock().tryLock())
             return;
 
@@ -138,7 +139,7 @@ public abstract class SnapshotSender {
             if (closed)
                 return;
 
-            sendPart0(from, to, drName, pair, length);
+            sendPart0(from, to, storagePath, pair, length);
         }
         finally {
             lock.readLock().unlock();
@@ -192,11 +193,12 @@ public abstract class SnapshotSender {
     /**
      * @param from Partition file to send.
      * @param to Patition file to copy to.
-     * @param drName Data region name.
+     * @param storagePath Cache storage path.
      * @param pair Group id with partition id pair.
      * @param length Partition length.
+     * @see CacheConfiguration#getStoragePath()
      */
-    protected abstract void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length);
+    protected abstract void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length);
 
     /**
      * @param delta Delta pages file.
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/AbstractDataRegionRelativeStoragePathTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/AbstractDataRegionRelativeStoragePathTest.java
index 765a4b51b85..850e715e438 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/AbstractDataRegionRelativeStoragePathTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/AbstractDataRegionRelativeStoragePathTest.java
@@ -20,13 +20,13 @@ package org.apache.ignite.internal.processors.cache.persistence.filename;
 import java.io.File;
 import java.util.List;
 import java.util.function.ObjIntConsumer;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 import org.apache.ignite.IgniteCache;
 import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
 import org.apache.ignite.cluster.ClusterState;
 import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.internal.IgniteEx;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.internal.util.typedef.internal.U;
@@ -36,25 +36,22 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
 /**
- * Test cases when {@link DataRegionConfiguration#setStoragePath(String)} used to set custom data region storage path.
+ * Test cases when {@link CacheConfiguration#setStoragePath(String)} used to set custom data region storage path.
  */
 @RunWith(Parameterized.class)
 public abstract class AbstractDataRegionRelativeStoragePathTest extends GridCommonAbstractTest {
-    /** Custom storage path for default data region. */
-    static final String DEFAULT_DR_STORAGE_PATH = "dflt_dr";
+    /** Custom storage path . */
+    static final String STORAGE_PATH = "storage";
 
-    /** Custom storage path for custom data region. */
-    static final String CUSTOM_STORAGE_PATH = "custom_dr";
-
-    /** Data region name with custom storage. */
-    static final String DR_WITH_STORAGE = "custom-storage";
-
-    /** Data region with default storage. */
-    static final String DR_WITH_DFLT_STORAGE = "default-storage";
+    /** Second custom storage path. */
+    static final String STORAGE_PATH_2 = "storage2";
 
     /** */
     static final String SNP_PATH = "ex_snapshots";
 
+    /** */
+    protected static final int PARTS_CNT = 15;
+
     /** */
     @Parameterized.Parameter()
     public boolean useAbsStoragePath;
@@ -74,10 +71,10 @@ public abstract class AbstractDataRegionRelativeStoragePathTest extends GridComm
         cleanPersistenceDir();
 
         if (useAbsStoragePath)
-            U.delete(new File(storagePath(DEFAULT_DR_STORAGE_PATH)).getParentFile());
+            U.delete(new File(storagePath(STORAGE_PATH)).getParentFile());
         else {
-            U.delete(new File(U.defaultWorkDirectory(), DEFAULT_DR_STORAGE_PATH));
-            U.delete(new File(U.defaultWorkDirectory(), CUSTOM_STORAGE_PATH));
+            U.delete(new File(U.defaultWorkDirectory(), STORAGE_PATH));
+            U.delete(new File(U.defaultWorkDirectory(), STORAGE_PATH_2));
         }
 
         U.delete(new File(U.defaultWorkDirectory(), SNP_PATH));
@@ -86,16 +83,20 @@ public abstract class AbstractDataRegionRelativeStoragePathTest extends GridComm
     /**
      * @param name Snapshot name
      * @param path Snapshot path.
-     * @param fts Nodes file trees.
      */
-    void restoreAndCheck(String name, String path, List<NodeFileTree> fts) throws Exception {
+    void restoreAndCheck(String name, String path) throws Exception {
+        List<NodeFileTree> fts = IntStream.range(0, 3)
+            .mapToObj(this::grid)
+            .map(ign -> ign.context().pdsFolderResolver().fileTree())
+            .collect(Collectors.toList());
+
         stopAllGrids();
 
         checkFileTrees(fts);
 
         fts.forEach(ft -> {
             U.delete(ft.nodeStorage());
-            ft.dataRegionStorages().values().forEach(U::delete);
+            ft.extraStorages().values().forEach(U::delete);
         });
 
         U.delete(F.first(fts).db());
@@ -164,16 +165,21 @@ public abstract class AbstractDataRegionRelativeStoragePathTest extends GridComm
     }
 
     /** */
-    CacheConfiguration<?, ?> ccfg(String name, String grp, String dr) {
+    CacheConfiguration<?, ?> ccfg(String name, String grp, String storagePath) {
         return new CacheConfiguration<>(name)
             .setGroupName(grp)
-            .setDataRegionName(dr)
-            .setAffinity(new RendezvousAffinityFunction().setPartitions(15));
+            .setStoragePath(storagePath)
+            .setAffinity(new RendezvousAffinityFunction().setPartitions(PARTS_CNT));
     }
 
     /** */
-    String storagePath(String storagePath) throws IgniteCheckedException {
-        return useAbsStoragePath ? new File(U.defaultWorkDirectory(), "abs/" + storagePath).getAbsolutePath() : storagePath;
+    String storagePath(String storagePath) {
+        try {
+            return useAbsStoragePath ? new File(U.defaultWorkDirectory(), "abs/" + storagePath).getAbsolutePath() : storagePath;
+        }
+        catch (IgniteCheckedException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     /** @param fts Nodes file trees. */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CacheConfigStoragePathTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CacheConfigStoragePathTest.java
new file mode 100644
index 00000000000..5a0d2a5bf51
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CacheConfigStoragePathTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.junit.Test;
+
+import static org.apache.ignite.configuration.IgniteConfiguration.DFLT_SNAPSHOT_DIRECTORY;
+import static org.apache.ignite.internal.processors.cache.persistence.filename.SharedFileTree.DB_DIR;
+
+/**
+ * Test cases when {@link CacheConfiguration#setStoragePath(String)} used to set custom data region storage path.
+ */
+public class CacheConfigStoragePathTest extends AbstractDataRegionRelativeStoragePathTest {
+
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        DataStorageConfiguration dsCfg = new DataStorageConfiguration()
+            .setExtraStoragePathes(storagePath(STORAGE_PATH), storagePath(STORAGE_PATH_2));
+
+        dsCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);
+
+        return super.getConfiguration(igniteInstanceName)
+            .setConsistentId(U.maskForFileName(igniteInstanceName))
+            .setDataStorageConfiguration(dsCfg)
+            .setCacheConfiguration(ccfgs());
+    }
+
+    /** {@inheritDoc} */
+    @Override CacheConfiguration[] ccfgs() {
+        return new CacheConfiguration[]{
+            ccfg("cache0", null, null),
+            ccfg("cache1", "grp1", null),
+            ccfg("cache2", "grp1", null),
+            ccfg("cache3", null, storagePath(STORAGE_PATH)),
+            ccfg("cache4", "grp2", storagePath(STORAGE_PATH)),
+            ccfg("cache5", null, storagePath(STORAGE_PATH_2)),
+            ccfg("cache6", "grp3", storagePath(STORAGE_PATH_2)),
+            ccfg("cache7", "grp3", storagePath(STORAGE_PATH_2))
+        };
+    }
+
+    /** */
+    @Test
+    public void testCaches() throws Exception {
+        startAndActivate();
+
+        putData();
+
+        checkDataExists();
+
+        stopAllGrids();
+
+        IgniteEx srv = startAndActivate();
+
+        checkDataExists();
+
+        srv.snapshot().createSnapshot("mysnp").get();
+
+        File fullPathSnp = new File(U.defaultWorkDirectory(), SNP_PATH);
+
+        srv.context().cache().context().snapshotMgr().createSnapshot("mysnp2", fullPathSnp.getAbsolutePath(), false, false).get();
+
+        checkSnapshotFiles("mysnp", null);
+        checkSnapshotFiles("mysnp2", fullPathSnp.getAbsolutePath());
+
+        restoreAndCheck("mysnp", null);
+        restoreAndCheck("mysnp2", fullPathSnp.getAbsolutePath());
+    }
+
+    /** {@inheritDoc} */
+    @Override void checkFileTrees(List<NodeFileTree> fts) {
+        for (NodeFileTree ft : fts) {
+            for (CacheConfiguration<?, ?> ccfg : ccfgs()) {
+                String storagePath = ccfg.getStoragePath();
+
+                File customRoot = storagePath == null ? ft.root() : ensureExists(useAbsStoragePath
+                    ? new File(storagePath)
+                    : new File(ft.root(), storagePath)
+                );
+                File db = ensureExists(new File(customRoot, DB_DIR));
+                File nodeStorage = ensureExists(new File(db, ft.folderName()));
+
+                ensureExists(new File(nodeStorage, ft.cacheStorage(ccfg).getName()));
+            }
+        }
+    }
+
+    /**
+     * @param name Snapshot name.
+     * @param path Snapshot path.
+     */
+    private void checkSnapshotFiles(String name, String path) {
+        NodeFileTree ft = grid(0).context().pdsFolderResolver().fileTree();
+
+        Function<String, File> snpRootF = storage -> {
+            File snpRoot;
+
+            if (path != null)
+                snpRoot = new File(path);
+            else if (storage == null)
+                snpRoot = ft.snapshotsRoot();
+            else {
+                File nodeStorage = useAbsStoragePath
+                    ? new File(storage)
+                    : new File(ft.root(), storage);
+
+                snpRoot = new File(nodeStorage, DFLT_SNAPSHOT_DIRECTORY);
+            }
+
+            return new File(snpRoot, name);
+        };
+
+        // Snapshot root directories.
+        Set<File> roots = new HashSet<>(Arrays.asList(
+            snpRootF.apply(null),
+            snpRootF.apply(storagePath(STORAGE_PATH)),
+            snpRootF.apply(storagePath(STORAGE_PATH_2))
+        ));
+
+        assertEquals(path != null ? 1 : 3, roots.size());
+
+        // Root -> cache -> partition set.
+        Map<File, Map<String, Set<Integer>>> snpFiles = new HashMap<>();
+
+        // Collecting all partition files under each snapshot root.
+        roots.forEach(snpRoot -> {
+            assertTrue(snpRoot.exists());
+            assertTrue(snpRoot.isDirectory());
+
+            try (Stream<Path> files = Files.walk(snpRoot.toPath())) {
+                files.filter(p -> NodeFileTree.partitionFile(p.toFile())).forEach(part -> {
+                    File root = roots.stream().filter(r -> part.startsWith(r.toPath())).findFirst().orElseThrow();
+
+                    snpFiles
+                        .computeIfAbsent(root, r -> new HashMap<>())
+                        .computeIfAbsent(NodeFileTree.cacheName(part.getParent().toFile()), c -> new HashSet<>())
+                        .add(NodeFileTree.partId(part.toFile()));
+                });
+            }
+            catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        Set<String> seenGrps = new HashSet<>();
+
+        Arrays.stream(ccfgs()).forEach(ccfg -> {
+            String cname = CU.cacheOrGroupName(ccfg);
+
+            if (seenGrps.contains(cname))
+                return;
+
+            seenGrps.add(cname);
+
+            File expRoot = snpRootF.apply(ccfg.getStoragePath());
+
+            assertTrue(cname + " must be found", snpFiles.containsKey(expRoot));
+
+            Set<Integer> parts = snpFiles.get(expRoot).get(cname);
+
+            assertNotNull(cname + " partitions must be found", parts);
+            assertEquals("All partitions for " + cname + " must be found", PARTS_CNT, parts.size());
+
+            IntStream.range(0, PARTS_CNT).forEach(i -> assertTrue(i + " partition must be found", parts.contains(i)));
+        });
+
+        assertEquals(6, seenGrps.size());
+    }
+}
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
new file mode 100644
index 00000000000..986cda86a28
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/CustomCacheStorageConfigurationSelfTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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 org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cluster.ClusterState;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.DataRegionConfiguration;
+import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+import static org.apache.ignite.testframework.GridTestUtils.assertThrows;
+import static org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+
+/**
+ *
+ */
+public class CustomCacheStorageConfigurationSelfTest extends GridCommonAbstractTest {
+    /** */
+    private File myPath;
+
+    /** */
+    private File myPath2;
+
+    /** */
+    private File myPath3;
+
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        myPath = new File(U.defaultWorkDirectory(), "my_path");
+        myPath2 = new File(U.defaultWorkDirectory(), "my_path2");
+        myPath3 = new File(U.defaultWorkDirectory(), "my_path3");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        cleanPersistenceDir();
+        U.delete(myPath);
+        U.delete(myPath2);
+        U.delete(myPath3);
+    }
+
+    /** */
+    @Test
+    public void testDuplicatesStoragePathThrows() throws Exception {
+        assertThrows(
+            log,
+            () -> startGrid(new IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+                .setStoragePath(myPath.getAbsolutePath())
+                .setExtraStoragePathes(myPath.getAbsolutePath()))),
+            IgniteCheckedException.class,
+            "DataStorageConfiguration contains duplicates"
+        );
+
+        assertThrows(
+            log,
+            () -> startGrid(new IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+                .setStoragePath(myPath.getAbsolutePath())
+                .setExtraStoragePathes(myPath2.getAbsolutePath(), myPath.getAbsolutePath()))),
+            IgniteCheckedException.class,
+            "DataStorageConfiguration contains duplicates"
+        );
+
+        assertThrows(
+            log,
+            () -> startGrid(new IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+                .setStoragePath(myPath.getAbsolutePath())
+                .setExtraStoragePathes(myPath2.getAbsolutePath(), myPath2.getAbsolutePath()))),
+            IgniteCheckedException.class,
+            "DataStorageConfiguration contains duplicates"
+        );
+    }
+
+    /** */
+    @Test
+    public void testCacheUnknownStoragePathThrows() throws Exception {
+        try (IgniteEx srv = startGrid(new IgniteConfiguration().setDataStorageConfiguration(new DataStorageConfiguration()
+                .setStoragePath(myPath.getAbsolutePath())
+                .setExtraStoragePathes(myPath2.getAbsolutePath())
+                .setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true))))) {
+            srv.cluster().state(ClusterState.ACTIVE);
+
+            assertThrowsWithCause(
+                () -> srv.createCache(new CacheConfiguration<>("my-cache").setStoragePath("other")),
+                IgniteCheckedException.class
+            );
+        }
+    }
+
+    /** */
+    @Test
+    public void testDifferentStoragePathForGroupThrows() throws Exception {
+        DataStorageConfiguration dsCfg = new DataStorageConfiguration()
+            .setStoragePath(myPath.getAbsolutePath())
+            .setExtraStoragePathes(myPath2.getAbsolutePath(), myPath3.getAbsolutePath())
+            .setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true));
+
+        try (IgniteEx srv = startGrid(new IgniteConfiguration().setDataStorageConfiguration(dsCfg))) {
+            srv.cluster().state(ClusterState.ACTIVE);
+
+            srv.createCache(new CacheConfiguration<>("my-cache")
+                    .setGroupName("grp")
+                    .setStoragePath(myPath.getAbsolutePath()));
+
+            assertThrowsWithCause(
+                () -> srv.createCache(new CacheConfiguration<>("my-cache2")
+                    .setGroupName("grp")
+                    .setStoragePath(myPath3.getAbsolutePath())),
+                IgniteCheckedException.class
+            );
+
+            assertThrowsWithCause(
+                () -> srv.createCache(new CacheConfiguration<>("my-cache2").setGroupName("grp")),
+                IgniteCheckedException.class
+            );
+
+            srv.createCache(new CacheConfiguration<>("my-cache2")
+                .setGroupName("grp-2"));
+
+            assertThrowsWithCause(
+                () -> srv.createCache(new CacheConfiguration<>("my-cache3")
+                    .setGroupName("grp-2")
+                    .setStoragePath(myPath.getAbsolutePath())),
+                IgniteCheckedException.class
+            );
+        }
+    }
+
+    /** */
+    @Test
+    public void testCreateCaches() throws Exception {
+        DataStorageConfiguration dsCfg = new DataStorageConfiguration()
+            .setStoragePath(myPath.getAbsolutePath())
+            .setExtraStoragePathes(myPath2.getAbsolutePath(), myPath3.getAbsolutePath())
+            .setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true));
+
+        try (IgniteEx srv = startGrid(new IgniteConfiguration().setDataStorageConfiguration(dsCfg))) {
+            srv.cluster().state(ClusterState.ACTIVE);
+
+            srv.createCache(new CacheConfiguration<>("my-cache")
+                .setGroupName("grp").setStoragePath(myPath3.getAbsolutePath()));
+
+            srv.createCache(new CacheConfiguration<>("my-cache2")
+                .setGroupName("grp").setStoragePath(myPath3.getAbsolutePath()));
+        }
+    }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/DataRegionRelativeStoragePathTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/DataRegionRelativeStoragePathTest.java
deleted file mode 100644
index abe6fc751c5..00000000000
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/filename/DataRegionRelativeStoragePathTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.List;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
-import org.apache.ignite.configuration.DataStorageConfiguration;
-import org.apache.ignite.configuration.IgniteConfiguration;
-import org.apache.ignite.internal.IgniteEx;
-import org.apache.ignite.internal.util.typedef.internal.U;
-import org.junit.Test;
-
-import static org.apache.ignite.internal.processors.cache.persistence.filename.SharedFileTree.DB_DIR;
-
-/**
- * Test cases when {@link DataRegionConfiguration#setStoragePath(String)} used to set custom data region storage path.
- */
-public class DataRegionRelativeStoragePathTest extends AbstractDataRegionRelativeStoragePathTest {
-    /** */
-    public final CacheConfiguration[] ccfgs = new CacheConfiguration[] {
-        ccfg("cache0", null, null),
-        ccfg("cache1", "grp1", null),
-        ccfg("cache2", "grp1", null),
-        ccfg("cache3", null, DR_WITH_DFLT_STORAGE),
-        ccfg("cache4", "grp2", DR_WITH_DFLT_STORAGE),
-        ccfg("cache5", null, DR_WITH_STORAGE),
-        ccfg("cache6", "grp3", DR_WITH_STORAGE),
-        ccfg("cache7", "grp3", DR_WITH_STORAGE)
-    };
-
-    /** {@inheritDoc} */
-    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
-        DataStorageConfiguration dsCfg = new DataStorageConfiguration();
-
-        dsCfg.getDefaultDataRegionConfiguration().setStoragePath(storagePath(DEFAULT_DR_STORAGE_PATH)).setPersistenceEnabled(true);
-
-        dsCfg.setDataRegionConfigurations(
-            new DataRegionConfiguration().setName(DR_WITH_STORAGE)
-                .setStoragePath(storagePath(CUSTOM_STORAGE_PATH))
-                .setPersistenceEnabled(true),
-            new DataRegionConfiguration()
-                .setName(DR_WITH_DFLT_STORAGE)
-                .setPersistenceEnabled(true)
-        );
-
-        return super.getConfiguration(igniteInstanceName)
-            .setConsistentId(U.maskForFileName(igniteInstanceName))
-            .setDataStorageConfiguration(dsCfg)
-            .setCacheConfiguration(ccfgs);
-    }
-
-    /** {@inheritDoc} */
-    @Override CacheConfiguration[] ccfgs() {
-        return ccfgs;
-    }
-
-    /** */
-    @Test
-    public void testCaches() throws Exception {
-        startAndActivate();
-
-        putData();
-
-        checkDataExists();
-
-        stopAllGrids();
-
-        IgniteEx srv = startAndActivate();
-
-        checkDataExists();
-
-        List<NodeFileTree> fts = IntStream.range(0, 3)
-            .mapToObj(this::grid)
-            .map(ign -> ign.context().pdsFolderResolver().fileTree())
-            .collect(Collectors.toList());
-
-        srv.snapshot().createSnapshot("mysnp").get();
-
-        File fullPathSnp = new File(U.defaultWorkDirectory(), SNP_PATH);
-
-        srv.context().cache().context().snapshotMgr().createSnapshot("mysnp2", fullPathSnp.getAbsolutePath(), false, false).get();
-
-        restoreAndCheck("mysnp", null, fts);
-
-        restoreAndCheck("mysnp2", fullPathSnp.getAbsolutePath(), fts);
-    }
-
-    /** {@inheritDoc} */
-    @Override void checkFileTrees(List<NodeFileTree> fts) throws IgniteCheckedException {
-        for (NodeFileTree ft : fts) {
-            boolean[] flags = new boolean[2];
-
-            for (CacheConfiguration<?, ?> ccfg : ccfgs()) {
-                File db;
-
-                if (Objects.equals(ccfg.getDataRegionName(), DR_WITH_DFLT_STORAGE)) {
-                    db = ensureExists(new File(ft.root(), DB_DIR));
-
-                    flags[0] = true;
-                }
-                else {
-                    String storagePath = ccfg.getDataRegionName() == null ? DEFAULT_DR_STORAGE_PATH : CUSTOM_STORAGE_PATH;
-
-                    File customRoot = ensureExists(useAbsStoragePath
-                        ? new File(storagePath(storagePath))
-                        : new File(ft.root(), storagePath)
-                    );
-
-                    db = ensureExists(new File(customRoot, DB_DIR));
-
-                    flags[1] = true;
-                }
-
-                File nodeStorage = ensureExists(new File(db, ft.folderName()));
-
-                ensureExists(new File(nodeStorage, ft.cacheStorage(ccfg).getName()));
-            }
-
-            for (boolean flag : flags)
-                assertTrue(flag);
-        }
-    }
-}
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 3ce793823ea..701012abc68 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
@@ -19,11 +19,7 @@ package org.apache.ignite.internal.processors.cache.persistence.filename;
 
 import java.io.File;
 import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import org.apache.ignite.IgniteCheckedException;
 import org.apache.ignite.configuration.CacheConfiguration;
-import org.apache.ignite.configuration.DataRegionConfiguration;
 import org.apache.ignite.configuration.DataStorageConfiguration;
 import org.apache.ignite.configuration.IgniteConfiguration;
 import org.apache.ignite.internal.IgniteEx;
@@ -31,7 +27,7 @@ import org.apache.ignite.internal.util.typedef.internal.U;
 import org.junit.Test;
 
 /**
- * Test cases when {@link DataRegionConfiguration#setStoragePath(String)} used to set custom data region storage path.
+ * Test snapshot can be created when {@link DataStorageConfiguration#setStoragePath(String)} used.
  */
 public class SnapshotCreationNonDefaultStoragePathTest extends AbstractDataRegionRelativeStoragePathTest {
     /** */
@@ -42,7 +38,7 @@ public class SnapshotCreationNonDefaultStoragePathTest extends AbstractDataRegio
     /** {@inheritDoc} */
     @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
         DataStorageConfiguration dsCfg = new DataStorageConfiguration()
-            .setStoragePath(storagePath(DEFAULT_DR_STORAGE_PATH));
+            .setStoragePath(storagePath(STORAGE_PATH));
 
         dsCfg.getDefaultDataRegionConfiguration()
             .setPersistenceEnabled(true);
@@ -73,20 +69,15 @@ public class SnapshotCreationNonDefaultStoragePathTest extends AbstractDataRegio
 
         srv.context().cache().context().snapshotMgr().createSnapshot("mysnp2", fullPathSnp.getAbsolutePath(), false, false).get();
 
-        List<NodeFileTree> fts = IntStream.range(0, 3)
-            .mapToObj(this::grid)
-            .map(ign -> ign.context().pdsFolderResolver().fileTree())
-            .collect(Collectors.toList());
-
-        restoreAndCheck("mysnp", null, fts);
-        restoreAndCheck("mysnp2", fullPathSnp.getAbsolutePath(), fts);
+        restoreAndCheck("mysnp", null);
+        restoreAndCheck("mysnp2", fullPathSnp.getAbsolutePath());
     }
 
     /** {@inheritDoc} */
-    @Override void checkFileTrees(List<NodeFileTree> fts) throws IgniteCheckedException {
+    @Override void checkFileTrees(List<NodeFileTree> fts) {
         for (NodeFileTree ft : fts) {
             for (CacheConfiguration<?, ?> ccfg : ccfgs()) {
-                String storagePath = DEFAULT_DR_STORAGE_PATH;
+                String storagePath = STORAGE_PATH;
 
                 File customRoot = ensureExists(useAbsStoragePath
                     ? new File(storagePath(storagePath))
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java
index 82696ea06a8..ab8b9b5eac1 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/AbstractSnapshotSelfTest.java
@@ -1007,8 +1007,8 @@ public abstract class AbstractSnapshotSelfTest extends GridCommonAbstractTest {
         }
 
         /** {@inheritDoc} */
-        @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
-            delegate.sendPart(from, to, drName, pair, length);
+        @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
+            delegate.sendPart(from, to, storagePath, pair, length);
         }
 
         /** {@inheritDoc} */
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotDeltaTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotDeltaTest.java
index cca7ec67c7b..c44ded79d02 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotDeltaTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteClusterSnapshotDeltaTest.java
@@ -121,7 +121,7 @@ public class IgniteClusterSnapshotDeltaTest extends AbstractSnapshotSelfTest {
 
         mgr.localSnapshotSenderFactory(sft -> new DelegateSnapshotSender(log,
             mgr.snapshotExecutorService(), old.apply(sft)) {
-            @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+            @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                 if (cacheDir.equals(to.getParentFile().getName()))
                     partStart.countDown();
 
@@ -132,7 +132,7 @@ public class IgniteClusterSnapshotDeltaTest extends AbstractSnapshotSelfTest {
                     throw new RuntimeException(e);
                 }
 
-                super.sendPart0(from, to, drName, pair, length);
+                super.sendPart0(from, to, storagePath, pair, length);
             }
 
             @Override public void sendDelta0(File delta, File snpPart, GroupPartitionId pair) {
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java
index 429fc8d9a3b..d387ef94c3c 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotManagerSelfTest.java
@@ -158,11 +158,11 @@ public class IgniteSnapshotManagerSelfTest extends AbstractSnapshotSelfTest {
             false,
             false,
             new DelegateSnapshotSender(log, mgr.snapshotExecutorService(), mgr.localSnapshotSenderFactory().apply(sft)) {
-                @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                     try {
                         U.await(slowCopy);
 
-                        delegate.sendPart0(from, to, drName, pair, length);
+                        delegate.sendPart0(from, to, storagePath, pair, length);
                     }
                     catch (IgniteInterruptedCheckedException e) {
                         throw new IgniteException(e);
@@ -309,11 +309,11 @@ public class IgniteSnapshotManagerSelfTest extends AbstractSnapshotSelfTest {
             encryption,
             new DelegateSnapshotSender(log, mgr0.snapshotExecutorService(),
                 mgr0.localSnapshotSenderFactory().apply(sft)) {
-                @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                     if (pair.getPartitionId() == 0)
                         throw new IgniteException(err_msg + pair);
 
-                    delegate.sendPart0(from, to, drName, pair, length);
+                    delegate.sendPart0(from, to, storagePath, pair, length);
                 }
             });
 
@@ -346,11 +346,11 @@ public class IgniteSnapshotManagerSelfTest extends AbstractSnapshotSelfTest {
             F.asMap(CU.cacheId(DEFAULT_CACHE_NAME), null),
             encryption,
             new DelegateSnapshotSender(log, mgr.snapshotExecutorService(), mgr.localSnapshotSenderFactory().apply(sft)) {
-                @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                     try {
                         U.await(cpLatch);
 
-                        delegate.sendPart0(from, to, drName, pair, length);
+                        delegate.sendPart0(from, to, storagePath, pair, length);
                     }
                     catch (IgniteInterruptedCheckedException e) {
                         throw new IgniteException(e);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRemoteRequestTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRemoteRequestTest.java
index e816ced3c86..53e0a9a54ff 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRemoteRequestTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRemoteRequestTest.java
@@ -178,7 +178,7 @@ public class IgniteSnapshotRemoteRequestTest extends IgniteClusterSnapshotRestor
         mgr1.remoteSnapshotSenderFactory(new BiFunction<String, UUID, SnapshotSender>() {
             @Override public SnapshotSender apply(String s, UUID uuid) {
                 return new DelegateSnapshotSender(log, mgr1.snapshotExecutorService(), mgr1.remoteSnapshotSenderFactory(s, uuid)) {
-                    @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                    @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                         if (partId(from) > 0) {
                             try {
                                 sndLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
@@ -188,7 +188,7 @@ public class IgniteSnapshotRemoteRequestTest extends IgniteClusterSnapshotRestor
                             }
                         }
 
-                        super.sendPart0(from, to, drName, pair, length);
+                        super.sendPart0(from, to, storagePath, pair, length);
                     }
                 };
             }
@@ -341,13 +341,13 @@ public class IgniteSnapshotRemoteRequestTest extends IgniteClusterSnapshotRestor
 
         mgr0.remoteSnapshotSenderFactory((rqId, nodeId) -> new DelegateSnapshotSender(log,
             snp(sndr).snapshotExecutorService(), mgr0.remoteSnapshotSenderFactory(rqId, nodeId)) {
-            @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+            @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                 nodes.add(nodeId);
 
                 // Single thread must send partitions sequentially node by node.
                 checkDuplicates(nodes);
 
-                super.sendPart0(from, to, drName, pair, length);
+                super.sendPart0(from, to, storagePath, pair, length);
             }
         });
 
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRestoreFromRemoteTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRestoreFromRemoteTest.java
index 37498ac88a0..a31cd9a7137 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRestoreFromRemoteTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/IgniteSnapshotRestoreFromRemoteTest.java
@@ -306,11 +306,11 @@ public class IgniteSnapshotRestoreFromRemoteTest extends IgniteClusterSnapshotRe
         mgr.remoteSnapshotSenderFactory(new BiFunction<String, UUID, SnapshotSender>() {
             @Override public SnapshotSender apply(String s, UUID uuid) {
                 return new DelegateSnapshotSender(log, mgr.snapshotExecutorService(), mgr.remoteSnapshotSenderFactory(s, uuid)) {
-                    @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
+                    @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
                         if (partId(from) > 0)
                             throw new IgniteException("Test exception. Uploading partition file failed: " + pair);
 
-                        super.sendPart0(from, to, drName, pair, length);
+                        super.sendPart0(from, to, storagePath, pair, length);
                     }
                 };
             }
@@ -350,8 +350,8 @@ public class IgniteSnapshotRestoreFromRemoteTest extends IgniteClusterSnapshotRe
         mgr.remoteSnapshotSenderFactory(new BiFunction<String, UUID, SnapshotSender>() {
             @Override public SnapshotSender apply(String s, UUID uuid) {
                 return new DelegateSnapshotSender(log, mgr.snapshotExecutorService(), mgr.remoteSnapshotSenderFactory(s, uuid)) {
-                    @Override public void sendPart0(File from, File to, @Nullable String drName, GroupPartitionId pair, Long length) {
-                        delegate.sendPart0(from, to, drName, pair, length);
+                    @Override public void sendPart0(File from, File to, @Nullable String storagePath, GroupPartitionId pair, Long length) {
+                        delegate.sendPart0(from, to, storagePath, pair, length);
 
                         restoreStarted.countDown();
 
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/AbstractCacheDumpTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/AbstractCacheDumpTest.java
index 80ece571bf7..4ca007c2c47 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/AbstractCacheDumpTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/AbstractCacheDumpTest.java
@@ -196,17 +196,18 @@ public abstract class AbstractCacheDumpTest extends GridCommonAbstractTest {
         IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName)
             .setSnapshotThreadPoolSize(snpPoolSz)
             .setDataStorageConfiguration(new DataStorageConfiguration()
+                .setStoragePath(DFLT_STORAGE)
+                .setExtraStoragePathes(CUSTOM_STORAGE)
                 .setDefaultDataRegionConfiguration(new DataRegionConfiguration()
-                    .setPersistenceEnabled(persistence)
-                    .setStoragePath(nodes > 1 ? DFLT_STORAGE : null))
+                    .setPersistenceEnabled(persistence))
                 .setDataRegionConfigurations(new DataRegionConfiguration()
-                    .setName(CUSTOM_STORAGE)
-                    .setStoragePath(CUSTOM_STORAGE)))
+                    .setName(CUSTOM_STORAGE)))
             .setCacheConfiguration(
                 new CacheConfiguration<>()
                     .setName(DEFAULT_CACHE_NAME)
                     .setBackups(backups)
                     .setDataRegionName(backups > 0 ? CUSTOM_STORAGE : null)
+                    .setStoragePath(backups > 0 ? CUSTOM_STORAGE : (nodes > 1 ? DFLT_STORAGE : null))
                     .setAtomicityMode(mode)
                     .setWriteSynchronizationMode(FULL_SYNC)
                     .setAffinity(new RendezvousAffinityFunction().setPartitions(20)),
@@ -215,6 +216,7 @@ public abstract class AbstractCacheDumpTest extends GridCommonAbstractTest {
                     .setName(CACHE_0)
                     .setBackups(backups)
                     .setDataRegionName(backups > 0 ? CUSTOM_STORAGE : null)
+                    .setStoragePath(backups > 0 ? CUSTOM_STORAGE : (nodes > 1 ? DFLT_STORAGE : null))
                     .setAtomicityMode(mode)
                     .setWriteSynchronizationMode(FULL_SYNC)
                     .setAffinity(new RendezvousAffinityFunction().setPartitions(20)),
@@ -223,6 +225,7 @@ public abstract class AbstractCacheDumpTest extends GridCommonAbstractTest {
                     .setName(CACHE_1)
                     .setBackups(backups)
                     .setDataRegionName(backups > 0 ? CUSTOM_STORAGE : null)
+                    .setStoragePath(backups > 0 ? CUSTOM_STORAGE : (nodes > 1 ? DFLT_STORAGE : null))
                     .setAtomicityMode(mode)
                     .setWriteSynchronizationMode(FULL_SYNC)
                     .setAffinity(new RendezvousAffinityFunction().setPartitions(20))
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/IgniteCacheDumpSelf2Test.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/IgniteCacheDumpSelf2Test.java
index fd4a477b2bc..a8d685f9d75 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/IgniteCacheDumpSelf2Test.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/persistence/snapshot/dump/IgniteCacheDumpSelf2Test.java
@@ -161,10 +161,11 @@ public class IgniteCacheDumpSelf2Test extends GridCommonAbstractTest {
         if (persistence)
             cfg.getDataStorageConfiguration().setDefaultDataRegionConfiguration(new DataRegionConfiguration().setPersistenceEnabled(true));
 
-        cfg.getDataStorageConfiguration().setDataRegionConfigurations(new DataRegionConfiguration()
+        cfg.getDataStorageConfiguration()
+            .setExtraStoragePathes(CUSTOM_LOCATION)
+            .setDataRegionConfigurations(new DataRegionConfiguration()
             .setPersistenceEnabled(persistence)
-            .setName(CUSTOM_LOCATION)
-            .setStoragePath(CUSTOM_LOCATION));
+            .setName(CUSTOM_LOCATION));
 
         cfg.setIncludeEventTypes(EVTS_CLUSTER_SNAPSHOT);
 
@@ -220,7 +221,7 @@ public class IgniteCacheDumpSelf2Test extends GridCommonAbstractTest {
 
         IgniteEx cli = startClientGrid(G.allGrids().size());
 
-        cli.createCache(defaultCacheConfiguration().setDataRegionName(dataCustomLocation ? CUSTOM_LOCATION : null));
+        cli.createCache(defaultCacheConfiguration().setStoragePath(dataCustomLocation ? CUSTOM_LOCATION : null));
 
         for (int i = 0; i < KEYS_CNT; ++i)
             cli.cache(DEFAULT_CACHE_NAME).put(i, USER_FACTORY.apply(i));
@@ -346,7 +347,7 @@ public class IgniteCacheDumpSelf2Test extends GridCommonAbstractTest {
 
         ign0.cluster().state(ClusterState.ACTIVE);
 
-        ign0.createCache(defaultCacheConfiguration().setDataRegionName(dataCustomLocation ? CUSTOM_LOCATION : null));
+        ign0.createCache(defaultCacheConfiguration().setStoragePath(dataCustomLocation ? CUSTOM_LOCATION : null));
 
         try (IgniteDataStreamer<Integer, String> ds = ign0.dataStreamer(DEFAULT_CACHE_NAME)) {
             IgniteCache<Integer, String> cache = ign0.cache(DEFAULT_CACHE_NAME);
@@ -691,7 +692,7 @@ public class IgniteCacheDumpSelf2Test extends GridCommonAbstractTest {
                 .setName("test-cache-0")
                 .setBackups(1)
                 .setAtomicityMode(CacheAtomicityMode.ATOMIC)
-                .setDataRegionName(dataCustomLocation ? CUSTOM_LOCATION : null));
+                .setStoragePath(dataCustomLocation ? CUSTOM_LOCATION : null));
 
             IntStream.range(0, KEYS_CNT).forEach(i -> cache.put(i, i));
 
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 3a4400ddcab..29be9f1c383 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
@@ -48,7 +48,8 @@ import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelo
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.log.OffHeapLockLogTest;
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.stack.HeapArrayLockStackTest;
 import org.apache.ignite.internal.processors.cache.persistence.diagnostic.pagelocktracker.stack.OffHeapLockStackTest;
-import org.apache.ignite.internal.processors.cache.persistence.filename.DataRegionRelativeStoragePathTest;
+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.warmup.LoadAllWarmUpStrategySelfTest;
 import org.apache.ignite.internal.processors.cache.warmup.WarmUpSelfTest;
@@ -111,8 +112,9 @@ public class IgnitePdsTestSuite8 {
 
         GridTestUtils.addTestIfNeeded(suite, ConnectionEnabledPropertyTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, IgnitePdsCheckpointRecoveryTest.class, ignoredTests);
-        GridTestUtils.addTestIfNeeded(suite, DataRegionRelativeStoragePathTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, CacheConfigStoragePathTest.class, ignoredTests);
         GridTestUtils.addTestIfNeeded(suite, SnapshotCreationNonDefaultStoragePathTest.class, ignoredTests);
+        GridTestUtils.addTestIfNeeded(suite, CustomCacheStorageConfigurationSelfTest.class, ignoredTests);
 
         return suite;
     }