From 08a89f8882e7adbd1b91184c639755c0d7d3495b Mon Sep 17 00:00:00 2001 From: Yongzao <532741407@qq.com> Date: Fri, 12 Jun 2026 18:26:33 +0800 Subject: [PATCH 1/2] Fix enable_topology_probing hot-reload by uncommenting it in the config template enable_topology_probing declares effectiveMode: hot_reload and the ConfigNode implements the hot-reload start/stop logic, but 'set configuration "enable_topology_probing"="true"' was rejected with 301 "immutable or undefined". Root cause: the item was left commented out (# enable_topology_probing=false) in iotdb-system.properties.template. ConfigurationFileUtils.getConfigurationItemsFromTemplate only parses uncommented key=value lines into the default-value map, so filterInvalidConfigItems treated the key as undefined and dropped it before it could reach the hot-reload path. Uncommenting it makes it a bare default like every other working hot_reload item (e.g. slow_query_threshold). Add regression tests: - ConfigurationFileUtilsTest.checkHotReloadItemsAreUncommentedInTemplate scans the template and fails if any hot_reload item is left commented out (while tolerating keys that also have an uncommented variant, e.g. the Windows/Unix path pairs). - IoTDBSetConfigurationIT.testSetHotReloadTopologyProbing verifies 'set configuration' accepts the item and persists it to the ConfigNode config file. --- .../iotdb/db/it/IoTDBSetConfigurationIT.java | 21 ++++++ .../db/utils/ConfigurationFileUtilsTest.java | 71 +++++++++++++++++++ .../conf/iotdb-system.properties.template | 2 +- 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java index 96ee0bc30c1c3..b430ddc754bf0 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java @@ -88,6 +88,27 @@ public void testSetConfiguration() { "enable_cross_space_compaction=false"))); } + /** + * Regression test for V2-995: {@code enable_topology_probing} is declared {@code effectiveMode: + * hot_reload}, so {@code set configuration} must accept it (instead of rejecting it as "immutable + * or undefined") and persist it to the config file. This used to fail because the item was left + * commented out in the template, hiding it from the default-value map. + */ + @Test + public void testSetHotReloadTopologyProbing() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("set configuration \"enable_topology_probing\"=\"true\""); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } + Assert.assertTrue( + EnvFactory.getEnv().getConfigNodeWrapperList().stream() + .allMatch( + nodeWrapper -> + checkConfigFileContains(nodeWrapper, "enable_topology_probing=true"))); + } + @Test public void testSetClusterName() throws Exception { // set cluster name on cn and dn diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java index af0fc1ae83e37..cf06df887f20e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.utils; +import org.apache.iotdb.commons.conf.CommonConfig; import org.apache.iotdb.commons.conf.ConfigurationFileUtils; import org.apache.iotdb.db.utils.constant.TestConstant; @@ -26,13 +27,18 @@ import org.junit.Assert; import org.junit.Test; +import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.nio.file.Files; +import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Properties; +import java.util.Set; public class ConfigurationFileUtilsTest { @@ -89,6 +95,71 @@ public void checkIoTDBSystemTemplateFileFormat() throws IOException { } } + /** + * Regression guard for V2-995: a configuration item declared {@code effectiveMode: hot_reload} + * must have its {@code key=value} line left uncommented in the template. {@code + * getConfigurationItemsFromTemplate} only parses uncommented lines, so a commented hot_reload + * item never enters {@code configuration2DefaultValue}; {@code filterInvalidConfigItems} then + * treats it as undefined and {@code set configuration} rejects it with "immutable or undefined", + * silently disabling the advertised hot-reload entry point. + */ + @Test + public void checkHotReloadItemsAreUncommentedInTemplate() throws IOException { + // Keys whose value line appears uncommented at least once. Some keys (e.g. dn_data_dirs) list a + // commented Windows-path variant next to an uncommented Unix-path variant; only the uncommented + // one is parsed, so such keys are fine and must not be flagged. + Set uncommentedKeys = new HashSet<>(); + // Keys with a commented value line whose enclosing block declares effectiveMode: hot_reload. + Set commentedHotReloadKeys = new HashSet<>(); + try (InputStream inputStream = + ConfigurationFileUtilsTest.class + .getClassLoader() + .getResourceAsStream(CommonConfig.SYSTEM_CONFIG_TEMPLATE_NAME); + BufferedReader reader = + new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream)))) { + String line; + ConfigurationFileUtils.EffectiveModeType currentMode = null; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) { + // Blank line separates configuration blocks; reset the accumulated effectiveMode. + currentMode = null; + continue; + } + if (line.startsWith("# effectiveMode:")) { + currentMode = + ConfigurationFileUtils.EffectiveModeType.getEffectiveMode( + line.substring("# effectiveMode:".length()).trim()); + continue; + } + // Detect the (possibly commented) "key=value" line that closes a block. + String stripped = line.startsWith("#") ? line.substring(1).trim() : line; + int equalsIndex = stripped.indexOf('='); + boolean isValueLine = + equalsIndex > 0 && stripped.substring(0, equalsIndex).trim().matches("[a-zA-Z0-9_.]+"); + if (!isValueLine) { + continue; + } + String key = stripped.substring(0, equalsIndex).trim(); + if (line.startsWith("#")) { + if (currentMode == ConfigurationFileUtils.EffectiveModeType.HOT_RELOAD) { + commentedHotReloadKeys.add(key); + } + } else { + uncommentedKeys.add(key); + } + // A value line ends the current block's effectiveMode scope. + currentMode = null; + } + } + commentedHotReloadKeys.removeAll(uncommentedKeys); + Assert.assertTrue( + "hot_reload configuration items must be uncommented in the template so that " + + "'set configuration' can hot-reload them; commented hot_reload items found: " + + commentedHotReloadKeys, + commentedHotReloadKeys.isEmpty()); + } + private void generateFile(File file, String content) throws IOException { Files.write(file.toPath(), content.getBytes()); } diff --git a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template index 8e4808cec2faa..3445d82bfe98a 100644 --- a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template +++ b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template @@ -747,7 +747,7 @@ failure_detector_phi_acceptable_pause_in_ms=10000 # Whether to enable topology probing between DataNodes # effectiveMode: hot_reload # Datatype: Boolean -# enable_topology_probing=false +enable_topology_probing=false # Base interval in ms for topology probing between DataNodes # effectiveMode: restart From ef292bb3103d47d6304de203b0ed3cc6d3136a85 Mon Sep 17 00:00:00 2001 From: Yongzao <532741407@qq.com> Date: Fri, 12 Jun 2026 18:49:30 +0800 Subject: [PATCH 2/2] Uncomment all commented set-configuration-able items in the config template Following the enable_topology_probing fix, a generalized regression test (checkSettableItemsAreUncommentedInTemplate) revealed that several other items whose effectiveMode permits 'set configuration' (everything except FIRST_START) were also left commented out in the template, hitting the same root cause: getConfigurationItemsFromTemplate only parses uncommented lines, so filterInvalidConfigItems treats them as undefined and rejects the SQL with '301 immutable or undefined'. Uncomment these so 'set configuration' can reach them (all restart mode, all with real descriptor bindings and sensible defaults): - topology_probing_base_interval_in_ms=5000 - topology_probing_timeout_ratio=0.5 - cn_max_idle_client_count_for_each_node_in_client_manager=1000 - dn_max_idle_client_count_for_each_node_in_client_manager=1000 - region_group_allocate_policy=GCR Broaden the unit-test guard from hot_reload-only to every settable mode (hot_reload / restart / first_start_or_set_configuration), so any future settable item left commented is caught. Extend the integration test to also set the restart-mode topology_probing_* items end-to-end. --- .../iotdb/db/it/IoTDBSetConfigurationIT.java | 20 ++++++--- .../db/utils/ConfigurationFileUtilsTest.java | 43 ++++++++++++------- .../conf/iotdb-system.properties.template | 10 ++--- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java index b430ddc754bf0..dac067769ac90 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSetConfigurationIT.java @@ -89,16 +89,20 @@ public void testSetConfiguration() { } /** - * Regression test for V2-995: {@code enable_topology_probing} is declared {@code effectiveMode: - * hot_reload}, so {@code set configuration} must accept it (instead of rejecting it as "immutable - * or undefined") and persist it to the config file. This used to fail because the item was left - * commented out in the template, hiding it from the default-value map. + * Regression test for V2-995: the topology-probing config items were left commented out in the + * template, so {@code getConfigurationItemsFromTemplate} never recorded them as known defaults + * and {@code set configuration} rejected them as "immutable or undefined". After uncommenting + * them, {@code set configuration} must accept and persist them: {@code enable_topology_probing} + * (hot_reload) and {@code topology_probing_base_interval_in_ms} / {@code + * topology_probing_timeout_ratio} (restart). */ @Test - public void testSetHotReloadTopologyProbing() { + public void testSetTopologyProbingConfiguration() { try (Connection connection = EnvFactory.getEnv().getConnection(); Statement statement = connection.createStatement()) { statement.execute("set configuration \"enable_topology_probing\"=\"true\""); + statement.execute("set configuration \"topology_probing_base_interval_in_ms\"=\"3000\""); + statement.execute("set configuration \"topology_probing_timeout_ratio\"=\"0.4\""); } catch (Exception e) { Assert.fail(e.getMessage()); } @@ -106,7 +110,11 @@ public void testSetHotReloadTopologyProbing() { EnvFactory.getEnv().getConfigNodeWrapperList().stream() .allMatch( nodeWrapper -> - checkConfigFileContains(nodeWrapper, "enable_topology_probing=true"))); + checkConfigFileContains( + nodeWrapper, + "enable_topology_probing=true", + "topology_probing_base_interval_in_ms=3000", + "topology_probing_timeout_ratio=0.4"))); } @Test diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java index cf06df887f20e..ae419c79ecdc4 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/ConfigurationFileUtilsTest.java @@ -96,21 +96,23 @@ public void checkIoTDBSystemTemplateFileFormat() throws IOException { } /** - * Regression guard for V2-995: a configuration item declared {@code effectiveMode: hot_reload} - * must have its {@code key=value} line left uncommented in the template. {@code - * getConfigurationItemsFromTemplate} only parses uncommented lines, so a commented hot_reload - * item never enters {@code configuration2DefaultValue}; {@code filterInvalidConfigItems} then - * treats it as undefined and {@code set configuration} rejects it with "immutable or undefined", - * silently disabling the advertised hot-reload entry point. + * Regression guard for V2-995: a configuration item whose {@code effectiveMode} allows {@code set + * configuration} (i.e. anything except {@code FIRST_START}) must have its {@code key=value} line + * left uncommented in the template. {@code getConfigurationItemsFromTemplate} only parses + * uncommented lines, so a commented item never enters {@code configuration2DefaultValue}; {@code + * filterInvalidConfigItems} then treats it as undefined and {@code set configuration} rejects it + * with "immutable or undefined" — silently disabling the advertised dynamic-config entry point. + * This was the root cause for {@code enable_topology_probing} (hot_reload) and the two {@code + * topology_probing_*} items (restart). */ @Test - public void checkHotReloadItemsAreUncommentedInTemplate() throws IOException { + public void checkSettableItemsAreUncommentedInTemplate() throws IOException { // Keys whose value line appears uncommented at least once. Some keys (e.g. dn_data_dirs) list a // commented Windows-path variant next to an uncommented Unix-path variant; only the uncommented // one is parsed, so such keys are fine and must not be flagged. Set uncommentedKeys = new HashSet<>(); - // Keys with a commented value line whose enclosing block declares effectiveMode: hot_reload. - Set commentedHotReloadKeys = new HashSet<>(); + // Keys with a commented value line whose enclosing block declares a settable effectiveMode. + Set commentedSettableKeys = new HashSet<>(); try (InputStream inputStream = ConfigurationFileUtilsTest.class .getClassLoader() @@ -142,8 +144,10 @@ public void checkHotReloadItemsAreUncommentedInTemplate() throws IOException { } String key = stripped.substring(0, equalsIndex).trim(); if (line.startsWith("#")) { - if (currentMode == ConfigurationFileUtils.EffectiveModeType.HOT_RELOAD) { - commentedHotReloadKeys.add(key); + // FIRST_START items are intentionally rejected by 'set configuration'; UNKNOWN items have + // no declared mode. Only flag items that 'set configuration' is supposed to accept. + if (isSettableByConfiguration(currentMode)) { + commentedSettableKeys.add(key); } } else { uncommentedKeys.add(key); @@ -152,12 +156,19 @@ public void checkHotReloadItemsAreUncommentedInTemplate() throws IOException { currentMode = null; } } - commentedHotReloadKeys.removeAll(uncommentedKeys); + commentedSettableKeys.removeAll(uncommentedKeys); Assert.assertTrue( - "hot_reload configuration items must be uncommented in the template so that " - + "'set configuration' can hot-reload them; commented hot_reload items found: " - + commentedHotReloadKeys, - commentedHotReloadKeys.isEmpty()); + "configuration items settable via 'set configuration' must be uncommented in the template " + + "so the command can reach them instead of rejecting them as undefined; " + + "commented settable items found: " + + commentedSettableKeys, + commentedSettableKeys.isEmpty()); + } + + private static boolean isSettableByConfiguration(ConfigurationFileUtils.EffectiveModeType mode) { + return mode == ConfigurationFileUtils.EffectiveModeType.HOT_RELOAD + || mode == ConfigurationFileUtils.EffectiveModeType.RESTART + || mode == ConfigurationFileUtils.EffectiveModeType.FIRST_START_OR_SET_CONFIGURATION; } private void generateFile(File file, String content) throws IOException { diff --git a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template index 3445d82bfe98a..1e855a9704c88 100644 --- a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template +++ b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template @@ -512,7 +512,7 @@ cn_max_client_count_for_each_node_in_client_manager=1000 # 0 means no idle clients will be retained, connections are destroyed immediately upon return. # effectiveMode: restart # Datatype: int -# cn_max_idle_client_count_for_each_node_in_client_manager=1000 +cn_max_idle_client_count_for_each_node_in_client_manager=1000 # The maximum session idle time. unit: ms # Idle sessions are the ones that performs neither query or non-query operations for a period of time @@ -571,7 +571,7 @@ dn_max_client_count_for_each_node_in_client_manager=1000 # 0 means no idle clients will be retained, connections are destroyed immediately upon return. # effectiveMode: restart # Datatype: int -# dn_max_idle_client_count_for_each_node_in_client_manager=1000 +dn_max_idle_client_count_for_each_node_in_client_manager=1000 #################### ### REST Service Configuration @@ -686,7 +686,7 @@ data_region_per_data_node=0 # 3. PGP (Partite-Graph Placement; based on the PGP paper, with database-aware balance) # effectiveMode: restart # Datatype: String -# region_group_allocate_policy=GCR +region_group_allocate_policy=GCR # Whether to enable auto leader balance for Ratis consensus protocol. # The ConfigNode-leader will balance the leader of Ratis-RegionGroups by leader_distribution_policy if set true. @@ -752,12 +752,12 @@ enable_topology_probing=false # Base interval in ms for topology probing between DataNodes # effectiveMode: restart # Datatype: long -# topology_probing_base_interval_in_ms=5000 +topology_probing_base_interval_in_ms=5000 # Ratio of probing timeout to probing interval (must be less than 1.0) # effectiveMode: restart # Datatype: double -# topology_probing_timeout_ratio=0.5 +topology_probing_timeout_ratio=0.5 # Disk remaining threshold at which DataNode is set to ReadOnly status # effectiveMode: restart