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"; + } }
