From e8f5c26196b202eee7ea60d2facb5e86353bdd8c Mon Sep 17 00:00:00 2001 From: JackieTien97 Date: Fri, 3 Jul 2026 16:49:21 +0800 Subject: [PATCH] Fix show configuration displaying raw value instead of effective value show configuration (table model) renders ConfigurationFileUtils.lastAppliedProperties, which is populated from the raw config-file values. Several hot-reloaded parameters have setters that rewrite the loaded value (e.g. `if (x > 0) setX(x)` skips non-positive values; loadFixedSizeLimitForQuery rewrites `<=0` to a computed default), so their displayed value diverged from the effective in-memory value. Add overlayEffectiveConfigurationValues() to overwrite those keys with the post-setter effective value, invoked after the setters in both loadProperties (startup) and loadHotModifiedProps (hot-reload), so local and remote show configuration stay correct. Affected keys: cte_buffer_size_in_bytes, max_rows_in_cte_buffer, max_sub_task_num_for_information_table_scan, sort_buffer_size_in_bytes, mods_cache_size_limit_per_fi_in_bytes. Added IT: IoTDBLoadConfigurationTableIT#showConfigurationDisplaysEffectiveValue. --- .../db/it/IoTDBLoadConfigurationTableIT.java | 94 +++++++++++++++++++ .../apache/iotdb/db/conf/IoTDBDescriptor.java | 33 +++++++ 2 files changed, 127 insertions(+) diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadConfigurationTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadConfigurationTableIT.java index 53e8bc9487e33..398a7f0e1e4f9 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadConfigurationTableIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBLoadConfigurationTableIT.java @@ -19,6 +19,8 @@ package org.apache.iotdb.relational.it.db.it; +import org.apache.iotdb.isession.ITableSession; +import org.apache.iotdb.isession.SessionDataSet; import org.apache.iotdb.it.env.EnvFactory; import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper; import org.apache.iotdb.it.framework.IoTDBTestRunner; @@ -40,6 +42,8 @@ import java.nio.file.StandardOpenOption; import java.sql.Connection; import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; @RunWith(IoTDBTestRunner.class) @Category({TableLocalStandaloneIT.class, TableClusterIT.class}) @@ -82,4 +86,94 @@ public void loadConfiguration() throws IOException { } } } + + // `show configuration` must display the in-memory effective value, not the raw config-file + // value. Several hot-reloaded parameters have setters that rewrite the loaded value (e.g. + // `if (x > 0) setX(x)` skips non-positive values; loadFixedSizeLimitForQuery rewrites `<=0` + // to a computed default). This test verifies those keys show the effective value after + // `load configuration`. + @Test + public void showConfigurationDisplaysEffectiveValue() throws Exception { + DataNodeWrapper dataNodeWrapper = EnvFactory.getEnv().getDataNodeWrapper(0); + String confPath = + dataNodeWrapper.getNodePath() + + File.separator + + "conf" + + File.separator + + "iotdb-system.properties"; + + // Case 1: non-positive guard params (`if (x > 0) setX(x)`) must display the effective + // default value, not the raw file value (0 / -1). + Map afterGuardReload = + appendLinesLoadAndShow( + confPath, + "cte_buffer_size_in_bytes=0", + "max_rows_in_cte_buffer=-1", + "max_sub_task_num_for_information_table_scan=0"); + Assert.assertEquals("131072", afterGuardReload.get("cte_buffer_size_in_bytes")); + Assert.assertEquals("1000", afterGuardReload.get("max_rows_in_cte_buffer")); + Assert.assertEquals("4", afterGuardReload.get("max_sub_task_num_for_information_table_scan")); + + // Case 2: a valid value is not rewritten, so display must equal the file value (regression). + Map afterValidReload = + appendLinesLoadAndShow(confPath, "cte_buffer_size_in_bytes=262144"); + Assert.assertEquals("262144", afterValidReload.get("cte_buffer_size_in_bytes")); + + // Case 3: params whose template default is 0 are always rewritten to a computed default, + // so they must never display 0 (this was always wrong, even without a bad file value). + Map defaultsReload = appendLinesLoadAndShow(confPath); + assertPositiveNonZero(defaultsReload, "sort_buffer_size_in_bytes"); + assertPositiveNonZero(defaultsReload, "mods_cache_size_limit_per_fi_in_bytes"); + } + + // Appends the given lines to the config file, runs `load configuration`, fetches the + // `show configuration` result, and restores the file to its original length. + private Map appendLinesLoadAndShow(String confPath, String... lines) + throws Exception { + long length = new File(confPath).length(); + try { + if (lines.length > 0) { + try (FileWriter fileWriter = new FileWriter(confPath, true)) { + fileWriter.write(System.lineSeparator()); + for (String line : lines) { + fileWriter.write(line); + fileWriter.write(System.lineSeparator()); + } + } + } + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + statement.execute("LOAD CONFIGURATION"); + } + return fetchShowConfiguration(); + } finally { + try (FileChannel fileChannel = + FileChannel.open(new File(confPath).toPath(), StandardOpenOption.WRITE)) { + fileChannel.truncate(length); + } + } + } + + private Map fetchShowConfiguration() throws Exception { + Map result = new HashMap<>(); + try (ITableSession tableSessionConnection = EnvFactory.getEnv().getTableSessionConnection()) { + SessionDataSet sessionDataSet = + tableSessionConnection.executeQueryStatement("show configuration"); + SessionDataSet.DataIterator iterator = sessionDataSet.iterator(); + while (iterator.next()) { + String name = iterator.getString(1); + String value = iterator.isNull(2) ? null : iterator.getString(2); + result.put(name, value); + } + } + return result; + } + + private static void assertPositiveNonZero(Map configMap, String key) { + String value = configMap.get(key); + Assert.assertNotNull("show configuration is missing key: " + key, value); + long parsed = Long.parseLong(value); + Assert.assertTrue( + key + " should display a positive effective value, but was " + value, parsed > 0); + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java index 147c3340bb381..7bd29087d50ba 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBDescriptor.java @@ -1232,6 +1232,11 @@ public void loadProperties(TrimProperties properties) throws BadNodeUrlException if (maxSubTaskNumForInformationTableScan > 0) { conf.setMaxSubTaskNumForInformationTableScan(maxSubTaskNumForInformationTableScan); } + + // Reflect the in-memory effective values (which the setters above may have rewritten) into + // lastAppliedProperties, so that `show configuration` displays effective values rather than + // the raw config-file values. + overlayEffectiveConfigurationValues(); } private void loadFixedSizeLimitForQuery( @@ -2333,6 +2338,9 @@ public synchronized void loadHotModifiedProps(TrimProperties properties) } ConfigurationFileUtils.updateAppliedProperties(properties, true); + // Overwrite the keys whose setters above may have rewritten, so `show configuration` + // displays the effective values rather than the raw file values. + overlayEffectiveConfigurationValues(); } catch (Exception e) { if (e instanceof InterruptedException) { Thread.currentThread().interrupt(); @@ -2342,6 +2350,31 @@ public synchronized void loadHotModifiedProps(TrimProperties properties) } } + // `show configuration` (table model) renders ConfigurationFileUtils.getAppliedProperties(). + // That map is populated from the raw config-file values, but several hot-reloaded parameters + // have setters that rewrite the loaded value before it takes effect, e.g.: + // - `if (x > 0) setX(x)` skips non-positive values (keeps the previous/default value); + // - loadFixedSizeLimitForQuery rewrites `<=0` to a computed default. + // For those keys the displayed value would otherwise diverge from the effective one. This + // method overwrites them with the post-setter effective value. It is invoked after the setters + // in both the startup (loadProperties) and hot-reload (loadHotModifiedProps) paths, so that + // local *and* remote `show configuration` (which reads each node's own map over RPC) stay + // correct. + private void overlayEffectiveConfigurationValues() { + ConfigurationFileUtils.updateAppliedProperties( + "cte_buffer_size_in_bytes", Long.toString(commonConfig.getCteBufferSize())); + ConfigurationFileUtils.updateAppliedProperties( + "max_rows_in_cte_buffer", Integer.toString(commonConfig.getMaxRowsInCteBuffer())); + ConfigurationFileUtils.updateAppliedProperties( + "max_sub_task_num_for_information_table_scan", + Integer.toString(conf.getMaxSubTaskNumForInformationTableScan())); + ConfigurationFileUtils.updateAppliedProperties( + "sort_buffer_size_in_bytes", + Long.toString(commonDescriptor.getConfig().getSortBufferSize())); + ConfigurationFileUtils.updateAppliedProperties( + "mods_cache_size_limit_per_fi_in_bytes", Long.toString(conf.getModsCacheSizeLimitPerFI())); + } + private void loadQuerySampleThroughput(TrimProperties properties) throws IOException { String querySamplingRateLimitNumber = properties.getProperty(