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

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

commit 63f24ab7269d7e4b3618f45cd4f7b74cebb77bcb
Author: Mikhail Pochatkin <[email protected]>
AuthorDate: Tue Oct 14 16:42:22 2025 +0300

    IGNITE-26690 Fix named list configuration updating
---
 .../storage/LocalFileConfigurationStorage.java     | 80 ++++++++--------------
 .../storage/LocalFileConfigurationStorageTest.java | 52 +++++++++++++-
 2 files changed, 81 insertions(+), 51 deletions(-)

diff --git 
a/modules/configuration-storage/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
 
b/modules/configuration-storage/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
index 081d19e3151..09ac65e5da2 100644
--- 
a/modules/configuration-storage/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
+++ 
b/modules/configuration-storage/src/main/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorage.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.configuration.storage;
 
 import static java.util.Collections.emptyNavigableMap;
+import static java.util.concurrent.CompletableFuture.completedFuture;
 import static java.util.stream.Collectors.toMap;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationFlattener.createFlattenedUpdatesMap;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.fillFromPrefixMap;
@@ -29,9 +30,7 @@ import com.typesafe.config.Config;
 import com.typesafe.config.ConfigException.Parse;
 import com.typesafe.config.ConfigFactory;
 import com.typesafe.config.ConfigObject;
-import com.typesafe.config.ConfigParseOptions;
 import com.typesafe.config.ConfigRenderOptions;
-import com.typesafe.config.ConfigSyntax;
 import com.typesafe.config.ConfigValue;
 import com.typesafe.config.impl.ConfigImpl;
 import java.io.IOException;
@@ -53,19 +52,19 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
-import org.apache.ignite.configuration.ConfigurationDynamicDefaultsPatcher;
 import org.apache.ignite.configuration.ConfigurationModule;
 import org.apache.ignite.configuration.KeyIgnorer;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
 import 
org.apache.ignite.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.configuration.validation.ValidationIssue;
-import 
org.apache.ignite.internal.configuration.ConfigurationDynamicDefaultsPatcherImpl;
 import org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
 import org.apache.ignite.internal.configuration.NodeConfigCreateException;
 import org.apache.ignite.internal.configuration.NodeConfigParseException;
 import org.apache.ignite.internal.configuration.NodeConfigWriteException;
 import org.apache.ignite.internal.configuration.SuperRoot;
+import org.apache.ignite.internal.configuration.SuperRootChangeImpl;
 import org.apache.ignite.internal.configuration.hocon.HoconConverter;
+import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
 import org.apache.ignite.internal.configuration.tree.ConverterToMapVisitor;
 import 
org.apache.ignite.internal.configuration.validation.ConfigurationDuplicatesValidator;
 import org.apache.ignite.internal.future.InFlightFutures;
@@ -145,61 +144,52 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
         checkAndRestoreConfigFile();
     }
 
