You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ib...@apache.org on 2022/04/11 11:23:33 UTC

[ignite-3] branch main updated: IGNITE-16792 Configuration for Default Storage Engine (#763)

This is an automated email from the ASF dual-hosted git repository.

ibessonov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 8b99bec40 IGNITE-16792 Configuration for Default Storage Engine (#763)
8b99bec40 is described below

commit 8b99bec402a1e8619351f5168ae6a82a3a927ee9
Author: Kirill Tkalenko <tk...@yandex.ru>
AuthorDate: Mon Apr 11 14:23:29 2022 +0300

    IGNITE-16792 Configuration for Default Storage Engine (#763)
---
 .../store/DataStorageConfigurationSchema.java      |   9 +-
 .../schemas/store/ExistingDataStorage.java}        |  18 ++--
 ...figurationSchema.java => KnownDataStorage.java} |  25 +++--
 .../UnknownDataStorageConfigurationSchema.java}    |  13 +--
 .../schemas/table/TableConfigurationSchema.java    |   2 +
 .../schemas/table/TablesConfigurationSchema.java   |   9 ++
 .../configuration/processor/Processor.java         |  16 ++-
 .../annotation/PolymorphicConfigInstance.java      |   2 -
 .../validation/ValidationContext.java              |   6 +-
 .../asm/ConfigurationAsmGenerator.java             |  97 ++++++++++++++----
 .../asm/ConfigurationAsmGeneratorTest.java         |  27 ++++-
 .../validation/TestValidationUtil.java             |  97 ++++++++++++++++++
 .../CoreDistributedConfigurationModule.java        |  16 ++-
 .../CoreDistributedConfigurationModuleTest.java    |   3 +-
 .../configuration/KnownDataStorageValidator.java   |  45 +++++++++
 .../KnownDataStorageValidatorTest.java             |  65 ++++++++++++
 .../SchemaConfigurationConverterTest.java          |   4 +-
 .../configuration/TableValidatorImplTest.java      |  44 ++------
 .../sql/engine/exec/MockedStructuresTest.java      |  19 ++--
 .../internal/storage/DataStorageManager.java       |  71 ++++++++-----
 .../ExistingDataStorageValidator.java              |  64 ++++++++++++
 ...torageEngineDistributedConfigurationModule.java |  45 +++++++++
 .../internal/storage/engine/StorageEngine.java     |   8 --
 .../storage/engine/StorageEngineFactory.java       |   8 ++
 ...nite.internal.configuration.ConfigurationModule |  17 ++++
 .../storage/ConcurrentHashMapStorageTest.java      |   4 +-
 .../internal/storage/DataStorageManagerTest.java   | 112 +++++++++++++++++----
 .../chm/TestConcurrentHashMapStorageEngine.java    |   6 --
 .../TestConcurrentHashMapStorageEngineFactory.java |   8 ++
 .../ExistingDataStorageValidatorTest.java          |  77 ++++++++++++++
 .../pagememory/PageMemoryStorageEngine.java        |   6 --
 .../pagememory/PageMemoryStorageEngineFactory.java |   8 ++
 .../pagememory/PageMemoryPartitionStorageTest.java |   4 +-
 .../TestRocksDbDataStorageConfigurationSchema.java |  30 ------
 .../storage/rocksdb/RocksDbStorageEngine.java      |   6 --
 .../rocksdb/RocksDbStorageEngineFactory.java       |   8 ++
 .../rocksdb/RocksDbPartitionStorageTest.java       |  11 +-
 .../storage/rocksdb/RocksDbTableStorageTest.java   |  24 +++--
 .../index/RocksDbSortedIndexStorageTest.java       |  13 ++-
 .../internal/table/distributed/TableManager.java   |  24 +++--
 .../ignite/internal/table/TableManagerTest.java    |  53 ++++++----
 41 files changed, 860 insertions(+), 264 deletions(-)

diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java
index 1af23e21f..5aa80e9d6 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java
@@ -19,18 +19,13 @@ package org.apache.ignite.configuration.schemas.store;
 
 import org.apache.ignite.configuration.annotation.PolymorphicConfig;
 import org.apache.ignite.configuration.annotation.PolymorphicId;
-import org.apache.ignite.configuration.validation.Immutable;
 
 /**
  * Configuration schema for data storage.
  */
 @PolymorphicConfig
 public class DataStorageConfigurationSchema {
-    /** Default data storage name. */
-    public static final String DEFAULT_DATA_STORAGE_NAME = "rocksdb";
-
-    /** Type of data storage. */
-    @Immutable
+    /** Name of data storage. */
     @PolymorphicId(hasDefault = true)
-    public String name = DEFAULT_DATA_STORAGE_NAME;
+    public String name = UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
 }
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/ExistingDataStorage.java
similarity index 59%
rename from modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java
rename to modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/ExistingDataStorage.java
index d3012eaea..572491db1 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/ExistingDataStorage.java
@@ -15,16 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.schema.configuration.schema;
+package org.apache.ignite.configuration.schemas.store;
 
-import static org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema.DEFAULT_DATA_STORAGE_NAME;
-
-import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
-import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 /**
- * Test RocksDB data storage configuration schema for tables.
+ * An annotation to check that the value is equal to the {@link DataStorageConfigurationSchema#name name} of one of the {@link
+ * DataStorageConfigurationSchema data storages}.
  */
