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 98ef53407b0 IGNITE-25043 Support configuration rename (#5816)
98ef53407b0 is described below

commit 98ef53407b0b502f76e9264f92818641ba03dcca
Author: Phillippko <[email protected]>
AuthorDate: Tue May 27 08:54:25 2025 +0200

    IGNITE-25043 Support configuration rename (#5816)
---
 .../configuration/annotation/PublicName.java       |  12 +-
 .../configuration/ConfigurationChanger.java        |  34 ++-
 .../asm/ConfigurationAsmGenerator.java             |  16 +-
 .../configuration/asm/InnerNodeAsmGenerator.java   |  58 +++-
 .../internal/configuration/storage/Data.java       |   9 +-
 .../configuration/util/ConfigurationFlattener.java |  28 +-
 .../util/KeysTrackingConfigurationVisitor.java     |  95 +++++-
 .../configuration/RenamedConfigurationTest.java    | 320 +++++++++++++++++++++
 .../deprecation/DeprecatedConfigurationTest.java   | 122 ++++++++
 .../configuration/util/ConfigurationUtilTest.java  |   2 +-
 .../storage/TestConfigurationStorage.java          |   3 +-
 .../storage/LocalFileConfigurationStorage.java     |   2 +-
 .../storage/LocalFileConfigurationStorageTest.java |  22 +-
 13 files changed, 683 insertions(+), 40 deletions(-)

diff --git 
a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PublicName.java
 
b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PublicName.java
index 32654789537..8df4827f521 100644
--- 
a/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PublicName.java
+++ 
b/modules/configuration-api/src/main/java/org/apache/ignite/configuration/annotation/PublicName.java
@@ -33,7 +33,15 @@ import java.lang.annotation.Target;
 @Documented
 public @interface PublicName {
     /**
-     * Public configuration property name.
+     * Public configuration property name. This name, if present, is used to 
store this configuration in configuration storage and to render
+     * the configuration to the user. Empty string means that public name 
matches the schema field name.
      */
-    String value();
+    String value() default "";
+
+    /**
+     * An array of old deprecated names for the configuration. Any of these 
names should be accounted for when parsing configuration from
+     * any source, but avoided when showing to the user or saving to the 
configuration storage. These names should also be deleted from the
+     * corresponding configuration storage upon encountering.
+     */
+    String[] legacyNames() default {};
 }
diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
index 35ff2b8b4a8..e4c40dbd1f9 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/ConfigurationChanger.java
@@ -71,6 +71,7 @@ import 
org.apache.ignite.internal.configuration.tree.ConstructableTreeNode;
 import org.apache.ignite.internal.configuration.tree.InnerNode;
 import org.apache.ignite.internal.configuration.tree.NamedListNode;
 import org.apache.ignite.internal.configuration.util.ConfigurationUtil;
+import 
org.apache.ignite.internal.configuration.util.KeysTrackingConfigurationVisitor;
 import 
org.apache.ignite.internal.configuration.validation.ConfigurationValidator;
 import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.lang.NodeStoppingException;
@@ -274,7 +275,7 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
             throw new ConfigurationChangeException("Failed to initialize 
configuration: " + e.getMessage(), e);
         }
 
-        Map<String, ? extends Serializable> storageValues = data.values();
+        var storageValues = new HashMap<String, Serializable>(data.values());
 
         ignoredKeys = ignoreDeleted(storageValues, keyIgnorer);
 
@@ -635,9 +636,11 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
 
             migrator.migrate(new SuperRootChangeImpl(changes));
 
-            Map<String, Serializable> allChanges = 
createFlattenedUpdatesMap(localRoots.rootsWithoutDefaults, changes);
-
-            dropUnnecessarilyDeletedKeys(allChanges, localRoots);
+            Map<String, Serializable> allChanges = createFlattenedUpdatesMap(
+                    localRoots.rootsWithoutDefaults,
+                    changes,
+                    localRoots.data.values()
+            );
 
             if (onStartup) {
                 for (String ignoredValue : ignoredKeys) {
@@ -645,6 +648,8 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
                 }
             }
 
+            dropUnnecessarilyDeletedKeys(allChanges, localRoots);
+
             if (allChanges.isEmpty() && onStartup) {
                 // We don't want an empty storage update if this is the 
initialization changer.
                 return nullCompletedFuture();
@@ -695,16 +700,18 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
             StorageRoots oldStorageRoots = storageRoots;
 
             try {
-                Map<String, ? extends Serializable> changedValues = 
changedEntries.values();
-
-                // We need to ignore deletion of deprecated values.
-                ignoreDeleted(changedValues, keyIgnorer);
+                var changedValues = new HashMap<String, 
Serializable>(changedEntries.values());
 
                 SuperRoot oldSuperRoot = oldStorageRoots.roots;
                 SuperRoot oldSuperRootNoDefaults = 
oldStorageRoots.rootsWithoutDefaults;
                 SuperRoot newSuperRoot = oldSuperRoot.copy();
                 SuperRoot newSuperNoDefaults = oldSuperRootNoDefaults.copy();
 
+                // We need to ignore deletion of deprecated values.
+                ignoreDeleted(changedValues, keyIgnorer);
+                // We need to ignore deletion of legacy values.
+                ignoreLegacyKeys(oldStorageRoots, changedValues);
+
                 Map<String, ?> dataValuesPrefixMap = 
toPrefixMap(changedValues);
 
                 compressDeletedEntries(dataValuesPrefixMap);
@@ -760,6 +767,17 @@ public abstract class ConfigurationChanger implements 
DynamicConfigurationChange
         return new Data(newState, delta.changeId());
     }
 
+    private static void ignoreLegacyKeys(StorageRoots oldStorageRoots, 
Map<String, ? extends Serializable> changedValues) {
+        oldStorageRoots.roots.traverseChildren(new 
KeysTrackingConfigurationVisitor<>() {
+            @Override
+            protected Object doVisitLeafNode(Field field, String key, 
Serializable val) {
+                processLegacyPaths(changedValues::remove);
+
+                return null;
+            }
+        }, true);
+    }
+
     /**
      * Remove keys from {@code allChanges}, that are associated with nulls in 
this map, and already absent in {@link StorageRoots#data}.
      */
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 63e113b077d..d41ce89e85e 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
@@ -43,6 +43,7 @@ import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.is
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.isPolymorphicId;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.polymorphicInstanceId;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.schemaFields;
+import static org.apache.ignite.internal.util.ArrayUtils.STRING_EMPTY_ARRAY;
 import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
 import static org.apache.ignite.internal.util.CollectionUtils.concat;
 import static org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
@@ -487,7 +488,20 @@ public class ConfigurationAsmGenerator {
     public static String publicName(Field f) {
         PublicName annotation = f.getAnnotation(PublicName.class);
 
-        return annotation == null ? f.getName() : annotation.value();
+        return annotation == null || annotation.value().isEmpty() ? 
f.getName() : annotation.value();
+    }
+
+    /**
+     * Return fields legacy names. If schema field contains {@link PublicName} 
annotation, this method will return its
+     * {@link PublicName#legacyNames()} ()}. Otherwise it will return empty 
array.
+     *
+     * @param f Configuration schema field.
+     * @return User-facing configuration legacy names.
+     */
+    public static String[] legacyNames(Field f) {
+        PublicName annotation = f.getAnnotation(PublicName.class);
+
+        return annotation == null ? STRING_EMPTY_ARRAY : 
annotation.legacyNames();
     }
 
     /**
diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
index d1841df1a53..f8e940432d4 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/asm/InnerNodeAsmGenerator.java
@@ -48,6 +48,7 @@ import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGener
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.fieldName;
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.getThisFieldCode;
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.internalName;
+import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.legacyNames;
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.nodeClassInterfaces;
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.polymorphicIdField;
 import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.publicName;
@@ -1188,6 +1189,7 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator {
                 );
 
                 // this.changePolymorphicTypeId(src == null ? null : 
src.unwrap(FieldType.class));
+
                 switchBuilder.addCase(
                         publicName,
                         new BytecodeBlock()
@@ -1196,11 +1198,29 @@ class InnerNodeAsmGenerator extends 
AbstractAsmGenerator {
                                 .invokeVirtual(changePolymorphicTypeIdMtd)
                                 .ret()
                 );
+
+                for (String legacyName : legacyNames(schemaField)) {
+                    switchBuilder.addCase(
+                            legacyName,
+                            new BytecodeBlock()
+                                    .append(constructMtd.getThis())
+                                    .append(getTypeIdFromSrcVar)
+                                    .invokeVirtual(changePolymorphicTypeIdMtd)
+                                    .ret()
+                    );
+                }
             } else {
                 switchBuilder.addCase(
                         publicName,
                         treatSourceForConstruct(constructMtd, schemaField, 
fieldDef).ret()
                 );
+
+                for (String legacyName : legacyNames(schemaField)) {
+                    switchBuilder.addCase(
+                            legacyName,
+                            treatSourceForConstruct(constructMtd, schemaField, 
fieldDef).ret()
+                    );
+                }
             }
         }
 
@@ -1222,6 +1242,13 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                         publicName,
                         treatSourceForConstruct(constructMtd, schemaField, 
fieldDefs.get(fieldName)).ret()
                 );
+
+                for (String legacyName : legacyNames(schemaField)) {
+                    switchBuilderAllFields.addCase(
+                            legacyName,
+                            treatSourceForConstruct(constructMtd, schemaField, 
fieldDefs.get(fieldName)).ret()
+                    );
+                }
             }
 
             // if (includeInternal) switch_by_all_fields
@@ -1253,6 +1280,13 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                             publicName,
                             treatSourceForConstruct(constructMtd, 
polymorphicField, fieldDef).ret()
                     );
+
+                    for (String legacyName : legacyNames(polymorphicField)) {
+                        switchBuilderPolymorphicExtension.addCase(
+                                legacyName,
+                                treatSourceForConstruct(constructMtd, 
polymorphicField, fieldDef).ret()
+                        );
+                    }
                 }
 
                 switchBuilderTypeId.addCase(polymorphicInstanceId(e.getKey()), 
switchBuilderPolymorphicExtension.build());
@@ -1444,6 +1478,10 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                         || isPolymorphicId(schemaField) && 
!schemaField.getAnnotation(PolymorphicId.class).hasDefault()) {
                     // return;
                     switchBuilder.addCase(publicName, new 
BytecodeBlock().ret());
+
+                    for (String legacyName : legacyNames(schemaField)) {
+                        switchBuilder.addCase(legacyName, new 
BytecodeBlock().ret());
+                    }
                 } else {
                     FieldDefinition fieldDef = fieldDefs.get(fieldName);
 
@@ -1458,6 +1496,13 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                             publicName,
                             addNodeConstructDefault(constructDfltMtd, 
schemaField, fieldDef, specFieldDef).ret()
                     );
+
+                    for (String legacyName : legacyNames(schemaField)) {
+                        switchBuilder.addCase(
+                                legacyName,
+                                addNodeConstructDefault(constructDfltMtd, 
schemaField, fieldDef, specFieldDef).ret()
+                        );
+                    }
                 }
             }
         }
@@ -1479,6 +1524,10 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                         if (!hasDefault(polymorphicField)) {
                             // return;
                             
switchBuilderPolymorphicExtension.addCase(publicName, new 
BytecodeBlock().ret());
+
+                            for (String legacyName : 
legacyNames(polymorphicField)) {
+                                
switchBuilderPolymorphicExtension.addCase(legacyName, new 
BytecodeBlock().ret());
+                            }
                         } else {
                             FieldDefinition fieldDef = 
fieldDefs.get(fieldName(polymorphicField));
                             FieldDefinition specFieldDef = 
specFields.get(polymorphicField.getDeclaringClass());
@@ -1488,6 +1537,13 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator 
{
                                     publicName,
                                     addNodeConstructDefault(constructDfltMtd, 
polymorphicField, fieldDef, specFieldDef).ret()
                             );
+
+                            for (String legacyName : 
legacyNames(polymorphicField)) {
+                                switchBuilderPolymorphicExtension.addCase(
+                                        legacyName,
+                                        
addNodeConstructDefault(constructDfltMtd, polymorphicField, fieldDef, 
specFieldDef).ret()
+                                );
+                            }
                         }
                     }
                 }
@@ -2024,7 +2080,7 @@ class InnerNodeAsmGenerator extends AbstractAsmGenerator {
         return changePolymorphicTypeIdMtd;
     }
 
-    private String polymorphicTypeNotDefinedErrorMessage(Field 
polymorphicIdField) {
+    private static String polymorphicTypeNotDefinedErrorMessage(Field 
polymorphicIdField) {
         return "Polymorphic configuration type is not defined: "
                 + polymorphicIdField.getDeclaringClass().getName()
                 + ". See @" + PolymorphicConfig.class.getSimpleName() + " 
documentation.";
diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/storage/Data.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/storage/Data.java
index 2abe50e60ca..685e7e19b6e 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/storage/Data.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/storage/Data.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.configuration.storage;
 
 import java.io.Serializable;
+import java.util.Collections;
 import java.util.Map;
 
 /**
@@ -37,15 +38,11 @@ public class Data {
      * @param changeId Version.
      */
     public Data(Map<String, ? extends Serializable> values, long changeId) {
-        this.values = values;
+        this.values = Collections.unmodifiableMap(values);
         this.changeId = changeId;
     }
 
-    /**
-     * Get values.
-     *
-     * @return Values.
-     */
+    /** Get values. */
     public Map<String, ? extends Serializable> values() {
         return values;
     }
diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
index 9f174038dec..72e26208e9b 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/ConfigurationFlattener.java
@@ -32,7 +32,7 @@ import org.apache.ignite.internal.configuration.SuperRoot;
 import org.apache.ignite.internal.configuration.tree.InnerNode;
 import org.apache.ignite.internal.configuration.tree.NamedListNode;
 
-/** Utility class that has {@link 
ConfigurationFlattener#createFlattenedUpdatesMap(SuperRoot, SuperRoot)} method. 
*/
+/** Utility class that has {@link 
ConfigurationFlattener#createFlattenedUpdatesMap} method. */
 public class ConfigurationFlattener {
     /**
      * Convert a traversable tree to a map of qualified keys to values.
@@ -41,7 +41,11 @@ public class ConfigurationFlattener {
      * @param updates Tree with updates.
      * @return Map of changes.
      */
-    public static Map<String, Serializable> 
createFlattenedUpdatesMap(SuperRoot curRoot, SuperRoot updates) {
+    public static Map<String, Serializable> createFlattenedUpdatesMap(
+            SuperRoot curRoot,
+            SuperRoot updates,
+            Map<String, ? extends Serializable> storageData
+    ) {
         // Resulting map.
         Map<String, Serializable> resMap = new HashMap<>();
 
@@ -54,7 +58,7 @@ public class ConfigurationFlattener {
 
         // Explicit access to the children of super root guarantees that 
"oldInnerNodesStack" is never empty, and thus
         // we don't need null-checks when calling Deque#peek().
-        updates.traverseChildren(new FlattenerVisitor(oldInnerNodesStack, 
resMap), true);
+        updates.traverseChildren(new FlattenerVisitor(oldInnerNodesStack, 
resMap, storageData), true);
 
         assert oldInnerNodesStack.peek() == curRoot : oldInnerNodesStack;
 
@@ -91,6 +95,9 @@ public class ConfigurationFlattener {
         /** Map with the result. */
         private final Map<String, Serializable> resMap;
 
+        /** Map with values in the configuration storage. */
+        private final Map<String, ? extends Serializable> storageData;
+
         /** Flag indicates that "old" and "new" trees are literally the same 
at the moment. */
         private boolean singleTreeTraversal;
 
@@ -106,9 +113,14 @@ public class ConfigurationFlattener {
          * @param oldInnerNodesStack Old nodes stack for recursion.
          * @param resMap Map with the result.
          */
-        FlattenerVisitor(Deque<InnerNode> oldInnerNodesStack, Map<String, 
Serializable> resMap) {
+        FlattenerVisitor(
+                Deque<InnerNode> oldInnerNodesStack,
+                Map<String, Serializable> resMap,
+                Map<String, ? extends Serializable> storageData
+        ) {
             this.oldInnerNodesStack = oldInnerNodesStack;
             this.resMap = resMap;
+            this.storageData = storageData;
         }
 
         /** {@inheritDoc} */
@@ -126,6 +138,12 @@ public class ConfigurationFlattener {
                 resMap.put(currentKey(), deletion ? null : newVal);
             }
 
+            processLegacyPaths(legacyKey -> {
+                if (storageData.containsKey(legacyKey)) {
+                    resMap.put(legacyKey, null);
+                }
+            });
+
             return null;
         }
 
@@ -185,7 +203,7 @@ public class ConfigurationFlattener {
 
                 String namedListFullKey = currentKey();
 
-                withTracking(newNodeInternalId.toString(), false, false, () -> 
{
+                withTracking(field, newNodeInternalId.toString(), false, 
false, () -> {
                     InnerNode newNamedElement = isDeprecated ? null : 
newNode.getInnerNode(newNodeKey);
 
                     String oldNodeKey = 
oldNode.keyByInternalId(newNodeInternalId);
diff --git 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
index d6753c10d48..0a5405f3f35 100644
--- 
a/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
+++ 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/util/KeysTrackingConfigurationVisitor.java
@@ -17,17 +17,23 @@
 
 package org.apache.ignite.internal.configuration.util;
 
+import static 
org.apache.ignite.internal.configuration.asm.ConfigurationAsmGenerator.legacyNames;
+import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.appendKey;
+import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.join;
+import static org.apache.ignite.internal.util.ArrayUtils.STRING_EMPTY_ARRAY;
+
 import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 import org.apache.ignite.internal.configuration.tree.ConfigurationVisitor;
 import org.apache.ignite.internal.configuration.tree.InnerNode;
 import org.apache.ignite.internal.configuration.tree.NamedListNode;
 
-/** Visitor that accumulates keys while descending. */
+/** Visitor that accumulates keys and legacy names while descending. */
 public abstract class KeysTrackingConfigurationVisitor<T> implements 
ConfigurationVisitor<T> {
     /** Current key, aggregated by visitor. */
     private final StringBuilder currentKey = new StringBuilder();
@@ -35,10 +41,16 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     /** Current keys list, almost the same as {@link #currentKey}. */
     private final List<String> currentPath = new ArrayList<>();
 
+    /** For every part of the current path stores corresponding legacy names. 
*/
+    private final List<String[]> currentLegacyNames = new ArrayList<>();
+
+    /** Total amount of legacy names, corresponding to the current path. */
+    private int currentLegacyNamesCount = 0;
+
     /** {@inheritDoc} */
     @Override
     public final T visitLeafNode(Field field, String key, Serializable val) {
-        int prevPos = startVisit(key, false, true);
+        int prevPos = startVisit(field, key, false, true);
 
         try {
             return doVisitLeafNode(field, key, val);
@@ -50,7 +62,7 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     /** {@inheritDoc} */
     @Override
     public final T visitInnerNode(Field field, String key, InnerNode node) {
-        int prevPos = startVisit(key, false, false);
+        int prevPos = startVisit(field, key, false, false);
 
         try {
             return doVisitInnerNode(field, key, node);
@@ -62,7 +74,7 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     /** {@inheritDoc} */
     @Override
     public final T visitNamedListNode(Field field, String key, 
NamedListNode<?> node) {
-        int prevPos = startVisit(key, false, false);
+        int prevPos = startVisit(field, key, false, false);
 
         try {
             return doVisitNamedListNode(field, key, node);
@@ -96,7 +108,7 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     }
 
     /**
-     * To be used instead of {@link 
ConfigurationVisitor#visitNamedListNode(String, NamedListNode)}.
+     * To be used instead of {@link ConfigurationVisitor#visitNamedListNode}}.
      *
      * @param key  Name of the node retrieved from its holder object.
      * @param node Named list inner configuration node.
@@ -104,7 +116,7 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
      */
     protected T doVisitNamedListNode(Field field, String key, NamedListNode<?> 
node) {
         for (String namedListKey : node.namedListKeys()) {
-            int prevPos = startVisit(namedListKey, true, false);
+            int prevPos = startVisit(field, namedListKey, true, false);
 
             try {
                 doVisitInnerNode(field, namedListKey, 
node.getInnerNode(namedListKey));
@@ -119,14 +131,15 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     /**
      * Tracks passed key to reflect it in {@link #currentKey()} and {@link 
#currentPath()}.
      *
-     * @param key     Key itself.
-     * @param escape  Whether the key needs escaping or not.
-     * @param leaf    Add dot at the end of {@link #currentKey()} if {@code 
leaf} is {@code false}.
+     * @param field Node's field.
+     * @param key Key itself.
+     * @param escape Whether the key needs escaping or not.
+     * @param leaf Add dot at the end of {@link #currentKey()} if {@code leaf} 
is {@code false}.
      * @param closure Closure to execute when {@link #currentKey()} and {@link 
#currentPath()} have updated values.
      * @return Closure result.
      */
-    protected final T withTracking(String key, boolean escape, boolean leaf, 
Supplier<T> closure) {
-        int prevPos = startVisit(key, escape, leaf);
+    protected final T withTracking(Field field, String key, boolean escape, 
boolean leaf, Supplier<T> closure) {
+        int prevPos = startVisit(field, key, escape, leaf);
 
         try {
             return closure.get();
@@ -156,11 +169,12 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
     /**
      * Prepares values of {@link #currentKey} and {@link #currentPath} for 
further processing.
      *
-     * @param key    Key.
+     * @param field Field.
+     * @param key Key.
      * @param escape Whether we need to escape the key before appending it to 
{@link #currentKey}.
      * @return Previous length of {@link #currentKey} so it can be passed to 
{@link #endVisit(int)} later.
      */
-    private int startVisit(String key, boolean escape, boolean leaf) {
+    private int startVisit(Field field, String key, boolean escape, boolean 
leaf) {
         final int previousKeyLength = currentKey.length();
 
         currentKey.append(escape ? ConfigurationUtil.escape(key) : key);
@@ -171,6 +185,11 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
 
         currentPath.add(key);
 
+        String[] legacyNames = field == null ? STRING_EMPTY_ARRAY : 
legacyNames(field);
+
+        currentLegacyNames.add(legacyNames);
+        currentLegacyNamesCount += legacyNames.length;
+
         return previousKeyLength;
     }
 
@@ -183,5 +202,55 @@ public abstract class KeysTrackingConfigurationVisitor<T> 
implements Configurati
         currentKey.setLength(previousKeyLength);
 
         currentPath.remove(currentPath.size() - 1);
+
+        String[] legacyNames = 
currentLegacyNames.remove(currentLegacyNames.size() - 1);
+        currentLegacyNamesCount -= legacyNames.length;
+    }
+
+    /** Calls consumer for all variations of legacy paths, i.e. to remove them 
from the storage. */
+    protected void processLegacyPaths(Consumer<String> legacyKeyConsumer) {
+        // Current path doesn't contain any legacy names.
+        if (currentLegacyNamesCount == 0) {
+            return;
+        }
+
+        processLegacyPaths(new ArrayList<>(), legacyKeyConsumer);
+    }
+
+    private void processLegacyPaths(List<String> path, Consumer<String> 
legacyKeyConsumer) {
+        // We reached the leaf. If path joined with leaf name != current key, 
it is legacy and should be processed.
+        if (path.size() == currentPath().size() - 1) {
+            for (String leafName : currentLegacyNames.get(currentPath().size() 
- 1)) {
+                processLeaf(path, legacyKeyConsumer, leafName);
+            }
+
+            // Process current name for cases when legacy name was in the 
middle of the path.
+            processLeaf(path, legacyKeyConsumer, 
currentPath().get(currentPath().size() - 1));
+
+            return;
+        }
+
+        // For inner nodes we should all legacy names and current name.
+        for (String innerNodeName : currentLegacyNames.get(path.size())) {
+            path.add(innerNodeName);
+
+            processLegacyPaths(path, legacyKeyConsumer);
+
+            path.remove(path.size() - 1);
+        }
+
+        path.add(currentPath.get(path.size()));
+
+        processLegacyPaths(path, legacyKeyConsumer);
+
+        path.remove(path.size() - 1);
+    }
+
+    private void processLeaf(List<String> path, Consumer<String> 
legacyKeyConsumer, String leafName) {
+        String key = join(appendKey(path, leafName));
+
+        if (!key.equals(currentKey())) {
+            legacyKeyConsumer.accept(key);
+        }
     }
 }
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/RenamedConfigurationTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/RenamedConfigurationTest.java
new file mode 100644
index 00000000000..f19758b62b2
--- /dev/null
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/RenamedConfigurationTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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;
+
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
+import static 
org.apache.ignite.internal.configuration.hocon.HoconConverter.hoconSource;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+
+import com.typesafe.config.ConfigFactory;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.configuration.RootKey;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
+import org.apache.ignite.configuration.annotation.InjectedName;
+import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.PolymorphicConfig;
+import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
+import org.apache.ignite.configuration.annotation.PolymorphicId;
+import org.apache.ignite.configuration.annotation.PublicName;
+import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.internal.configuration.storage.Data;
+import 
org.apache.ignite.internal.configuration.storage.TestConfigurationStorage;
+import 
org.apache.ignite.internal.configuration.validation.TestConfigurationValidator;
+import org.apache.ignite.internal.manager.ComponentContext;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+class RenamedConfigurationTest extends BaseIgniteAbstractTest {
+
+    private static final ConfigurationTreeGenerator OLD_GENERATOR = new 
ConfigurationTreeGenerator(
+            Set.of(RenamedTestOldConfiguration.KEY),
+            Set.of(),
+            Set.of(RenamedPolymorphicInstanceOldConfigurationSchema.class)
+    );
+
+    private static final ConfigurationTreeGenerator NEW_GENERATOR = new 
ConfigurationTreeGenerator(
+            Set.of(RenamedTestNewConfiguration.KEY),
+            Set.of(),
+            Set.of(RenamedPolymorphicInstanceNewConfigurationSchema.class)
+    );
+    public static final String OLD_DEFAULT = "oldDefault";
+
+    private final TestConfigurationStorage storage = new 
TestConfigurationStorage(LOCAL);
+
+    private ConfigurationRegistry registry;
+
+    @AfterAll
+    public static void afterAll() {
+        OLD_GENERATOR.close();
+    }
+
+    @BeforeEach
+    void setUp() {
+        registry = startRegistry(RenamedTestOldConfiguration.KEY, 
OLD_GENERATOR);
+
+        String updatedConfig = "key.listOldName.listInstance.oldName = 
oldValue, "
+                + "key.oldPolymorphicName.polymorphicType.oldName = oldValue";
+
+        
assertThat(registry.change(hoconSource(ConfigFactory.parseString(updatedConfig).root())),
 willCompleteSuccessfully());
+
+        stopRegistry(registry);
+
+        registry = startRegistry(RenamedTestNewConfiguration.KEY, 
NEW_GENERATOR);
+    }
+
+    @AfterEach
+    void tearDown() {
+        stopRegistry(registry);
+    }
+
+    @Test
+    public void testLegacyNameIsRecognisedOnStartup() {
+        // Default value was set when registry was started with old 
configuration.
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().newName().value(),
+                equalTo(OLD_DEFAULT)
+        );
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().name().value(),
+                equalTo(OLD_DEFAULT)
+        );
+
+        assertThat(storage.readLatest("key.oldInnerName.oldName"), 
willBe(nullValue()));
+        assertThat(storage.readLatest("key.oldInnerName.name"), 
willBe(nullValue()));
+    }
+
+    @Test
+    public void testLegacyNameIsRecognisedOnUpdate() {
+        String updatedValue = "updatedValue";
+        String configWithFirstLegacyName = "key.oldInnerName.oldName = " + 
updatedValue;
+        updateConfig(registry, configWithFirstLegacyName);
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().newName().value(),
+                equalTo(updatedValue)
+        );
+        assertThat(storage.readLatest("key.oldInnerName.oldName"), 
willBe(nullValue()));
+
+        String secondUpdatedValue = "secondUpdatedValue";
+        String configWithSecondLegacyName = "key.secondOldInnerName.oldName = 
" + secondUpdatedValue;
+
+        updateConfig(registry, configWithSecondLegacyName);
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().newName().value(),
+                equalTo(secondUpdatedValue)
+        );
+        assertThat(storage.readLatest("key.secondOldInnerName.oldName"), 
willBe(nullValue()));
+    }
+
+    @Test
+    public void testNewValuePersistsAfterRestart() {
+        String updatedValue = "updatedValue";
+        String updatedConfig = "key.oldInnerName.oldName = " + updatedValue;
+        updateConfig(registry, updatedConfig);
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().newName().value(),
+                equalTo(updatedValue)
+        );
+
+        stopRegistry(registry);
+        registry = startRegistry(RenamedTestNewConfiguration.KEY, 
NEW_GENERATOR);
+
+        assertThat(storage.readLatest("key.oldInnerName.oldName"), 
willBe(nullValue()));
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newInnerName().newName().value(),
+                equalTo(updatedValue)
+        );
+    }
+
+    @Test
+    @Disabled("IGNITE-25458")
+    public void testNamedListLegacyNameIsRecognisedOnStartup() {
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newListName().get("listInstance").newName().value(),
+                equalTo("oldValue")
+        );
+
+        // TODO IGNITE-25458 Add check that old value is deleted
+    }
+
+    @Test
+    public void testNamedListLegacyNameIsRecognisedOnUpdate() {
+        String newValue = "newValue";
+
+        String updatedConfig = "key.listOldName.listInstance.oldName = " + 
newValue;
+        updateConfig(registry, updatedConfig);
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newListName().get("listInstance").newName().value(),
+                equalTo(newValue)
+        );
+
+        // TODO IGNITE-25458 Add check that old value is deleted
+    }
+
+    @Test
+    @Disabled("IGNITE-25458")
+    public void testPolymorphicLegacyNameIsRecognisedOnStartup() {
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newPolymorphicName().get("polymorphicType").newName().value(),
+                equalTo("oldValue")
+        );
+
+        // TODO IGNITE-25458 Add check that old value is deleted
+    }
+
+    @Test
+    public void testPolymorphicLegacyNameIsRecognisedOnUpdate() {
+        String newValue = "newValue";
+        String updatedConfig = "key.oldPolymorphicName.polymorphicType.oldName 
= " + newValue;
+        updateConfig(registry, updatedConfig);
+
+        assertThat(
+                
registry.getConfiguration(RenamedTestNewConfiguration.KEY).newPolymorphicName().get("polymorphicType").newName().value(),
+                equalTo(newValue)
+        );
+
+        // TODO IGNITE-25458 Add check that old value is deleted
+    }
+
+    private static void updateConfig(ConfigurationRegistry registry, String 
updatedConfig) {
+        CompletableFuture<Void> change = 
registry.change(hoconSource(ConfigFactory.parseString(updatedConfig).root()));
+        assertThat(change, willCompleteSuccessfully());
+    }
+
+    private ConfigurationRegistry startRegistry(RootKey<?, ?> rootKey, 
ConfigurationTreeGenerator generator) {
+        var registry = new ConfigurationRegistry(Set.of(rootKey), storage, 
generator, new TestConfigurationValidator());
+
+        assertThat(registry.startAsync(new ComponentContext()), 
willCompleteSuccessfully());
+        assertThat(registry.onDefaultsPersisted(), willCompleteSuccessfully());
+
+        return registry;
+    }
+
+    private void stopRegistry(ConfigurationRegistry registry) {
+        assertThat(registry.stopAsync(), willCompleteSuccessfully());
+        // Removes registry update listener.
+        storage.close();
+    }
+
+    private Data getData() {
+        CompletableFuture<Data> dataFuture = storage.readDataOnRecovery();
+
+        assertThat(dataFuture, willCompleteSuccessfully());
+
+        return dataFuture.join();
+    }
+
+    @ConfigurationRoot(rootName = "key", type = LOCAL)
+    public static class RenamedTestOldConfigurationSchema {
+        @ConfigValue
+        @PublicName("oldInnerName")
+        public RenamedLeafOldConfigurationSchema oldInnerName;
+
+        @NamedConfigValue
+        @PublicName("listOldName")
+        public RenamedLeafOldConfigurationSchema oldListName;
+
+        @NamedConfigValue
+        @PublicName("oldPolymorphicName")
+        public RenamedPolymorphicOldConfigurationSchema oldPolymorphicName;
+    }
+
+    @ConfigurationRoot(rootName = "key", type = LOCAL)
+    public static class RenamedTestNewConfigurationSchema {
+        @ConfigValue
+        @PublicName(legacyNames = {"oldInnerName", "secondOldInnerName"})
+        public RenamedLeafNewConfigurationSchema newInnerName;
+
+        @NamedConfigValue
+        @PublicName(legacyNames = {"listOldName"})
+        public RenamedLeafNewConfigurationSchema newListName;
+
+        @NamedConfigValue
+        @PublicName(legacyNames = "oldPolymorphicName")
+        public RenamedPolymorphicNewConfigurationSchema newPolymorphicName;
+    }
+
+    @Config
+    public static class RenamedLeafOldConfigurationSchema {
+        @Value(hasDefault = true)
+        @PublicName("oldName")
+        public String oldName = OLD_DEFAULT;
+
+        @Value(hasDefault = true)
+        public String name = OLD_DEFAULT;
+    }
+
+    @Config
+    public static class RenamedLeafNewConfigurationSchema {
+        @Value(hasDefault = true)
+        @PublicName(legacyNames = {"oldName"})
+        public String newName = "newDefault";
+
+        @Value(hasDefault = true)
+        public String name = "newDefault";
+    }
+
+    @PolymorphicConfig
+    public static class RenamedPolymorphicOldConfigurationSchema {
+        @PolymorphicId(hasDefault = true)
+        public String type = "polymorphicType";
+
+        @InjectedName
+        public String name;
+
+        @Value(hasDefault = true)
+        @PublicName("oldName")
+        public String oldName = OLD_DEFAULT;
+    }
+
+    @PolymorphicConfigInstance("polymorphicType")
+    public static class RenamedPolymorphicInstanceOldConfigurationSchema 
extends RenamedPolymorphicOldConfigurationSchema {
+    }
+
+    @PolymorphicConfig
+    public static class RenamedPolymorphicNewConfigurationSchema {
+        @PolymorphicId(hasDefault = true)
+        public String type = "polymorphicType";
+
+        @InjectedName
+        public String name;
+
+        @Value(hasDefault = true)
+        @PublicName(legacyNames = "oldName")
+        public String newName = "newDefault";
+    }
+
+    @PolymorphicConfigInstance("polymorphicType")
+    public static class RenamedPolymorphicInstanceNewConfigurationSchema 
extends RenamedPolymorphicNewConfigurationSchema{
+    }
+}
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/deprecation/DeprecatedConfigurationTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/deprecation/DeprecatedConfigurationTest.java
index b7146bfe3de..1952efe9c96 100644
--- 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/deprecation/DeprecatedConfigurationTest.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/deprecation/DeprecatedConfigurationTest.java
@@ -36,6 +36,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import org.apache.ignite.configuration.RootKey;
 import org.apache.ignite.configuration.SuperRootChange;
+import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
@@ -145,6 +146,31 @@ public class DeprecatedConfigurationTest extends 
BaseIgniteAbstractTest {
         public NamedElementConfigurationSchema list;
     }
 
+    /**
+     * Configuration root schema with deprecated field inside named list 
configuration.
+     */
+    @ConfigurationRoot(rootName = "root", type = ConfigurationType.LOCAL)
+    public static class DeprecatedFieldInNamedListConfigurationSchema {
+        @Value(hasDefault = true)
+        public int intValue = 10;
+
+        @ConfigValue
+        public ChildConfigurationSchema child;
+
+        @NamedConfigValue
+        public DeprecatedElementConfigurationSchema list;
+    }
+
+    /**
+     * Configuration schema with deprecated field inside.
+     */
+    @Config
+    public static class DeprecatedElementConfigurationSchema {
+        @Deprecated
+        @Value
+        public String strCfg;
+    }
+
     @BeforeEach
     void setUp() {
         storage = spy(new TestConfigurationStorage(TEST_CONFIGURATION_TYPE));
@@ -227,6 +253,20 @@ public class DeprecatedConfigurationTest extends 
BaseIgniteAbstractTest {
         });
     }
 
+    @Test
+    void testDeprecationReturnsDefaultValue() {
+        ConfigurationMigrator changeIntValue = change -> 
change.changeRoot(BeforeDeprecationConfiguration.KEY).changeIntValue(999);
+
+        withConfigurationChanger(BeforeDeprecationConfiguration.KEY, true, 
changeIntValue, changer -> {});
+
+        withConfigurationChanger(DeprecatedValueConfiguration.KEY, false, 
change -> {}, changer -> {
+            //noinspection CastToIncompatibleInterface
+            var root = (DeprecatedValueView) 
changer.superRoot().getRoot(DeprecatedValueConfiguration.KEY);
+            // Deprecated value should be read as a default.
+            assertEquals(10, root.intValue());
+        });
+    }
+
     @Test
     void testConfigurationMigrator() {
         withConfigurationChanger(BeforeDeprecationConfiguration.KEY, true, 
change -> {}, changer -> {});
@@ -396,6 +436,88 @@ public class DeprecatedConfigurationTest extends 
BaseIgniteAbstractTest {
         });
     }
 
+    @Test
+    void testDeprecatedValueInNamedListConfiguration() {
+        AtomicReference<UUID> internalIdReference = new AtomicReference<>();
+
+        withConfigurationChanger(BeforeDeprecationConfiguration.KEY, true, 
change -> {}, changer -> {
+            // Add named list element for the test.
+            changeConfiguration(changer, superRoot -> {
+                superRoot.changeRoot(BeforeDeprecationConfiguration.KEY)
+                        .changeList()
+                        .create("foo", namedElementChange -> {
+                            internalIdReference.set(((InnerNode) 
namedElementChange).internalId());
+                            namedElementChange.changeStrCfg("value");
+                        }
+                        );
+            });
+
+            UUID internalId = internalIdReference.get();
+            assertNotNull(internalId);
+
+            // Check that all expected properties have been added.
+            assertEquals(
+                    Map.of(
+                            "root.list." + internalId + ".<order>", 0,
+                            "root.list." + internalId + ".<name>", "foo",
+                            "root.list." + internalId + ".strCfg", "value",
+                            "root.list.<ids>.foo", internalId
+                    ),
+                    lastWriteCapture.getValue()
+            );
+
+            // Check storage state.
+            Data data = getData();
+
+            assertEquals(2, data.changeId());
+            assertEquals(
+                    Map.of(
+                            "root.intValue", 10,
+                            "root.child.my-int-cfg", 99,
+                            "root.list.<ids>.foo", internalId,
+                            "root.list." + internalId + ".<order>", 0,
+                            "root.list." + internalId + ".strCfg", "value",
+                            "root.list." + internalId + ".<name>", "foo"
+                    ),
+                    data.values()
+            );
+        });
+
+        withConfigurationChanger(DeprecatedFieldInNamedListConfiguration.KEY, 
false, change -> {}, changer -> {
+            //noinspection CastToIncompatibleInterface
+            var root = (DeprecatedFieldInNamedListView) 
changer.superRoot().getRoot(DeprecatedFieldInNamedListConfiguration.KEY);
+            assertNotNull(root.list());
+
+            UUID internalId = internalIdReference.get();
+
+            // Check that deprecated value is accessible.
+            assertEquals("value", root.list().get(internalId).strCfg());
+
+            // Check that deprecated value has been deleted while starting the 
changer.
+            assertEquals(
+                    mapWithNulls(
+                            "root.list." + internalId + ".strCfg", null
+                    ),
+                    lastWriteCapture.getValue()
+            );
+
+            // Check that deprecated value is not present in the storage 
anymore.
+            Data data = getData();
+
+            assertEquals(3, data.changeId());
+            assertEquals(
+                    Map.of(
+                            "root.intValue", 10,
+                            "root.child.my-int-cfg", 99,
+                            "root.list.<ids>.foo", internalId,
+                            "root.list." + internalId + ".<order>", 0,
+                            "root.list." + internalId + ".<name>", "foo"
+                    ),
+                    data.values()
+            );
+        });
+    }
+
     private void withConfigurationChanger(
             RootKey<?, ?> rootKey,
             boolean init,
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
index 0ec72f49b7d..18fb0324b4a 100644
--- 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/util/ConfigurationUtilTest.java
@@ -1192,7 +1192,7 @@ public class ConfigurationUtilTest {
         patch.accept(superRoot.getRoot(rootKey));
 
         // Create flat diff between two super trees.
-        return createFlattenedUpdatesMap(originalSuperRoot, superRoot);
+        return createFlattenedUpdatesMap(originalSuperRoot, superRoot, 
Map.of());
     }
 
     /**
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
index 3ea658c6997..7bd50a019a9 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
+++ 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/storage/TestConfigurationStorage.java
@@ -63,7 +63,8 @@ public class TestConfigurationStorage implements 
ConfigurationStorage {
 
     @Override
     public void close() {
-        // No-op.
+        // To reuse this instance with new configuration changer.
+        listeners.clear();
     }
 
     /**
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
index 358b25cbec6..120f63883b0 100644
--- 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
+++ 
b/modules/runner/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
@@ -175,7 +175,7 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
             Config hocon = readHoconFromFile();
             HoconConverter.hoconSource(hocon.root(), 
keyIgnorer).descend(copiedSuperRoot);
 
-            Map<String, Serializable> flattenedUpdatesMap = 
createFlattenedUpdatesMap(superRoot, copiedSuperRoot);
+            Map<String, Serializable> flattenedUpdatesMap = 
createFlattenedUpdatesMap(superRoot, copiedSuperRoot, Map.of());
             flattenedUpdatesMap.forEach((key, value) -> {
                 if (value != null) { // Filter defaults.
                     latest.put(key, value);
diff --git 
a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
 
b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
index 51bc16dcef6..962687dcf73 100644
--- 
a/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
+++ 
b/modules/runner/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.configuration.storage;
 
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.aMapWithSize;
 import static org.hamcrest.Matchers.allOf;
@@ -27,6 +28,7 @@ import static 
org.hamcrest.Matchers.equalToCompressingWhiteSpace;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.hasValue;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
 import com.typesafe.config.ConfigFactory;
@@ -45,6 +47,7 @@ import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.ConfigValue;
 import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.NamedConfigValue;
+import org.apache.ignite.configuration.annotation.PublicName;
 import org.apache.ignite.configuration.annotation.Value;
 import 
org.apache.ignite.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
@@ -580,8 +583,24 @@ public class LocalFileConfigurationStorageTest {
 
         Files.write(configFile, fileContent.getBytes(StandardCharsets.UTF_8));
 
-        // Storage ignores deleted property.
+        // Deleted properties are ignored and removed from the storage.
         assertDoesNotThrow(changer::start);
+        assertThat(storage.readLatest("top.deleted_property"), 
willBe(nullValue()));
+    }
+
+    @Test
+    void testReadDataOnStartupWithRenamedProperty() throws IOException {
+        // Given config in JSON format
+        String fileContent = "top.oldShortValName = 3";
+
+        Path configFile = getConfigFile();
+
+        Files.write(configFile, fileContent.getBytes(StandardCharsets.UTF_8));
+
+        // Storage handles renamed property.
+        assertDoesNotThrow(changer::start);
+
+        assertThat(storage.readLatest("top.shortVal"), willBe((short) 3));
     }
 
     private String configFileContent() throws IOException {
@@ -602,6 +621,7 @@ public class LocalFileConfigurationStorageTest {
         public InnerConfigurationSchema inner;
 
         @Value(hasDefault = true)
+        @PublicName(legacyNames = "oldShortValName")
         public short shortVal = 1;
 
         @Deprecated

Reply via email to