-    /**
-     * Patch the local configs with defaults from provided {@link 
ConfigurationModule}.
-     *
-     * @param hocon Config string in Hocon format.
-     * @param module Configuration module, which provides configuration 
patches.
-     * @return Patched config string in Hocon format.
-     */
-    private String patch(String hocon, ConfigurationModule module) {
-        if (module == null) {
-            return hocon;
-        }
-
-        ConfigurationDynamicDefaultsPatcher localCfgDynamicDefaultsPatcher = 
new ConfigurationDynamicDefaultsPatcherImpl(
-                module,
-                generator
-        );
-
-        return localCfgDynamicDefaultsPatcher.patchWithDynamicDefaults(hocon);
-    }
-
     @Override
     public CompletableFuture<Data> readDataOnRecovery() {
         lock.writeLock().lock();
         try {
             String hocon = readHoconFromFile();
-            String hoconWithDynamicDefaults = patch(hocon, module);
+            SuperRoot superRoot = convertToSuperRoot(hocon);
+
+            Map<String, Serializable> transformedHocon = 
transformToMap(superRoot);
 
-            transformToMap(hocon).forEach((key, value) -> {
+            transformedHocon.forEach((key, value) -> {
                 if (value != null) { // Filter defaults.
                     latest.put(key, value);
                 }
             });
 
-            return CompletableFuture.completedFuture(new 
Data(transformToMap(hoconWithDynamicDefaults), lastRevision));
+            module.patchConfigurationWithDynamicDefaults(new 
SuperRootChangeImpl(superRoot));
+            Map<String, Serializable> transformedHoconWithDefaults = 
transformToMap(superRoot);
+
+            return completedFuture(new Data(transformedHoconWithDefaults, 
lastRevision));
         } finally {
             lock.writeLock().unlock();
         }
     }
 
-    private Map<String, Serializable> transformToMap(String hocon) {
-        SuperRoot superRoot = generator.createSuperRoot();
-        SuperRoot copiedSuperRoot = superRoot.copy();
+    private SuperRoot convertToSuperRoot(String hocon) {
+        try {
+            Config config = ConfigFactory.parseString(hocon);
+            KeyIgnorer keyIgnorer = module == null ? s -> false : 
KeyIgnorer.fromDeletedPrefixes(module.deletedPrefixes());
+
+            ConfigurationSource hoconSource = 
HoconConverter.hoconSource(config.root(), keyIgnorer);
 
-        KeyIgnorer keyIgnorer = module == null ? s -> false : 
KeyIgnorer.fromDeletedPrefixes(module.deletedPrefixes());
+            SuperRoot superRoot = generator.createSuperRoot();
+            hoconSource.descend(superRoot);
 
-        HoconConverter.hoconSource(parseConfig(hocon).root(), 
keyIgnorer).descend(copiedSuperRoot);
+            return superRoot;
+        } catch (Exception e) {
+            throw new ConfigurationValidationException("Failed to parse HOCON: 
" + e.getMessage());
+        }
+    }
 
-        return createFlattenedUpdatesMap(superRoot, copiedSuperRoot, 
emptyNavigableMap())
+    private Map<String, Serializable> transformToMap(SuperRoot superRoot) {
+        return createFlattenedUpdatesMap(generator.createSuperRoot(), 
superRoot, emptyNavigableMap())
                 .entrySet()
                 .stream()
                 .filter(e -> e.getValue() != null)
-                .collect(toMap(
-                        Entry::getKey,
-                        Entry::getValue)
-                );
+                .collect(toMap(Entry::getKey, Entry::getValue));
     }
 
     private String readHoconFromFile() {
@@ -220,21 +210,11 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
         }
     }
 
-    private Config parseConfig(String confString) {
-        try {
-            ConfigParseOptions parseOptions = 
ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF).setAllowMissing(false);
-
-            return ConfigFactory.parseString(confString, parseOptions);
-        } catch (Parse e) {
-            throw new NodeConfigParseException("Failed to parse config content 
from file " + configPath, e);
-        }
-    }
-
     @Override
     public CompletableFuture<Map<String, ? extends Serializable>> 
readAllLatest(String prefix) {
         lock.readLock().lock();
         try {
-            return CompletableFuture.completedFuture(
+            return completedFuture(
                     latest.entrySet()
                             .stream()
                             .filter(entry -> entry.getKey().startsWith(prefix))
@@ -249,7 +229,7 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
     public CompletableFuture<Serializable> readLatest(String key) {
         lock.readLock().lock();
         try {
-            return CompletableFuture.completedFuture(latest.get(key));
+            return completedFuture(latest.get(key));
         } finally {
             lock.readLock().unlock();
         }
@@ -303,7 +283,7 @@ public class LocalFileConfigurationStorage implements 
ConfigurationStorage {
 
     @Override
     public CompletableFuture<Long> lastRevision() {
-        return CompletableFuture.completedFuture(lastRevision);
+        return completedFuture(lastRevision);
     }
 
     @Override
diff --git 
a/modules/configuration-storage/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
 
b/modules/configuration-storage/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
index c78382a5033..96b759c1678 100644
--- 
a/modules/configuration-storage/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
+++ 
b/modules/configuration-storage/src/test/java/org/apache/ignite/internal/configuration/storage/LocalFileConfigurationStorageTest.java
@@ -50,12 +50,18 @@ import org.apache.ignite.configuration.KeyIgnorer;
 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.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
 import org.apache.ignite.internal.configuration.TestConfigurationChanger;
+import org.apache.ignite.internal.configuration.hocon.HoconConverter;
+import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
 import 
org.apache.ignite.internal.configuration.validation.ConfigurationValidatorImpl;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
@@ -85,7 +91,11 @@ public class LocalFileConfigurationStorageTest {
 
     @BeforeAll
     public static void beforeAll() {
-        treeGenerator = new ConfigurationTreeGenerator(TopConfiguration.KEY, 
TopEmptyConfiguration.KEY);
+        treeGenerator = new ConfigurationTreeGenerator(
+                List.of(TopConfiguration.KEY, TopEmptyConfiguration.KEY),
+                List.of(),
+                List.of(FirstNamedListConfigurationSchema.class)
+        );
     }
 
     @AfterAll
@@ -545,6 +555,28 @@ public class LocalFileConfigurationStorageTest {
         assertThat(storage.write(Map.of(), storage.localRevision().get() + 1), 
willCompleteSuccessfully());
     }
 
+    @Test
+    void updateAfterRestart() throws Exception {
+        assertThat(configFileContent(), emptyString());
+
+        // Initialize value
+        com.typesafe.config.Config config = 
ConfigFactory.parseString("top.polyNamedList = 
[{name=name1,strVal=foo,type=first}]");
+        ConfigurationSource source = HoconConverter.hoconSource(config.root());
+
+        changer.start();
+        changer.change(source).join();
+
+        // Force restart of the storage
+        after();
+        before();
+
+        config = 
ConfigFactory.parseString("top.polyNamedList.name1.strVal=strVal1");
+        source = HoconConverter.hoconSource(config.root());
+
+        changer.start();
+        changer.change(source).join();
+    }
+
     private String configFileContent() throws IOException {
         return Files.readString(getConfigFile());
     }
@@ -569,6 +601,9 @@ public class LocalFileConfigurationStorageTest {
         @Deprecated
         @Value(hasDefault = true)
         public int deprecated = 0;
+
+        @NamedConfigValue
+        public PolyNamedListConfigurationSchema polyNamedList;
     }
 
 
@@ -601,4 +636,19 @@ public class LocalFileConfigurationStorageTest {
         @Value(hasDefault = true)
         public int intVal = 1;
     }
+
+    @PolymorphicConfig
+    public static class PolyNamedListConfigurationSchema {
+        @PolymorphicId
+        public String type;
+
+        @InjectedName
+        public String name;
+    }
+
+    @PolymorphicConfigInstance("first")
+    public static class FirstNamedListConfigurationSchema extends 
PolyNamedListConfigurationSchema {
+        @Value(hasDefault = true)
+        public String strVal = "foo";
+    }
 }

Reply via email to