-@PolymorphicConfigInstance(DEFAULT_DATA_STORAGE_NAME)
-public class TestRocksDbDataStorageConfigurationSchema extends DataStorageConfigurationSchema {
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExistingDataStorage {
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/KnownDataStorage.java
similarity index 58%
copy from modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java
copy to modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/KnownDataStorage.java
index 1af23e21f..bf9cd42c8 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/DataStorageConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/KnownDataStorage.java
@@ -17,20 +17,19 @@
 
 package org.apache.ignite.configuration.schemas.store;
 
-import org.apache.ignite.configuration.annotation.PolymorphicConfig;
-import org.apache.ignite.configuration.annotation.PolymorphicId;
-import org.apache.ignite.configuration.validation.Immutable;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.ignite.configuration.annotation.ConfigValue;
 
 /**
- * Configuration schema for data storage.
+ * An annotation to check that the {@link DataStorageConfigurationSchema data storage} is known, i.e. its {@link
+ * DataStorageConfigurationSchema#name name} is not {@link UnknownDataStorageConfigurationSchema#UNKNOWN_DATA_STORAGE "unknown"}.
+ *
+ * <p>Can be applied to a {@link ConfigValue}.
  */
-@PolymorphicConfig
-public class DataStorageConfigurationSchema {
-    /** Default data storage name. */
-    public static final String DEFAULT_DATA_STORAGE_NAME = "rocksdb";
-
-    /** Type of data storage. */
-    @Immutable
-    @PolymorphicId(hasDefault = true)
-    public String name = DEFAULT_DATA_STORAGE_NAME;
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface KnownDataStorage {
 }
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/schema/TestRocksDbDataStorageConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/UnknownDataStorageConfigurationSchema.java
similarity index 65%
rename from modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/schema/TestRocksDbDataStorageConfigurationSchema.java
rename to modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/UnknownDataStorageConfigurationSchema.java
index 15107640d..54f742d4e 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/schema/TestRocksDbDataStorageConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/store/UnknownDataStorageConfigurationSchema.java
@@ -15,16 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.storage.chm.schema;
+package org.apache.ignite.configuration.schemas.store;
 
-import static org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema.DEFAULT_DATA_STORAGE_NAME;
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
 
 import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
-import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
 
 /**
- * Test RocksDB data storage configuration schema for tables.
+ * Configuration schema of an unknown data storage.
  */
-@PolymorphicConfigInstance(DEFAULT_DATA_STORAGE_NAME)
-public class TestRocksDbDataStorageConfigurationSchema extends DataStorageConfigurationSchema {
+@PolymorphicConfigInstance(UNKNOWN_DATA_STORAGE)
+public class UnknownDataStorageConfigurationSchema extends DataStorageConfigurationSchema {
+    /** Default data storage name. */
+    public static final String UNKNOWN_DATA_STORAGE = "unknown";
 }
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TableConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TableConfigurationSchema.java
index 67c013bf6..8a052b5a4 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TableConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TableConfigurationSchema.java
@@ -23,6 +23,7 @@ import org.apache.ignite.configuration.annotation.InjectedName;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
 import org.apache.ignite.configuration.annotation.Value;
 import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
+import org.apache.ignite.configuration.schemas.store.KnownDataStorage;
 import org.apache.ignite.configuration.validation.Max;
 import org.apache.ignite.configuration.validation.Min;
 
@@ -48,6 +49,7 @@ public class TableConfigurationSchema {
     public int replicas = 1;
 
     /** Data storage configuration. */
+    @KnownDataStorage
     @ConfigValue
     public DataStorageConfigurationSchema dataStorage;
 
diff --git a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TablesConfigurationSchema.java b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TablesConfigurationSchema.java
index 90e806e15..95ace27b7 100644
--- a/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TablesConfigurationSchema.java
+++ b/modules/api/src/main/java/org/apache/ignite/configuration/schemas/table/TablesConfigurationSchema.java
@@ -17,9 +17,13 @@
 
 package org.apache.ignite.configuration.schemas.table;
 
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
+
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.schemas.store.ExistingDataStorage;
 
 /**
  * Tables configuration schema.
@@ -31,4 +35,9 @@ public class TablesConfigurationSchema {
     @NamedConfigValue
     @TableValidator
     public TableConfigurationSchema tables;
+
+    /** Default data storage for tables. */
+    @ExistingDataStorage
+    @Value(hasDefault = true)
+    public String defaultDataStorage = UNKNOWN_DATA_STORAGE;
 }
diff --git a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
index d7b27d83a..7b4bd9cac 100644
--- a/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
+++ b/modules/configuration-annotation-processor/src/main/java/org/apache/ignite/internal/configuration/processor/Processor.java
@@ -483,17 +483,25 @@ public class Processor extends AbstractProcessor {
                     TypeVariableName.get("T")
             );
 
-            // Variable type, for example: <T extends SimpleChange & PolymorphicInstance>.
+            // Variable type, for example: <T extends SimpleChange & PolymorphicChange>.
             TypeVariableName typeVariable = TypeVariableName.get("T", changeClsName, POLYMORPHIC_CHANGE_CLASSNAME);
 
-            // Method like: <T extends SimpleChange> T convert(Class<T> changeClass);
-            MethodSpec.Builder convertMtdBuilder = MethodSpec.methodBuilder("convert")
+            // Method like: <T extends SimpleChange & PolymorphicChange> T convert(Class<T> changeClass);
+            MethodSpec.Builder convertByChangeClassMtdBuilder = MethodSpec.methodBuilder("convert")
                     .addModifiers(PUBLIC, ABSTRACT)
                     .addTypeVariable(typeVariable)
                     .addParameter(parameterType, "changeClass")
                     .returns(TypeVariableName.get("T"));
 
-            changeClsBuilder.addMethod(convertMtdBuilder.build());
+            changeClsBuilder.addMethod(convertByChangeClassMtdBuilder.build());
+
+            // Method like: SimpleChange convert(String polymorphicTypeId);
+            MethodSpec.Builder convertByStringMtdBuilder = MethodSpec.methodBuilder("convert")
+                    .addModifiers(PUBLIC, ABSTRACT)
+                    .addParameter(ClassName.get(String.class), "polymorphicTypeId")
+                    .returns(changeClsName);
+
+            changeClsBuilder.addMethod(convertByStringMtdBuilder.build());
         }
 
         TypeSpec viewCls = viewClsBuilder.build();
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PolymorphicConfigInstance.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PolymorphicConfigInstance.java
index 7b0074d14..1230328f7 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PolymorphicConfigInstance.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PolymorphicConfigInstance.java
@@ -36,8 +36,6 @@ import java.lang.annotation.Target;
 public @interface PolymorphicConfigInstance {
     /**
      * Unique identifier for an extension within a single {@link PolymorphicConfig polymorphic configuration}.
-     *
-     * @return Unique identifier for the extension of the polymorphic configuration instance.
      */
     String value();
 }
diff --git a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ValidationContext.java b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ValidationContext.java
index b97aaa57e..e2ae57bd6 100644
--- a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ValidationContext.java
+++ b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/validation/ValidationContext.java
@@ -38,14 +38,14 @@ public interface ValidationContext<VIEWT> {
     /**
      * Returns previous value of the configuration.
      *
-     * @return Previous value of the configuration. Might be null for leaves only.
+     * @return Previous value of the configuration. Might be {@code null} for leaves only.
      */
     @Nullable VIEWT getOldValue();
 
     /**
      * Returns updated value of the configuration.
      *
-     * @return Updated value of the configuration. Cannot be null.
+     * @return Updated value of the configuration. Cannot be {@code null}.
      */
     VIEWT getNewValue();
 
@@ -54,7 +54,6 @@ public interface ValidationContext<VIEWT> {
      *
      * @param rootKey Root key.
      * @param <ROOT>  Root view type derived from the root key.
-     * @return Configuration root view before updates.
      */
     @Nullable <ROOT> ROOT getOldRoot(RootKey<?, ROOT> rootKey);
 
@@ -63,7 +62,6 @@ public interface ValidationContext<VIEWT> {
      *
      * @param rootKey Root key.
      * @param <ROOT>  Root view type derived from the root key.
-     * @return Configuration root view after updates.
      */
     @Nullable <ROOT> ROOT getNewRoot(RootKey<?, ROOT> rootKey);
 
diff --git a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
index 2f1f52336..c953b4a50 100644
--- a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
+++ b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGenerator.java
@@ -699,7 +699,7 @@ public class ConfigurationAsmGenerator {
                     polymorphicTypeIdFieldDef
             );
 
-            addNodeConvertMethod(classDef, schemaClass, polymorphicExtensions, changePolymorphicTypeIdMtd);
+            addNodeConvertMethods(classDef, schemaClass, polymorphicExtensions, changePolymorphicTypeIdMtd);
 
             polymorphicFieldsByExtension = new LinkedHashMap<>();
 
@@ -2461,8 +2461,8 @@ public class ConfigurationAsmGenerator {
 
         ParameterizedType returnType = typeFromJavaClassName(schemaClassInfo.changeClassName);
 
-        // Creates Node#convert.
-        MethodDefinition convertMtd = classDef.declareMethod(
+        // Creates Node#convert(Class<T> changeClass).
+        MethodDefinition convertByChangeClassMtd = classDef.declareMethod(
                 of(PUBLIC),
                 CONVERT_MTD_NAME,
                 returnType,
@@ -2470,12 +2470,27 @@ public class ConfigurationAsmGenerator {
         );
 
         // return this.this$0.convert(changeClass);
-        convertMtd.getBody()
-                .append(getThisFieldCode(convertMtd, parentInnerNodeFieldDef))
-                .append(convertMtd.getScope().getVariable("changeClass"))
+        convertByChangeClassMtd.getBody()
+                .append(getThisFieldCode(convertByChangeClassMtd, parentInnerNodeFieldDef))
+                .append(convertByChangeClassMtd.getScope().getVariable("changeClass"))
                 .invokeVirtual(schemaInnerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(Class.class))
                 .retObject();
 
+        // Creates Node#convert(String polymorphicId).
+        MethodDefinition convertByPolymorphicTypeIdMtd = classDef.declareMethod(
+                of(PUBLIC),
+                CONVERT_MTD_NAME,
+                returnType,
+                arg("polymorphicTypeId", String.class)
+        );
+
+        // return this.this$0.convert(polymorphicTypeId);
+        convertByPolymorphicTypeIdMtd.getBody()
+                .append(getThisFieldCode(convertByPolymorphicTypeIdMtd, parentInnerNodeFieldDef))
+                .append(convertByPolymorphicTypeIdMtd.getScope().getVariable("polymorphicTypeId"))
+                .invokeVirtual(schemaInnerNodeClassDef.getType(), CONVERT_MTD_NAME, returnType, type(String.class))
+                .retObject();
+
         return classDef;
     }
 
@@ -2586,14 +2601,15 @@ public class ConfigurationAsmGenerator {
     }
 
     /**
-     * Adds a {@code *Node#convert} for the polymorphic configuration case.
+     * Adds a {@code *Node#convert(Class changeClass)} and {@code *Node#convert(String polymorphicTypeId)} for the polymorphic configuration
+     * case.
      *
-     * @param classDef                   Definition of a polymorphic configuration class {@code schemaClass}.
-     * @param schemaClass                Polymorphic configuration schema (parent).
-     * @param polymorphicExtensions      Polymorphic configuration instance schemas (children).
+     * @param classDef Definition of a polymorphic configuration class {@code schemaClass}.
+     * @param schemaClass Polymorphic configuration schema (parent).
+     * @param polymorphicExtensions Polymorphic configuration instance schemas (children).
      * @param changePolymorphicTypeIdMtd Method for changing the type of polymorphic configuration.
      */
-    private void addNodeConvertMethod(
+    private void addNodeConvertMethods(
             ClassDefinition classDef,
             Class<?> schemaClass,
             Set<Class<?>> polymorphicExtensions,
@@ -2601,43 +2617,80 @@ public class ConfigurationAsmGenerator {
     ) {
         SchemaClassesInfo schemaClassInfo = schemasInfo.get(schemaClass);
 
-        MethodDefinition convertMtd = classDef.declareMethod(
+        MethodDefinition convertByChangeClassMtd = classDef.declareMethod(
                 of(PUBLIC),
                 CONVERT_MTD_NAME,
                 typeFromJavaClassName(schemaClassInfo.changeClassName),
                 arg("changeClass", Class.class)
         );
 
+        MethodDefinition convertByPolymorphicTypeIdMtd = classDef.declareMethod(
+                of(PUBLIC),
+                CONVERT_MTD_NAME,
+                typeFromJavaClassName(schemaClassInfo.changeClassName),
+                arg("polymorphicTypeId", String.class)
+        );
+
         // changeClass.getName();
-        BytecodeExpression changeClassName = convertMtd.getScope()
+        BytecodeExpression changeClassName = convertByChangeClassMtd.getScope()
                 .getVariable("changeClass")
                 .invoke(CLASS_GET_NAME_MTD);
 
-        StringSwitchBuilder switchBuilder = new StringSwitchBuilder(convertMtd.getScope())
+        StringSwitchBuilder switchByChangeClassBuilder = new StringSwitchBuilder(convertByChangeClassMtd.getScope())
                 .expression(changeClassName)
                 .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, changeClassName));
 
+        Variable polymorphicTypeId = convertByPolymorphicTypeIdMtd.getScope()
+                .getVariable("polymorphicTypeId");
+
+        StringSwitchBuilder switchByPolymorphicTypeIdBuilder = new StringSwitchBuilder(convertByPolymorphicTypeIdMtd.getScope())
+                .expression(polymorphicTypeId)
+                .defaultCase(throwException(ConfigurationWrongPolymorphicTypeIdException.class, polymorphicTypeId));
+
         for (Class<?> polymorphicExtension : polymorphicExtensions) {
             SchemaClassesInfo polymorphicExtensionClassInfo = schemasInfo.get(polymorphicExtension);
 
-            // this.changePolymorphicTypeId(polymorphicTypeId);
-            // return new ChildNode(this);
-            switchBuilder.addCase(
+            String polymorphicInstanceId = polymorphicInstanceId(polymorphicExtension);
+
+            // case "HashIndexChange":
+            //     this.changePolymorphicTypeId("hashIndex");
+            //     return new HashIndexNode(this);
+            switchByChangeClassBuilder.addCase(
                     polymorphicExtensionClassInfo.changeClassName,
                     new BytecodeBlock()
-                            .append(constantString(polymorphicInstanceId(polymorphicExtension)))
+                            .append(constantString(polymorphicInstanceId))
+                            .invokeVirtual(changePolymorphicTypeIdMtd)
+                            .append(newInstance(
+                                    typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
+                                    convertByChangeClassMtd.getThis()
+                            ))
+                            .retObject()
+            );
+
+            // case "hashIndex":
+            //     this.changePolymorphicTypeId("hashIndex");
+            //     return new HashIndexNode(this);
+            switchByPolymorphicTypeIdBuilder.addCase(
+                    polymorphicInstanceId,
+                    new BytecodeBlock()
+                            .append(constantString(polymorphicInstanceId))
                             .invokeVirtual(changePolymorphicTypeIdMtd)
                             .append(newInstance(
                                     typeFromJavaClassName(polymorphicExtensionClassInfo.nodeClassName),
-                                    convertMtd.getThis()
+                                    convertByPolymorphicTypeIdMtd.getThis()
                             ))
                             .retObject()
             );
         }
 
-        convertMtd.getBody()
-                .append(convertMtd.getThis())
-                .append(switchBuilder.build())
+        convertByChangeClassMtd.getBody()
+                .append(convertByChangeClassMtd.getThis())
+                .append(switchByChangeClassBuilder.build())
+                .ret();
+
+        convertByPolymorphicTypeIdMtd.getBody()
+                .append(convertByPolymorphicTypeIdMtd.getThis())
+                .append(switchByPolymorphicTypeIdBuilder.build())
                 .ret();
     }
 
diff --git a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
index f2b189bc1..4b3bd8c0b 100644
--- a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
+++ b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/asm/ConfigurationAsmGeneratorTest.java
@@ -37,6 +37,7 @@ import java.util.NoSuchElementException;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import org.apache.ignite.configuration.ConfigurationReadOnlyException;
+import org.apache.ignite.configuration.ConfigurationWrongPolymorphicTypeIdException;
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
@@ -301,7 +302,19 @@ public class ConfigurationAsmGeneratorTest {
 
         // Check convert.
 
-        rootConfig.polymorphicSubCfg().change(c -> c.convert(SecondPolymorphicInstanceTestChange.class)).get(1, SECONDS);
+        rootConfig.polymorphicSubCfg()
+                .change(c -> {
+                    assertThat(c.convert(FirstPolymorphicInstanceTestChange.class), instanceOf(FirstPolymorphicInstanceTestChange.class));
+
+                    assertThat(c.convert(SecondPolymorphicInstanceTestChange.class), instanceOf(SecondPolymorphicInstanceTestChange.class));
+
+                    assertThat(c.convert(PolymorphicTestConfigurationSchema.FIRST), instanceOf(FirstPolymorphicInstanceTestChange.class));
+
+                    assertThat(c.convert(PolymorphicTestConfigurationSchema.SECOND), instanceOf(SecondPolymorphicInstanceTestChange.class));
+
+                    assertThrows(ConfigurationWrongPolymorphicTypeIdException.class, () -> c.convert(UUID.randomUUID().toString()));
+                })
+                .get(1, SECONDS);
 
         SecondPolymorphicInstanceTestConfiguration secondCfg = (SecondPolymorphicInstanceTestConfiguration) rootConfig.polymorphicSubCfg();
         assertEquals("second", secondCfg.typeId().value());
@@ -657,6 +670,12 @@ public class ConfigurationAsmGeneratorTest {
      */
     @PolymorphicConfig
     public static class PolymorphicTestConfigurationSchema {
+        static final String FIRST = "first";
+
+        static final String SECOND = "second";
+
+        static final String NON_DEFAULT = "non_default";
+
         /** Polymorphic type id field. */
         @PolymorphicId(hasDefault = true)
         public String typeId = "first";
@@ -669,7 +688,7 @@ public class ConfigurationAsmGeneratorTest {
     /**
      * First instance of the polymorphic configuration schema.
      */
-    @PolymorphicConfigInstance("first")
+    @PolymorphicConfigInstance(PolymorphicTestConfigurationSchema.FIRST)
     public static class FirstPolymorphicInstanceTestConfigurationSchema extends PolymorphicTestConfigurationSchema {
         /** Integer value. */
         @Value(hasDefault = true)
@@ -679,7 +698,7 @@ public class ConfigurationAsmGeneratorTest {
     /**
      * Second instance of the polymorphic configuration schema.
      */
-    @PolymorphicConfigInstance("second")
+    @PolymorphicConfigInstance(PolymorphicTestConfigurationSchema.SECOND)
     public static class SecondPolymorphicInstanceTestConfigurationSchema extends PolymorphicTestConfigurationSchema {
         /** Integer value. */
         @Value(hasDefault = true)
@@ -693,7 +712,7 @@ public class ConfigurationAsmGeneratorTest {
     /**
      * Instance of the polymorphic configuration schema that has a field without a default value.
      */
-    @PolymorphicConfigInstance("non_default")
+    @PolymorphicConfigInstance(PolymorphicTestConfigurationSchema.NON_DEFAULT)
     public static class NonDefaultPolymorphicInstanceTestConfigurationSchema extends PolymorphicTestConfigurationSchema {
         @Value
         public String nonDefaultValue;
diff --git a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/validation/TestValidationUtil.java b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/validation/TestValidationUtil.java
new file mode 100644
index 000000000..ea7b5d71a
--- /dev/null
+++ b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/validation/TestValidationUtil.java
@@ -0,0 +1,97 @@
+/*
+ * 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.configuration.validation;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.startsWith;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.annotation.Annotation;
+import org.apache.ignite.configuration.validation.ValidationContext;
+import org.apache.ignite.configuration.validation.ValidationIssue;
+import org.apache.ignite.configuration.validation.Validator;
+import org.jetbrains.annotations.Nullable;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Useful class for testing {@link Validator}.
+ */
+public class TestValidationUtil {
+    /**
+     * Creates mock {@link ValidationContext}.
+     *
+     * @param <VIEWT> Type of the subtree or the value that is being validated.
+     * @param oldValue Previous value of the configuration. Might be {@code null} for leaves only.
+     * @param newValue Updated value of the configuration.
+     */
+    public static <VIEWT> ValidationContext<VIEWT> mockValidationContext(@Nullable VIEWT oldValue, VIEWT newValue) {
+        ValidationContext<VIEWT> mock = mock(ValidationContext.class);
+
+        when(mock.getOldValue()).thenReturn(oldValue);
+
+        when(mock.getNewValue()).thenReturn(newValue);
+
+        return mock;
+    }
+
+    /**
+     * Adds mocking for method {@link ValidationContext#addIssue}.
+     *
+     * @param <VIEWT> Type of the subtree or the value that is being validated.
+     * @param mock Mocked validation context.
+     * @return New {@link ArgumentCaptor}.
+     */
+    public static <VIEWT> ArgumentCaptor<ValidationIssue> addIssuesCaptor(ValidationContext<VIEWT> mock) {
+        ArgumentCaptor<ValidationIssue> issuesCaptor = ArgumentCaptor.forClass(ValidationIssue.class);
+
+        doNothing().when(mock).addIssue(issuesCaptor.capture());
+
+        return issuesCaptor;
+    }
+
+    /**
+     * Performs validation checking whether there are errors.
+     *
+     * @param validator Checked validator.
+     * @param annotation Mocked annotation.
+     * @param ctx Mocked validation context.
+     * @param errorMessagePrefix Error prefix, if {@code null} it is expected that there will be no errors.
+     */
+    public static <A extends Annotation, VIEWT> void validate(
+            Validator<A, VIEWT> validator,
+            A annotation,
+            ValidationContext<VIEWT> ctx,
+            @Nullable String errorMessagePrefix
+    ) {
+        ArgumentCaptor<ValidationIssue> argumentCaptor = addIssuesCaptor(ctx);
+
+        validator.validate(annotation, ctx);
+
+        if (errorMessagePrefix == null) {
+            assertThat(argumentCaptor.getAllValues(), empty());
+        } else {
+            assertThat(argumentCaptor.getAllValues(), hasSize(1));
+
+            assertThat(argumentCaptor.getValue().message(), startsWith(errorMessagePrefix));
+        }
+    }
+}
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModule.java b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModule.java
index 42488fa6f..037569542 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModule.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModule.java
@@ -17,15 +17,22 @@
 
 package org.apache.ignite.internal.configuration;
 
+import java.lang.annotation.Annotation;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.apache.ignite.configuration.RootKey;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
 import org.apache.ignite.configuration.schemas.runner.ClusterConfiguration;
+import org.apache.ignite.configuration.schemas.store.KnownDataStorage;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
+import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.schema.configuration.KnownDataStorageValidator;
 
 /**
  * {@link ConfigurationModule} for cluster-wide configuration provided by ignite-api.
@@ -52,7 +59,14 @@ public class CoreDistributedConfigurationModule implements ConfigurationModule {
         return List.of(
                 HashIndexConfigurationSchema.class,
                 SortedIndexConfigurationSchema.class,
-                PartialIndexConfigurationSchema.class
+                PartialIndexConfigurationSchema.class,
+                UnknownDataStorageConfigurationSchema.class
         );
     }
+
+    /** {@inheritDoc} */
+    @Override
+    public Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators() {
+        return Map.of(KnownDataStorage.class, Set.of(new KnownDataStorageValidator()));
+    }
 }
diff --git a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModuleTest.java b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModuleTest.java
index 5ef3fa279..7a28ee929 100644
--- a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModuleTest.java
+++ b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/CoreDistributedConfigurationModuleTest.java
@@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
 
 import java.util.Optional;
 import java.util.ServiceLoader;
@@ -58,7 +59,7 @@ class CoreDistributedConfigurationModuleTest {
 
     @Test
     void providesNoValidators() {
-        assertThat(module.validators(), is(anEmptyMap()));
+        assertThat(module.validators(), not(anEmptyMap()));
     }
 
     @Test
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidator.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidator.java
new file mode 100644
index 000000000..5e6f5acd9
--- /dev/null
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.schema.configuration;
+
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
+
+import org.apache.ignite.configuration.schemas.store.DataStorageView;
+import org.apache.ignite.configuration.schemas.store.KnownDataStorage;
+import org.apache.ignite.configuration.validation.ValidationContext;
+import org.apache.ignite.configuration.validation.ValidationIssue;
+import org.apache.ignite.configuration.validation.Validator;
+
+/**
+ * Implementing a validator for {@link KnownDataStorage}.
+ */
+public class KnownDataStorageValidator implements Validator<KnownDataStorage, DataStorageView> {
+    /** {@inheritDoc} */
+    @Override
+    public void validate(KnownDataStorage annotation, ValidationContext<DataStorageView> ctx) {
+        DataStorageView newValue = ctx.getNewValue();
+
+        if (newValue.name().equals(UNKNOWN_DATA_STORAGE)) {
+            ctx.addIssue(new ValidationIssue(String.format(
+                    "Data storage cannot be '%s': %s",
+                    UNKNOWN_DATA_STORAGE,
+                    ctx.currentKey()
+            )));
+        }
+    }
+}
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidatorTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidatorTest.java
new file mode 100644
index 000000000..ecdd857a1
--- /dev/null
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/KnownDataStorageValidatorTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.schema.configuration;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.mockValidationContext;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.validate;
+import static org.mockito.Mockito.mock;
+
+import org.apache.ignite.configuration.schemas.store.DataStorageConfiguration;
+import org.apache.ignite.configuration.schemas.store.DataStorageView;
+import org.apache.ignite.configuration.schemas.store.KnownDataStorage;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
+import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
+import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
+import org.apache.ignite.internal.schema.configuration.schema.TestDataStorageChange;
+import org.apache.ignite.internal.schema.configuration.schema.TestDataStorageConfigurationSchema;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+/**
+ * For {@link KnownDataStorageValidator} testing.
+ */
+@ExtendWith(ConfigurationExtension.class)
+public class KnownDataStorageValidatorTest {
+    @InjectConfiguration(
+            polymorphicExtensions = {UnknownDataStorageConfigurationSchema.class, TestDataStorageConfigurationSchema.class}
+    )
+    private DataStorageConfiguration dataStorageConfig;
+
+    @Test
+    void testFailValidation() {
+        KnownDataStorage annotation = mock(KnownDataStorage.class);
+
+        DataStorageView value = dataStorageConfig.value();
+
+        validate(new KnownDataStorageValidator(), annotation, mockValidationContext(value, value), "Data storage cannot be 'unknown'");
+    }
+
+    @Test
+    void testSuccessValidation() throws Exception {
+        KnownDataStorage annotation = mock(KnownDataStorage.class);
+
+        dataStorageConfig.change(c -> c.convert(TestDataStorageChange.class)).get(1, SECONDS);
+
+        DataStorageView value = dataStorageConfig.value();
+
+        validate(new KnownDataStorageValidator(), annotation, mockValidationContext(value, value), null);
+    }
+}
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
index 433931013..366112f77 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/SchemaConfigurationConverterTest.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
@@ -37,7 +38,6 @@ import org.apache.ignite.configuration.schemas.table.TableValidator;
 import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
-import org.apache.ignite.internal.schema.configuration.schema.TestRocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.schema.SchemaBuilders;
 import org.apache.ignite.schema.definition.ColumnType;
 import org.apache.ignite.schema.definition.TableDefinition;
@@ -84,7 +84,7 @@ public class SchemaConfigurationConverterTest {
                         HashIndexConfigurationSchema.class,
                         SortedIndexConfigurationSchema.class,
                         PartialIndexConfigurationSchema.class,
-                        TestRocksDbDataStorageConfigurationSchema.class
+                        UnknownDataStorageConfigurationSchema.class
                 )
         );
 
diff --git a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/TableValidatorImplTest.java b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/TableValidatorImplTest.java
index f3163ed86..903079573 100644
--- a/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/TableValidatorImplTest.java
+++ b/modules/schema/src/test/java/org/apache/ignite/internal/schema/configuration/TableValidatorImplTest.java
@@ -17,29 +17,24 @@
 
 package org.apache.ignite.internal.schema.configuration;
 
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.doNothing;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.mockValidationContext;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.validate;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import org.apache.ignite.configuration.NamedListView;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TableValidator;
 import org.apache.ignite.configuration.schemas.table.TableView;
 import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
 import org.apache.ignite.configuration.validation.ValidationContext;
-import org.apache.ignite.configuration.validation.ValidationIssue;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
 import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
 import org.apache.ignite.internal.schema.configuration.schema.TestDataStorageConfigurationSchema;
-import org.apache.ignite.internal.schema.configuration.schema.TestRocksDbDataStorageConfigurationSchema;
-import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
 
 /**
  * TableValidatorImplTest.
@@ -59,7 +54,7 @@ public class TableValidatorImplTest {
                     HashIndexConfigurationSchema.class,
                     SortedIndexConfigurationSchema.class,
                     PartialIndexConfigurationSchema.class,
-                    TestRocksDbDataStorageConfigurationSchema.class,
+                    UnknownDataStorageConfigurationSchema.class,
                     TestDataStorageConfigurationSchema.class
             }
     )
@@ -68,33 +63,8 @@ public class TableValidatorImplTest {
     /** Tests that validator finds no issues in a simple valid configuration. */
     @Test
     public void testNoIssues() {
-        ValidationContext<NamedListView<TableView>> ctx = mockContext(null);
+        ValidationContext<NamedListView<TableView>> ctx = mockValidationContext(null, tablesCfg.tables().value());
 
-        ArgumentCaptor<ValidationIssue> issuesCaptor = validate(ctx);
-
-        assertThat(issuesCaptor.getAllValues(), is(empty()));
-    }
-
-    private ValidationContext<NamedListView<TableView>> mockContext(
-            @Nullable NamedListView<TableView> oldValue
-    ) {
-        ValidationContext<NamedListView<TableView>> ctx = mock(ValidationContext.class);
-
-        NamedListView<TableView> newValue = tablesCfg.tables().value();
-
-        when(ctx.getOldValue()).thenReturn(oldValue);
-        when(ctx.getNewValue()).thenReturn(newValue);
-
-        return ctx;
-    }
-
-    private static ArgumentCaptor<ValidationIssue> validate(ValidationContext<NamedListView<TableView>> ctx) {
-        ArgumentCaptor<ValidationIssue> issuesCaptor = ArgumentCaptor.forClass(ValidationIssue.class);
-
-        doNothing().when(ctx).addIssue(issuesCaptor.capture());
-
-        TableValidatorImpl.INSTANCE.validate(null, ctx);
-
-        return issuesCaptor;
+        validate(TableValidatorImpl.INSTANCE, mock(TableValidator.class), ctx, null);
     }
 }
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/MockedStructuresTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/MockedStructuresTest.java
index 88fd0773d..5610af96f 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/MockedStructuresTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/MockedStructuresTest.java
@@ -33,6 +33,7 @@ import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Consumer;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
@@ -49,8 +50,7 @@ import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.SchemaUtils;
 import org.apache.ignite.internal.sql.engine.SqlQueryProcessor;
 import org.apache.ignite.internal.storage.DataStorageManager;
-import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
-import org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngine;
+import org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngineFactory;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbStorageEngineConfiguration;
 import org.apache.ignite.internal.table.distributed.TableManager;
@@ -129,6 +129,7 @@ public class MockedStructuresTest extends IgniteAbstractTest {
                     HashIndexConfigurationSchema.class,
                     SortedIndexConfigurationSchema.class,
                     PartialIndexConfigurationSchema.class,
+                    UnknownDataStorageConfigurationSchema.class,
                     RocksDbDataStorageConfigurationSchema.class
             }
     )
@@ -194,13 +195,13 @@ public class MockedStructuresTest extends IgniteAbstractTest {
             });
         };
 
-        dsm = new DataStorageManager(configRegistry, workDir) {
-            /** {@inheritDoc} */
-            @Override
-            protected Iterable<StorageEngineFactory> engineFactories() {
-                return List.of((configurationRegistry, storePath) -> new RocksDbStorageEngine(rocksDbEngineConfig, storePath));
-            }
-        };
+        when(configRegistry.getConfiguration(RocksDbStorageEngineConfiguration.KEY)).thenReturn(rocksDbEngineConfig);
+
+        dsm = new DataStorageManager(
+                configRegistry,
+                workDir,
+                List.of(new RocksDbStorageEngineFactory())
+        );
 
         dsm.start();
 
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/DataStorageManager.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/DataStorageManager.java
index 4364ef922..50bd9c53f 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/DataStorageManager.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/DataStorageManager.java
@@ -17,15 +17,20 @@
 
 package org.apache.ignite.internal.storage;
 
-import static java.util.function.Function.identity;
 import static java.util.stream.Collectors.toUnmodifiableMap;
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
+import static org.apache.ignite.internal.util.CollectionUtils.first;
 
 import java.nio.file.Path;
-import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
+import java.util.function.Consumer;
 import java.util.stream.StreamSupport;
+import org.apache.ignite.configuration.schemas.store.DataStorageChange;
 import org.apache.ignite.configuration.schemas.store.DataStorageConfiguration;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TableConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TablesConfigurationSchema;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.manager.IgniteComponent;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
@@ -38,7 +43,7 @@ import org.jetbrains.annotations.Nullable;
  * Data storage manager.
  */
 public class DataStorageManager implements IgniteComponent {
-    /** Mapping: {@link StorageEngine#name} -> {@link StorageEngine}. */
+    /** Mapping: {@link StorageEngineFactory#name} -> {@link StorageEngine}. */
     private final Map<String, StorageEngine> engines;
 
     /**
@@ -46,30 +51,35 @@ public class DataStorageManager implements IgniteComponent {
      *
      * @param clusterConfigRegistry Register of the (distributed) cluster configuration.
      * @param storagePath Storage path.
-     * @throws StorageException If there are duplicates of the data storage engine.
+     * @param engineFactories Storage engine factories.
+     * @throws IllegalStateException If there are duplicates of the data storage engine.
      */
     public DataStorageManager(
             ConfigurationRegistry clusterConfigRegistry,
-            Path storagePath
+            Path storagePath,
+            Iterable<StorageEngineFactory> engineFactories
     ) {
-        this.engines = StreamSupport.stream(engineFactories().spliterator(), false)
-                .map(engineFactory -> engineFactory.createEngine(clusterConfigRegistry, storagePath))
+        engines = StreamSupport.stream(engineFactories.spliterator(), false)
                 .collect(toUnmodifiableMap(
-                        StorageEngine::name,
-                        identity(),
-                        (storageEngine1, storageEngine2) -> {
-                            throw new StorageException(String.format(
-                                    "Duplicate key [key=%s, engines=%s]",
-                                    storageEngine1.name(),
-                                    List.of(storageEngine1, storageEngine2)
-                            ));
-                        }
+                        StorageEngineFactory::name,
+                        engineFactory -> engineFactory.createEngine(clusterConfigRegistry, storagePath)
                 ));
     }
 
+    /**
+     * Constructor, overloads {@link DataStorageManager#DataStorageManager(ConfigurationRegistry, Path, Iterable)} with loading factories
+     * through a {@link ServiceLoader}.
+     */
+    public DataStorageManager(
+            ConfigurationRegistry clusterConfigRegistry,
+            Path storagePath
+    ) {
+        this(clusterConfigRegistry, storagePath, ServiceLoader.load(StorageEngineFactory.class));
+    }
+
     /** {@inheritDoc} */
     @Override
-    public void start() {
+    public void start() throws StorageException {
         engines.values().forEach(StorageEngine::start);
     }
 
@@ -80,21 +90,32 @@ public class DataStorageManager implements IgniteComponent {
     }
 
     /**
-     * Returns the storage engine factories.
+     * Returns the data storage engine by data storage configuration.
      *
-     * <p>NOTE: It is recommended to override only in tests.
+     * @param config Data storage configuration.
      */
-    protected Iterable<StorageEngineFactory> engineFactories() {
-        return ServiceLoader.load(StorageEngineFactory.class);
+    public @Nullable StorageEngine engine(DataStorageConfiguration config) {
+        return engines.get(config.value().name());
     }
 
     /**
-     * Returns the data storage engine by data storage configuration.
+     * Returns a consumer that will set the default {@link TableConfigurationSchema#dataStorage table data storage} depending on the {@link
+     * StorageEngine engine}.
      *
-     * @param config Data storage configuration.
+     * @param defaultDataStorageView View of {@link TablesConfigurationSchema#defaultDataStorage}. For the case {@link
+     *      UnknownDataStorageConfigurationSchema#UNKNOWN_DATA_STORAGE} and there is only one engine, then it will be the default,
+     *      otherwise there will be no default.
      */
-    public @Nullable StorageEngine engine(DataStorageConfiguration config) {
-        return engines.get(config.value().name());
+    public Consumer<DataStorageChange> defaultTableDataStorageConsumer(String defaultDataStorageView) {
+        return tableDataStorageChange -> {
+            if (!defaultDataStorageView.equals(UNKNOWN_DATA_STORAGE)) {
+                assert engines.containsKey(defaultDataStorageView) : defaultDataStorageView;
+
+                tableDataStorageChange.convert(defaultDataStorageView);
+            } else if (engines.size() == 1) {
+                tableDataStorageChange.convert(first(engines.keySet()));
+            }
+        };
     }
 
     /** {@inheritDoc} */
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidator.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidator.java
new file mode 100644
index 000000000..6c41e4098
--- /dev/null
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidator.java
@@ -0,0 +1,64 @@
+/*
+ * 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.storage.configuration;
+
+import static java.util.stream.Collectors.toUnmodifiableSet;
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.ignite.configuration.schemas.store.ExistingDataStorage;
+import org.apache.ignite.configuration.validation.ValidationContext;
+import org.apache.ignite.configuration.validation.ValidationIssue;
+import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
+
+/**
+ * Implementing a validator for {@link ExistingDataStorage}.
+ */
+public class ExistingDataStorageValidator implements Validator<ExistingDataStorage, String> {
+    private final Set<String> dataStorages;
+
+    /**
+     * Constructor.
+     *
+     * @param engineFactories Storage engine factories.
+     */
+    public ExistingDataStorageValidator(Iterable<StorageEngineFactory> engineFactories) {
+        dataStorages = Stream.concat(
+                Stream.of(UNKNOWN_DATA_STORAGE),
+                StreamSupport.stream(engineFactories.spliterator(), false).map(StorageEngineFactory::name)
+        ).collect(toUnmodifiableSet());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void validate(ExistingDataStorage annotation, ValidationContext<String> ctx) {
+        String newValue = ctx.getNewValue();
+
+        if (!dataStorages.contains(newValue)) {
+            ctx.addIssue(new ValidationIssue(String.format(
+                    "Non-existent data storage [dataStorage=%s, existDataStorages=%s, key=%s]",
+                    newValue,
+                    dataStorages,
+                    ctx.currentKey()
+            )));
+        }
+    }
+}
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/StorageEngineDistributedConfigurationModule.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/StorageEngineDistributedConfigurationModule.java
new file mode 100644
index 000000000..24b54d808
--- /dev/null
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/configuration/StorageEngineDistributedConfigurationModule.java
@@ -0,0 +1,45 @@
+/*
+ * 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.storage.configuration;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
+import org.apache.ignite.configuration.schemas.store.ExistingDataStorage;
+import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.configuration.ConfigurationModule;
+import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
+
+/**
+ * {@link ConfigurationModule} for cluster-wide configuration provided by ignite-storage-api.
+ */
+public class StorageEngineDistributedConfigurationModule implements ConfigurationModule {
+    /** {@inheritDoc} */
+    @Override
+    public ConfigurationType type() {
+        return ConfigurationType.DISTRIBUTED;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Map<Class<? extends Annotation>, Set<Validator<? extends Annotation, ?>>> validators() {
+        return Map.of(ExistingDataStorage.class, Set.of(new ExistingDataStorageValidator(ServiceLoader.load(StorageEngineFactory.class))));
+    }
+}
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
index f54bf46bc..255548466 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngine.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.storage.engine;
 
-import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
 import org.apache.ignite.internal.storage.StorageException;
 
@@ -25,13 +24,6 @@ import org.apache.ignite.internal.storage.StorageException;
  * General storage engine interface.
  */
 public interface StorageEngine {
-    /**
-     * Returns the unique name of the storage engine.
-     *
-     * <p>Used to map {@link DataStorageConfigurationSchema#name} to {@link StorageEngine#name}.
-     */
-    String name();
-
     /**
      * Starts the engine.
      *
diff --git a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngineFactory.java b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngineFactory.java
index 310cc72f6..0b13ecec7 100644
--- a/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngineFactory.java
+++ b/modules/storage-api/src/main/java/org/apache/ignite/internal/storage/engine/StorageEngineFactory.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.storage.engine;
 
 import java.nio.file.Path;
+import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.storage.StorageException;
 
@@ -25,6 +26,13 @@ import org.apache.ignite.internal.storage.StorageException;
  * Factory for creating storage engines.
  */
 public interface StorageEngineFactory {
+    /**
+     * Returns the unique name of the storage engine.
+     *
+     * <p>Used to map {@link DataStorageConfigurationSchema#name} to {@link StorageEngine}.
+     */
+    String name();
+
     /**
      * Creates a new storage engine.
      *
diff --git a/modules/storage-api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule b/modules/storage-api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
new file mode 100644
index 000000000..fad3807f8
--- /dev/null
+++ b/modules/storage-api/src/main/resources/META-INF/services/org.apache.ignite.internal.configuration.ConfigurationModule
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+org.apache.ignite.internal.storage.configuration.StorageEngineDistributedConfigurationModule
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/ConcurrentHashMapStorageTest.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/ConcurrentHashMapStorageTest.java
index fbdbd3b1c..9c47666ea 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/ConcurrentHashMapStorageTest.java
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/ConcurrentHashMapStorageTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.storage;
 
 import java.util.concurrent.TimeUnit;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -26,7 +27,6 @@ import org.apache.ignite.internal.storage.chm.TestConcurrentHashMapPartitionStor
 import org.apache.ignite.internal.storage.chm.TestConcurrentHashMapStorageEngine;
 import org.apache.ignite.internal.storage.chm.schema.TestConcurrentHashMapDataStorageChange;
 import org.apache.ignite.internal.storage.chm.schema.TestConcurrentHashMapDataStorageConfigurationSchema;
-import org.apache.ignite.internal.storage.chm.schema.TestRocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
 import org.apache.ignite.internal.storage.engine.TableStorage;
 import org.apache.ignite.internal.util.IgniteUtils;
@@ -48,7 +48,7 @@ public class ConcurrentHashMapStorageTest extends AbstractPartitionStorageTest {
             @InjectConfiguration(
                     polymorphicExtensions = {
                             HashIndexConfigurationSchema.class,
-                            TestRocksDbDataStorageConfigurationSchema.class,
+                            UnknownDataStorageConfigurationSchema.class,
                             TestConcurrentHashMapDataStorageConfigurationSchema.class
                     }
             ) TableConfiguration tableCfg
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/DataStorageManagerTest.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/DataStorageManagerTest.java
index 3dd08c372..c780568e6 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/DataStorageManagerTest.java
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/DataStorageManagerTest.java
@@ -17,21 +17,32 @@
 
 package org.apache.ignite.internal.storage;
 
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.nio.file.Path;
 import java.util.List;
 import java.util.UUID;
+import org.apache.ignite.configuration.schemas.store.DataStorageChange;
+import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
+import org.apache.ignite.configuration.schemas.table.TablesConfigurationSchema;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
 import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
 import org.hamcrest.Matchers;
+import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mockito;
+import org.mockito.ArgumentCaptor;
 
 /**
  * For {@link DataStorageManager} testing.
@@ -45,27 +56,94 @@ public class DataStorageManagerTest {
     void testStorageEngineDuplicate() {
         String sameName = UUID.randomUUID().toString();
 
-        StorageException exception = assertThrows(
-                StorageException.class,
-                () -> new DataStorageManager(Mockito.mock(ConfigurationRegistry.class), workDir) {
-                    /** {@inheritDoc} */
-                    @Override
-                    protected Iterable<StorageEngineFactory> engineFactories() {
-                        return List.of(
-                                (configRegistry, storagePath) -> createMockedStorageEngine(sameName),
-                                (configRegistry, storagePath) -> createMockedStorageEngine(sameName)
-                        );
-                    }
-                });
+        IllegalStateException exception = assertThrows(
+                IllegalStateException.class,
+                () -> new DataStorageManager(
+                        mock(ConfigurationRegistry.class),
+                        workDir,
+                        List.of(
+                                createMockedStorageEngineFactory(sameName),
+                                createMockedStorageEngineFactory(sameName)
+                        )
+                ));
 
         assertThat(exception.getMessage(), Matchers.startsWith("Duplicate key"));
     }
 
-    private StorageEngine createMockedStorageEngine(String name) {
-        StorageEngine mocked = Mockito.mock(StorageEngine.class);
+    @Test
+    void testDefaultTableDataStorageConsumerSingleStorageEngine() {
+        String engineName = UUID.randomUUID().toString();
+
+        DataStorageManager dataStorageManager = new DataStorageManager(
+                mock(ConfigurationRegistry.class),
+                workDir,
+                List.of(createMockedStorageEngineFactory(engineName))
+        );
+
+        checkDefaultTableDataStorageConsumer(dataStorageManager, engineName, engineName);
+
+        checkDefaultTableDataStorageConsumer(dataStorageManager, UNKNOWN_DATA_STORAGE, engineName);
+    }
+
+    @Test
+    void testDefaultTableDataStorageConsumerMultipleStorageEngines() {
+        String engineName1 = UUID.randomUUID().toString();
+        String engineName2 = UUID.randomUUID().toString();
+
+        DataStorageManager dataStorageManager = new DataStorageManager(
+                mock(ConfigurationRegistry.class),
+                workDir,
+                List.of(
+                        createMockedStorageEngineFactory(engineName1),
+                        createMockedStorageEngineFactory(engineName2)
+                )
+        );
+
+        checkDefaultTableDataStorageConsumer(dataStorageManager, engineName1, engineName1);
+
+        checkDefaultTableDataStorageConsumer(dataStorageManager, engineName2, engineName2);
+
+        checkDefaultTableDataStorageConsumer(dataStorageManager, UNKNOWN_DATA_STORAGE, null);
+    }
+
+    /**
+     * Checks that the consumer from {@link DataStorageManager#defaultTableDataStorageConsumer} will correctly set the {@link
+     * DataStorageConfigurationSchema data storage} type via {@link DataStorageChange#convert(String)}.
+     *
+     * @param dataStorageManager Data storage manager.
+     * @param defaultDataStorageView One of the options for the value of the {@link TablesConfigurationSchema#defaultDataStorage}.
+     * @param expPolymorphicTypeId Expected value that should get into the {@link DataStorageChange#convert(String)}. {@code null} if
+     *      nothing should go into the method.
+     */
+    private static void checkDefaultTableDataStorageConsumer(
+            DataStorageManager dataStorageManager,
+            String defaultDataStorageView,
+            @Nullable String expPolymorphicTypeId
+    ) {
+        DataStorageChange mock = mock(DataStorageChange.class);
+
+        ArgumentCaptor<String> stringArgumentCaptor = ArgumentCaptor.forClass(String.class);
+
+        when(mock.convert(stringArgumentCaptor.capture())).thenReturn(mock);
+
+        dataStorageManager.defaultTableDataStorageConsumer(defaultDataStorageView).accept(mock);
+
+        if (expPolymorphicTypeId != null) {
+            assertThat(stringArgumentCaptor.getAllValues(), hasSize(1));
+
+            assertThat(stringArgumentCaptor.getValue(), equalTo(expPolymorphicTypeId));
+        } else {
+            assertThat(stringArgumentCaptor.getAllValues(), empty());
+        }
+    }
+
+    private static StorageEngineFactory createMockedStorageEngineFactory(String name) {
+        StorageEngineFactory mock = mock(StorageEngineFactory.class);
+
+        when(mock.name()).thenReturn(name);
 
-        Mockito.when(mocked.name()).thenReturn(name);
+        when(mock.createEngine(any(), any())).thenReturn(mock(StorageEngine.class));
 
-        return mocked;
+        return mock;
     }
 }
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngine.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngine.java
index 5187f0080..bbc14befc 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngine.java
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngine.java
@@ -30,12 +30,6 @@ public class TestConcurrentHashMapStorageEngine implements StorageEngine {
     /** Engine name. */
     public static final String ENGINE_NAME = "test_chm";
 
-    /** {@inheritDoc} */
-    @Override
-    public String name() {
-        return ENGINE_NAME;
-    }
-
     /** {@inheritDoc} */
     @Override
     public void start() throws StorageException {
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngineFactory.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngineFactory.java
index 8f0ac697f..eaae7f765 100644
--- a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngineFactory.java
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/chm/TestConcurrentHashMapStorageEngineFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.storage.chm;
 
+import static org.apache.ignite.internal.storage.chm.TestConcurrentHashMapStorageEngine.ENGINE_NAME;
+
 import java.nio.file.Path;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.storage.StorageException;
@@ -27,6 +29,12 @@ import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
  * Implementation for creating {@link TestConcurrentHashMapStorageEngine}s.
  */
 public class TestConcurrentHashMapStorageEngineFactory implements StorageEngineFactory {
+    /** {@inheritDoc} */
+    @Override
+    public String name() {
+        return ENGINE_NAME;
+    }
+
     /** {@inheritDoc} */
     @Override
     public StorageEngine createEngine(ConfigurationRegistry configRegistry, Path storagePath) throws StorageException {
diff --git a/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidatorTest.java b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidatorTest.java
new file mode 100644
index 000000000..cbfd111a1
--- /dev/null
+++ b/modules/storage-api/src/test/java/org/apache/ignite/internal/storage/configuration/ExistingDataStorageValidatorTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.storage.configuration;
+
+import static org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema.UNKNOWN_DATA_STORAGE;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.mockValidationContext;
+import static org.apache.ignite.internal.configuration.validation.TestValidationUtil.validate;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.configuration.schemas.store.ExistingDataStorage;
+import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
+import org.junit.jupiter.api.Test;
+
+/**
+ * For {@link ExistingDataStorageValidator} testing.
+ */
+public class ExistingDataStorageValidatorTest {
+    @Test
+    void testFailValidation() {
+        ExistingDataStorage annotation = mock(ExistingDataStorage.class);
+
+        String dataStorage1 = UUID.randomUUID().toString();
+        String dataStorage2 = UUID.randomUUID().toString();
+
+        ExistingDataStorageValidator validator = new ExistingDataStorageValidator(List.of(
+                createMockedStorageEngineFactory(dataStorage1),
+                createMockedStorageEngineFactory(dataStorage2)
+        ));
+
+        String value = UUID.randomUUID().toString();
+
+        validate(validator, annotation, mockValidationContext(value, value), "Non-existent data storage");
+    }
+
+    @Test
+    void testSuccessValidation() {
+        ExistingDataStorage annotation = mock(ExistingDataStorage.class);
+
+        String dataStorage1 = UUID.randomUUID().toString();
+        String dataStorage2 = UUID.randomUUID().toString();
+
+        ExistingDataStorageValidator validator = new ExistingDataStorageValidator(List.of(
+                createMockedStorageEngineFactory(dataStorage1),
+                createMockedStorageEngineFactory(dataStorage2)
+        ));
+
+        validate(validator, annotation, mockValidationContext(UNKNOWN_DATA_STORAGE, UNKNOWN_DATA_STORAGE), null);
+        validate(validator, annotation, mockValidationContext(dataStorage1, dataStorage1), null);
+        validate(validator, annotation, mockValidationContext(dataStorage2, dataStorage2), null);
+    }
+
+    private StorageEngineFactory createMockedStorageEngineFactory(String name) {
+        StorageEngineFactory mock = mock(StorageEngineFactory.class);
+
+        when(mock.name()).thenReturn(name);
+
+        return mock;
+    }
+}
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngine.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngine.java
index 6f4b68e43..286b3fbb8 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngine.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngine.java
@@ -59,12 +59,6 @@ public class PageMemoryStorageEngine implements StorageEngine {
         this.ioRegistry = ioRegistry;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String name() {
-        return ENGINE_NAME;
-    }
-
     /** {@inheritDoc} */
     @Override
     public void start() {
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngineFactory.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngineFactory.java
index b11737aa3..a4206ea44 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngineFactory.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PageMemoryStorageEngineFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.storage.pagememory;
 
+import static org.apache.ignite.internal.storage.pagememory.PageMemoryStorageEngine.ENGINE_NAME;
+
 import java.nio.file.Path;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.pagememory.io.PageIoRegistry;
@@ -29,6 +31,12 @@ import org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMe
  * Implementation for creating {@link PageMemoryStorageEngine}s.
  */
 public class PageMemoryStorageEngineFactory implements StorageEngineFactory {
+    /** {@inheritDoc} */
+    @Override
+    public String name() {
+        return ENGINE_NAME;
+    }
+
     /** {@inheritDoc} */
     @Override
     public StorageEngine createEngine(ConfigurationRegistry configRegistry, Path storagePath) throws StorageException {
diff --git a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PageMemoryPartitionStorageTest.java b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PageMemoryPartitionStorageTest.java
index 89098bede..874d10264 100644
--- a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PageMemoryPartitionStorageTest.java
+++ b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/PageMemoryPartitionStorageTest.java
@@ -27,6 +27,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import java.nio.file.Path;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -42,7 +43,6 @@ import org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMe
 import org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMemoryDataStorageView;
 import org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMemoryStorageEngineConfiguration;
 import org.apache.ignite.internal.storage.pagememory.configuration.schema.PageMemoryStorageEngineConfigurationSchema;
-import org.apache.ignite.internal.storage.pagememory.configuration.schema.TestRocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
 import org.apache.ignite.internal.util.IgniteUtils;
@@ -70,7 +70,7 @@ public class PageMemoryPartitionStorageTest extends AbstractPartitionStorageTest
             name = "table",
             polymorphicExtensions = {
                     HashIndexConfigurationSchema.class,
-                    TestRocksDbDataStorageConfigurationSchema.class,
+                    UnknownDataStorageConfigurationSchema.class,
                     PageMemoryDataStorageConfigurationSchema.class
             }
     )
diff --git a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java
deleted file mode 100644
index b79f3b3d8..000000000
--- a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/configuration/schema/TestRocksDbDataStorageConfigurationSchema.java
+++ /dev/null
@@ -1,30 +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.storage.pagememory.configuration.schema;
-
-import static org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema.DEFAULT_DATA_STORAGE_NAME;
-
-import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
-import org.apache.ignite.configuration.schemas.store.DataStorageConfigurationSchema;
-
-/**
- * Test RocksDB data storage configuration schema for tables.
- */
-@PolymorphicConfigInstance(DEFAULT_DATA_STORAGE_NAME)
-public class TestRocksDbDataStorageConfigurationSchema extends DataStorageConfigurationSchema {
-}
diff --git a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngine.java b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngine.java
index 82531eb31..119875e7c 100644
--- a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngine.java
+++ b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngine.java
@@ -71,12 +71,6 @@ public class RocksDbStorageEngine implements StorageEngine {
         this.storagePath = storagePath;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public String name() {
-        return ENGINE_NAME;
-    }
-
     /** {@inheritDoc} */
     @Override
     public void start() throws StorageException {
diff --git a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngineFactory.java b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngineFactory.java
index 1550a5130..02117fd81 100644
--- a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngineFactory.java
+++ b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/RocksDbStorageEngineFactory.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.storage.rocksdb;
 
+import static org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngine.ENGINE_NAME;
+
 import java.nio.file.Path;
 import org.apache.ignite.internal.configuration.ConfigurationRegistry;
 import org.apache.ignite.internal.storage.StorageException;
@@ -28,6 +30,12 @@ import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbSt
  * Implementation for creating {@link RocksDbStorageEngine}s.
  */
 public class RocksDbStorageEngineFactory implements StorageEngineFactory {
+    /** {@inheritDoc} */
+    @Override
+    public String name() {
+        return ENGINE_NAME;
+    }
+
     /** {@inheritDoc} */
     @Override
     public StorageEngine createEngine(ConfigurationRegistry configRegistry, Path storagePath) throws StorageException {
diff --git a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbPartitionStorageTest.java b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbPartitionStorageTest.java
index 517ebddfe..5fe74d3b4 100644
--- a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbPartitionStorageTest.java
+++ b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbPartitionStorageTest.java
@@ -25,6 +25,7 @@ import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import java.nio.file.Path;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -32,7 +33,7 @@ import org.apache.ignite.internal.configuration.testframework.InjectConfiguratio
 import org.apache.ignite.internal.storage.AbstractPartitionStorageTest;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
 import org.apache.ignite.internal.storage.engine.TableStorage;
-import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfiguration;
+import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageChange;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageView;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbStorageEngineConfiguration;
@@ -59,10 +60,14 @@ public class RocksDbPartitionStorageTest extends AbstractPartitionStorageTest {
             @InjectConfiguration RocksDbStorageEngineConfiguration engineConfig,
             @InjectConfiguration(
                     name = "table",
-                    polymorphicExtensions = {HashIndexConfigurationSchema.class, RocksDbDataStorageConfigurationSchema.class}
+                    polymorphicExtensions = {
+                            HashIndexConfigurationSchema.class,
+                            UnknownDataStorageConfigurationSchema.class,
+                            RocksDbDataStorageConfigurationSchema.class
+                    }
             ) TableConfiguration tableCfg
     ) throws Exception {
-        assertThat(tableCfg.dataStorage(), is(instanceOf(RocksDbDataStorageConfiguration.class)));
+        tableCfg.dataStorage().change(c -> c.convert(RocksDbDataStorageChange.class)).get(1, SECONDS);
 
         assertThat(((RocksDbDataStorageView) tableCfg.dataStorage().value()).dataRegion(), equalTo(DEFAULT_DATA_REGION_NAME));
 
diff --git a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbTableStorageTest.java b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbTableStorageTest.java
index 356f43669..152092848 100644
--- a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbTableStorageTest.java
+++ b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/RocksDbTableStorageTest.java
@@ -34,6 +34,8 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -42,7 +44,7 @@ import org.apache.ignite.internal.storage.PartitionStorage;
 import org.apache.ignite.internal.storage.basic.SimpleDataRow;
 import org.apache.ignite.internal.storage.engine.StorageEngine;
 import org.apache.ignite.internal.storage.engine.TableStorage;
-import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfiguration;
+import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageChange;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageView;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbStorageEngineConfiguration;
@@ -73,21 +75,27 @@ public class RocksDbTableStorageTest {
             @InjectConfiguration RocksDbStorageEngineConfiguration rocksDbEngineConfig,
             @InjectConfiguration(
                     name = "table",
-                    polymorphicExtensions = {HashIndexConfigurationSchema.class, RocksDbDataStorageConfigurationSchema.class}
+                    polymorphicExtensions = {
+                            HashIndexConfigurationSchema.class,
+                            UnknownDataStorageConfigurationSchema.class,
+                            RocksDbDataStorageConfigurationSchema.class
+                    }
             ) TableConfiguration tableCfg
     ) throws Exception {
-        assertThat(tableCfg.dataStorage(), is(instanceOf(RocksDbDataStorageConfiguration.class)));
+        CompletableFuture<Void> changeDataStorageFuture = tableCfg.dataStorage().change(c -> c.convert(RocksDbDataStorageChange.class));
+
+        assertThat(changeDataStorageFuture, willBe(nullValue(Void.class)));
 
         assertThat(((RocksDbDataStorageView) tableCfg.dataStorage().value()).dataRegion(), equalTo(DEFAULT_DATA_REGION_NAME));
 
-        CompletableFuture<Void> changeFuture = rocksDbEngineConfig.defaultRegion()
+        CompletableFuture<Void> changeEngineFuture = rocksDbEngineConfig.defaultRegion()
                 .change(c -> c.changeSize(16 * 1024).changeWriteBufferSize(16 * 1024));
 
-        assertThat(changeFuture, willBe(nullValue(Void.class)));
+        assertThat(changeEngineFuture, willBe(nullValue(Void.class)));
 
-        changeFuture = tableCfg.change(cfg -> cfg.changePartitions(512));
+        changeEngineFuture = tableCfg.change(cfg -> cfg.changePartitions(512));
 
-        assertThat(changeFuture, willBe(nullValue(Void.class)));
+        assertThat(changeEngineFuture, willBe(nullValue(Void.class)));
 
         engine = new RocksDbStorageEngine(rocksDbEngineConfig, workDir);
 
@@ -202,6 +210,8 @@ public class RocksDbTableStorageTest {
 
         storage.stop();
 
+        tableCfg.dataStorage().change(c -> c.convert(RocksDbDataStorageChange.class)).get(1, TimeUnit.SECONDS);
+
         storage = engine.createTable(tableCfg);
 
         storage.start();
diff --git a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorageTest.java b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorageTest.java
index 1085e9334..4f2532e72 100644
--- a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorageTest.java
+++ b/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorageTest.java
@@ -35,7 +35,6 @@ import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
@@ -48,6 +47,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.function.Predicate;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableConfiguration;
@@ -62,7 +62,7 @@ import org.apache.ignite.internal.storage.index.IndexRowPrefix;
 import org.apache.ignite.internal.storage.index.SortedIndexDescriptor.ColumnDescriptor;
 import org.apache.ignite.internal.storage.index.SortedIndexStorage;
 import org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngine;
-import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfiguration;
+import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageChange;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageView;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbStorageEngineConfiguration;
@@ -106,6 +106,7 @@ public class RocksDbSortedIndexStorageTest {
     @InjectConfiguration(polymorphicExtensions = {
             HashIndexConfigurationSchema.class,
             SortedIndexConfigurationSchema.class,
+            UnknownDataStorageConfigurationSchema.class,
             RocksDbDataStorageConfigurationSchema.class
     }, name = "table")
     private TableConfiguration tableCfg;
@@ -119,7 +120,7 @@ public class RocksDbSortedIndexStorageTest {
     void setUp(
             @WorkDirectory Path workDir,
             @InjectConfiguration RocksDbStorageEngineConfiguration rocksDbEngineConfig
-    ) {
+    ) throws Exception {
         long seed = System.currentTimeMillis();
 
         log.info("Using random seed: " + seed);
@@ -144,7 +145,7 @@ public class RocksDbSortedIndexStorageTest {
     /**
      * Configures a test table with columns of all supported types.
      */
-    private void createTestConfiguration(RocksDbStorageEngineConfiguration rocksDbEngineConfig) {
+    private void createTestConfiguration(RocksDbStorageEngineConfiguration rocksDbEngineConfig) throws Exception {
         CompletableFuture<Void> dataRegionChangeFuture = rocksDbEngineConfig.defaultRegion()
                 .change(c -> c.changeSize(16 * 1024).changeWriteBufferSize(16 * 1024));
 
@@ -155,7 +156,9 @@ public class RocksDbSortedIndexStorageTest {
                 .withPrimaryKey(ALL_TYPES_COLUMN_DEFINITIONS.get(0).name())
                 .build();
 
-        assertThat(tableCfg.dataStorage(), is(instanceOf(RocksDbDataStorageConfiguration.class)));
+        CompletableFuture<Void> dataStorageChangeFuture = tableCfg.dataStorage().change(c -> c.convert(RocksDbDataStorageChange.class));
+
+        assertThat(dataStorageChangeFuture, willBe(nullValue(Void.class)));
 
         assertThat(((RocksDbDataStorageView) tableCfg.dataStorage().value()).dataRegion(), equalTo(DEFAULT_DATA_REGION_NAME));
 
diff --git a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
index 79594bcd4..19209ea0d 100644
--- a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
+++ b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/TableManager.java
@@ -738,23 +738,27 @@ public class TableManager extends Producer<TableEvent, TableEventParameters> imp
             if (tbl != null) {
                 tblFut.completeExceptionally(new TableAlreadyExistsException(name));
             } else {
-                tablesCfg.tables().change(change -> {
-                    if (change.get(name) != null) {
+                tablesCfg.change(tablesChange -> tablesChange.changeTables(tablesListChange -> {
+                    if (tablesListChange.get(name) != null) {
                         throw new TableAlreadyExistsException(name);
                     }
 
-                    change.create(name, (ch) -> {
-                        tableInitChange.accept(ch);
+                    tablesListChange.create(name, (tableChange) -> {
+                        tableChange.changeDataStorage(
+                                dataStorageMgr.defaultTableDataStorageConsumer(tablesChange.defaultDataStorage())
+                        );
 
-                        var extConfCh = ((ExtendedTableChange) ch);
+                        tableInitChange.accept(tableChange);
+
+                        var extConfCh = ((ExtendedTableChange) tableChange);
 
                         tableCreateFuts.put(extConfCh.id(), tblFut);
 
                         // Affinity assignments calculation.
                         extConfCh.changeAssignments(ByteUtils.toBytes(AffinityUtils.calculateAssignments(
                                         baselineMgr.nodes(),
-                                        ch.partitions(),
-                                        ch.replicas())))
+                                        tableChange.partitions(),
+                                        tableChange.replicas())))
                                 // Table schema preparation.
                                 .changeSchemas(schemasCh -> schemasCh.create(
                                         String.valueOf(INITIAL_SCHEMA_VERSION),
@@ -766,8 +770,8 @@ public class TableManager extends Producer<TableEvent, TableEventParameters> imp
                                             // prepareSchemaDescriptor() method.
                                             try {
                                                 schemaDesc = SchemaUtils.prepareSchemaDescriptor(
-                                                        ((ExtendedTableView) ch).schemas().size(),
-                                                        ch);
+                                                        ((ExtendedTableView) tableChange).schemas().size(),
+                                                        tableChange);
                                             } catch (IllegalArgumentException ex) {
                                                 throw new ConfigurationValidationException(ex.getMessage());
                                             }
@@ -776,7 +780,7 @@ public class TableManager extends Producer<TableEvent, TableEventParameters> imp
                                         }
                                 ));
                     });
-                }).exceptionally(t -> {
+                })).exceptionally(t -> {
                     Throwable ex = getRootCause(t);
 
                     if (ex instanceof TableAlreadyExistsException) {
diff --git a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
index d99422b89..8bbf17915 100644
--- a/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
+++ b/modules/table/src/test/java/org/apache/ignite/internal/table/TableManagerTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.table;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -43,10 +45,13 @@ import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Phaser;
 import java.util.function.Consumer;
+import org.apache.ignite.configuration.NamedListView;
+import org.apache.ignite.configuration.schemas.store.UnknownDataStorageConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.HashIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.PartialIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.SortedIndexConfigurationSchema;
 import org.apache.ignite.configuration.schemas.table.TableChange;
+import org.apache.ignite.configuration.schemas.table.TableView;
 import org.apache.ignite.configuration.schemas.table.TablesConfiguration;
 import org.apache.ignite.internal.affinity.AffinityUtils;
 import org.apache.ignite.internal.baseline.BaselineManager;
@@ -64,8 +69,9 @@ import org.apache.ignite.internal.schema.SchemaUtils;
 import org.apache.ignite.internal.schema.configuration.SchemaConfigurationConverter;
 import org.apache.ignite.internal.schema.marshaller.schema.SchemaSerializerImpl;
 import org.apache.ignite.internal.storage.DataStorageManager;
-import org.apache.ignite.internal.storage.engine.StorageEngineFactory;
 import org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngine;
+import org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngineFactory;
+import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageChange;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbDataStorageConfigurationSchema;
 import org.apache.ignite.internal.storage.rocksdb.configuration.schema.RocksDbStorageEngineConfiguration;
 import org.apache.ignite.internal.table.distributed.TableManager;
@@ -158,6 +164,7 @@ public class TableManagerTest extends IgniteAbstractTest {
                     HashIndexConfigurationSchema.class,
                     SortedIndexConfigurationSchema.class,
                     PartialIndexConfigurationSchema.class,
+                    UnknownDataStorageConfigurationSchema.class,
                     RocksDbDataStorageConfigurationSchema.class
             }
     )
@@ -214,7 +221,7 @@ public class TableManagerTest extends IgniteAbstractTest {
      * Tests a table which was preconfigured.
      */
     @Test
-    public void testPreconfiguredTable()  throws Exception {
+    public void testPreconfiguredTable() throws Exception {
         when(rm.updateRaftGroup(any(), any(), any(), any())).thenAnswer(mock ->
                 CompletableFuture.completedFuture(mock(RaftGroupService.class)));
 
@@ -239,10 +246,12 @@ public class TableManagerTest extends IgniteAbstractTest {
 
         tblsCfg.tables().change(tablesChange -> {
             tablesChange.create(scmTbl.canonicalName(), tableChange -> {
-                ((ExtendedTableChange) SchemaConfigurationConverter.convert(scmTbl, tableChange))
+                (SchemaConfigurationConverter.convert(scmTbl, tableChange))
                         .changeReplicas(REPLICAS)
                         .changePartitions(PARTITIONS);
 
+                tableChange.changeDataStorage(c -> c.convert(RocksDbDataStorageChange.class));
+
                 var extConfCh = ((ExtendedTableChange) tableChange);
 
                 ArrayList<List<ClusterNode>> assignment = new ArrayList<>(PARTITIONS);
@@ -268,6 +277,8 @@ public class TableManagerTest extends IgniteAbstractTest {
         assertEquals(1, tableManager.tables().size());
 
         assertNotNull(tableManager.table(scmTbl.canonicalName()));
+
+        checkTableDataStorage(tblsCfg.tables().value(), RocksDbStorageEngine.ENGINE_NAME);
     }
 
     /**
@@ -287,6 +298,8 @@ public class TableManagerTest extends IgniteAbstractTest {
         assertNotNull(table);
 
         assertSame(table, tblManagerFut.join().table(scmTbl.canonicalName()));
+
+        checkTableDataStorage(tblsCfg.tables().value(), RocksDbStorageEngine.ENGINE_NAME);
     }
 
     /**
@@ -328,9 +341,9 @@ public class TableManagerTest extends IgniteAbstractTest {
 
         Consumer<TableChange> createTableChange = (TableChange change) ->
                 SchemaConfigurationConverter.convert(SchemaBuilders.tableBuilder("PUBLIC", DYNAMIC_TABLE_FOR_DROP_NAME).columns(
-                        SchemaBuilders.column("key", ColumnType.INT64).build(),
-                        SchemaBuilders.column("val", ColumnType.INT64).asNullable(true).build()
-                ).withPrimaryKey("key").build(), change)
+                                SchemaBuilders.column("key", ColumnType.INT64).build(),
+                                SchemaBuilders.column("val", ColumnType.INT64).asNullable(true).build()
+                        ).withPrimaryKey("key").build(), change)
                         .changeReplicas(REPLICAS)
                         .changePartitions(PARTITIONS);
 
@@ -481,7 +494,7 @@ public class TableManagerTest extends IgniteAbstractTest {
      * Instantiates Table manager and creates a table in it.
      *
      * @param tableDefinition Configuration schema for a table.
-     * @param tblManagerFut   Future for table manager.
+     * @param tblManagerFut Future for table manager.
      * @return Table.
      * @throws NodeStoppingException If something went wrong.
      */
@@ -496,8 +509,8 @@ public class TableManagerTest extends IgniteAbstractTest {
      * Instantiates a table and prepares Table manager. When the latch would open, the method completes.
      *
      * @param tableDefinition Configuration schema for a table.
-     * @param tblManagerFut   Future for table manager.
-     * @param phaser          Phaser for the wait.
+     * @param tblManagerFut Future for table manager.
+     * @param phaser Phaser for the wait.
      * @return Table manager.
      * @throws NodeStoppingException If something went wrong.
      */
@@ -596,20 +609,26 @@ public class TableManagerTest extends IgniteAbstractTest {
     }
 
     private DataStorageManager createDataStorageManager(
-            ConfigurationRegistry registry,
+            ConfigurationRegistry mockedRegistry,
             Path storagePath,
             RocksDbStorageEngineConfiguration config
     ) {
-        DataStorageManager manager = new DataStorageManager(registry, storagePath) {
-            /** {@inheritDoc} */
-            @Override
-            protected Iterable<StorageEngineFactory> engineFactories() {
-                return List.of((registry1, storagePath1) -> new RocksDbStorageEngine(config, storagePath1));
-            }
-        };
+        when(mockedRegistry.getConfiguration(RocksDbStorageEngineConfiguration.KEY)).thenReturn(config);
+
+        DataStorageManager manager = new DataStorageManager(
+                mockedRegistry,
+                storagePath,
+                List.of(new RocksDbStorageEngineFactory())
+        );
 
         manager.start();
 
         return manager;
     }
+
+    private void checkTableDataStorage(NamedListView<TableView> tables, String expDataStorage) {
+        for (String tableName : tables.namedListKeys()) {
+            assertThat(tables.get(tableName).dataStorage().name(), equalTo(expDataStorage));
+        }
+    }
 }