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

wuchong pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new e5e6bb49c [server] Add configurable disk write-limit recover ratio 
(#3612)
e5e6bb49c is described below

commit e5e6bb49cee5d2c0a8e167f6588c206a227e0f87
Author: yunhong <[email protected]>
AuthorDate: Mon Jul 13 20:37:42 2026 +0800

    [server] Add configurable disk write-limit recover ratio (#3612)
---
 .../fluss/client/admin/FlussAdminITCase.java       | 152 +++++++-------
 .../org/apache/fluss/config/ConfigOptions.java     |  19 +-
 .../apache/fluss/server/DynamicServerConfig.java   |   2 +
 .../server/coordinator/CoordinatorServer.java      |   4 +-
 .../fluss/server/storage/DiskUsageMonitor.java     |  74 ++++---
 .../storage/DiskWriteLimitConfigValidator.java     |  65 ++++++
 .../storage/DiskWriteLimitRatioValidator.java      |  60 ------
 .../fluss/server/storage/LocalDiskManager.java     |  94 ++++++---
 .../apache/fluss/server/tablet/TabletServer.java   |   2 +-
 .../fluss/server/DynamicConfigChangeTest.java      | 223 ++++++++++++---------
 .../fluss/server/replica/ReplicaManagerTest.java   |   2 +-
 .../fluss/server/storage/DiskUsageMonitorTest.java | 116 +++++++----
 .../fluss/server/storage/LocalDiskManagerTest.java |  34 ++++
 website/docs/maintenance/configuration.md          |  33 +--
 14 files changed, 541 insertions(+), 339 deletions(-)

diff --git 
a/fluss-client/src/test/java/org/apache/fluss/client/admin/FlussAdminITCase.java
 
b/fluss-client/src/test/java/org/apache/fluss/client/admin/FlussAdminITCase.java
index cda9fa334..ea60dc3a0 100644
--- 
a/fluss-client/src/test/java/org/apache/fluss/client/admin/FlussAdminITCase.java
+++ 
b/fluss-client/src/test/java/org/apache/fluss/client/admin/FlussAdminITCase.java
@@ -1652,76 +1652,92 @@ class FlussAdminITCase extends ClientToServerITCaseBase 
{
     }
 
     @Test
-    void testDynamicDiskWriteLimitRatio() throws Exception {
-        // Valid value should succeed
-        admin.alterClusterConfigs(
-                        Collections.singletonList(
-                                new AlterConfig(
-                                        
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
-                                        "0.15",
-                                        AlterConfigOpType.SET)))
-                .get();
-        assertConfigEntry(
-                ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
-                "0.15",
-                ConfigEntry.ConfigSource.DYNAMIC_SERVER_CONFIG);
-
-        // Invalid value: 0.0 (must be > 0.1)
-        assertThatThrownBy(
-                        () ->
-                                admin.alterClusterConfigs(
-                                                Collections.singletonList(
-                                                        new AlterConfig(
-                                                                ConfigOptions
-                                                                        
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
-                                                                        .key(),
-                                                                "0.0",
-                                                                
AlterConfigOpType.SET)))
-                                        .get())
-                .cause()
-                .isInstanceOf(ConfigException.class)
-                .hasMessageContaining("must be within (0.1, 1.0]");
-
-        // Invalid value: 0.1 (boundary, must be > 0.1)
-        assertThatThrownBy(
-                        () ->
-                                admin.alterClusterConfigs(
-                                                Collections.singletonList(
-                                                        new AlterConfig(
-                                                                ConfigOptions
-                                                                        
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
-                                                                        .key(),
-                                                                "0.1",
-                                                                
AlterConfigOpType.SET)))
-                                        .get())
-                .cause()
-                .isInstanceOf(ConfigException.class)
-                .hasMessageContaining("must be within (0.1, 1.0]");
+    void testDynamicDiskWriteLimitRatios() throws Exception {
+        try {
+            // Both ratios can be lowered atomically when their relationship 
remains valid.
+            admin.alterClusterConfigs(
+                            Arrays.asList(
+                                    new AlterConfig(
+                                            
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                                            "0.95",
+                                            AlterConfigOpType.SET),
+                                    new AlterConfig(
+                                            
ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO
+                                                    .key(),
+                                            "0.90",
+                                            AlterConfigOpType.SET)))
+                    .get();
+            assertConfigEntry(
+                    ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                    "0.95",
+                    ConfigEntry.ConfigSource.DYNAMIC_SERVER_CONFIG);
+            assertConfigEntry(
+                    ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
+                    "0.90",
+                    ConfigEntry.ConfigSource.DYNAMIC_SERVER_CONFIG);
+
+            // Invalid value: 0.0 (must be greater than the recover ratio).
+            assertThatThrownBy(
+                            () ->
+                                    admin.alterClusterConfigs(
+                                                    Collections.singletonList(
+                                                            new AlterConfig(
+                                                                    
ConfigOptions
+                                                                            
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
+                                                                            
.key(),
+                                                                    "0.0",
+                                                                    
AlterConfigOpType.SET)))
+                                            .get())
+                    .cause()
+                    .isInstanceOf(ConfigException.class)
+                    .hasMessageContaining("Invalid disk write-limit 
configuration");
+            // Invalid value: 0.90 (must be strictly greater than the recover 
ratio).
+            assertThatThrownBy(
+                            () ->
+                                    admin.alterClusterConfigs(
+                                                    Collections.singletonList(
+                                                            new AlterConfig(
+                                                                    
ConfigOptions
+                                                                            
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
+                                                                            
.key(),
+                                                                    "0.90",
+                                                                    
AlterConfigOpType.SET)))
+                                            .get())
+                    .cause()
+                    .isInstanceOf(ConfigException.class)
+                    .hasMessageContaining("Invalid disk write-limit 
configuration");
 
-        // Invalid value: 1.5 (must be <= 1.0)
-        assertThatThrownBy(
-                        () ->
-                                admin.alterClusterConfigs(
-                                                Collections.singletonList(
-                                                        new AlterConfig(
-                                                                ConfigOptions
-                                                                        
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
-                                                                        .key(),
-                                                                "1.5",
-                                                                
AlterConfigOpType.SET)))
-                                        .get())
-                .cause()
-                .isInstanceOf(ConfigException.class)
-                .hasMessageContaining("must be within (0.1, 1.0]");
+            // Invalid value: 1.5 (must be <= 1.0)
+            assertThatThrownBy(
+                            () ->
+                                    admin.alterClusterConfigs(
+                                                    Collections.singletonList(
+                                                            new AlterConfig(
+                                                                    
ConfigOptions
+                                                                            
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
+                                                                            
.key(),
+                                                                    "1.5",
+                                                                    
AlterConfigOpType.SET)))
+                                            .get())
+                    .cause()
+                    .isInstanceOf(ConfigException.class)
+                    .hasMessageContaining("Invalid disk write-limit 
configuration");
 
-        // Reset should succeed (restores default)
-        admin.alterClusterConfigs(
-                        Collections.singletonList(
-                                new AlterConfig(
-                                        
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
-                                        null,
-                                        AlterConfigOpType.DELETE)))
-                .get();
+        } finally {
+            // Reset both ratios in one request so the default relationship 
remains valid.
+            admin.alterClusterConfigs(
+                            Arrays.asList(
+                                    new AlterConfig(
+                                            
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                                            null,
+                                            AlterConfigOpType.DELETE),
+                                    new AlterConfig(
+                                            
ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO
+                                                    .key(),
+                                            null,
+                                            AlterConfigOpType.DELETE)))
+                    .get();
+        }
     }
 
     @Test
diff --git 
a/fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java 
b/fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java
index 73eccf1b7..1914df7e1 100644
--- a/fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java
+++ b/fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java
@@ -385,10 +385,23 @@ public class ConfigOptions {
                     .doubleType()
                     .defaultValue(0.85)
                     .withDescription(
-                            "Reject writes when the tablet server data disk 
usage exceeds this ratio. "
-                                    + "Writes resume after the usage drops 
below (ratio - 0.10). "
+                            "Reject writes when the tablet server data disk 
usage reaches this ratio. "
+                                    + "Writes resume when the usage reaches or 
drops below "
+                                    + "server.data-disk.write-recover-ratio. "
                                     + "Set to 1.0 to disable the disk-usage 
protection entirely. "
-                                    + "The valid range is (0.1, 1.0].");
+                                    + "The valid range is "
+                                    + "(server.data-disk.write-recover-ratio, 
1.0]. When lowering "
+                                    + "both ratios dynamically, update them in 
the same request or "
+                                    + "lower 
server.data-disk.write-recover-ratio first.");
+
+    public static final ConfigOption<Double> 
SERVER_DATA_DISK_WRITE_RECOVER_RATIO =
+            key("server.data-disk.write-recover-ratio")
+                    .doubleType()
+                    .defaultValue(0.80)
+                    .withDescription(
+                            "Resume writes when the tablet server data disk 
usage reaches or "
+                                    + "drops below this ratio. The valid range 
is "
+                                    + "(0.0, 
server.data-disk.write-limit-ratio).");
 
     public static final ConfigOption<Duration> SERVER_DATA_DISK_CHECK_INTERVAL 
=
             key("server.data-disk.check-interval")
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/DynamicServerConfig.java 
b/fluss-server/src/main/java/org/apache/fluss/server/DynamicServerConfig.java
index 710c6b675..269e0c483 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/DynamicServerConfig.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/DynamicServerConfig.java
@@ -53,6 +53,7 @@ import static 
org.apache.fluss.config.ConfigOptions.REMOTE_DATA_DIRS;
 import static org.apache.fluss.config.ConfigOptions.REMOTE_DATA_DIRS_STRATEGY;
 import static org.apache.fluss.config.ConfigOptions.REMOTE_DATA_DIRS_WEIGHTS;
 import static 
org.apache.fluss.config.ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO;
+import static 
org.apache.fluss.config.ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO;
 import static org.apache.fluss.config.ConfigOptions.SERVER_SASL_CREDENTIALS;
 import static 
org.apache.fluss.config.ConfigOptions.SERVER_SASL_PLAIN_JAAS_CONFIG;
 import static org.apache.fluss.utils.concurrent.LockUtils.inReadLock;
@@ -75,6 +76,7 @@ class DynamicServerConfig {
                             LOG_REPLICA_MIN_IN_SYNC_REPLICAS_NUMBER.key(),
                             KV_SHARED_RATE_LIMITER_BYTES_PER_SEC.key(),
                             KV_SNAPSHOT_INTERVAL.key(),
+                            SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
                             SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
                             // Config options for remote.data.dirs
                             REMOTE_DATA_DIRS.key(),
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java
index d4fbcbb97..ba9287b27 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/coordinator/CoordinatorServer.java
@@ -40,7 +40,7 @@ import org.apache.fluss.server.metadata.ServerMetadataCache;
 import org.apache.fluss.server.metrics.ServerMetricUtils;
 import org.apache.fluss.server.metrics.group.CoordinatorMetricGroup;
 import org.apache.fluss.server.metrics.group.LakeTieringMetricGroup;
-import org.apache.fluss.server.storage.DiskWriteLimitRatioValidator;
+import org.apache.fluss.server.storage.DiskWriteLimitConfigValidator;
 import org.apache.fluss.server.zk.ZkEpoch;
 import org.apache.fluss.server.zk.ZooKeeperClient;
 import org.apache.fluss.server.zk.ZooKeeperUtils;
@@ -299,7 +299,7 @@ public class CoordinatorServer extends ServerBase {
             dynamicConfigManager.register(lakeCatalogDynamicLoader);
             dynamicConfigManager.register(remoteDirDynamicLoader);
             // Register stateless validators for coordinator-side upfront 
validation
-            dynamicConfigManager.registerValidator(new 
DiskWriteLimitRatioValidator());
+            dynamicConfigManager.register(new DiskWriteLimitConfigValidator());
             
rpcServer.getServerReconfigurables().forEach(dynamicConfigManager::register);
             dynamicConfigManager.startup();
 
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskUsageMonitor.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskUsageMonitor.java
index 8a27c1ef3..16f7d90a8 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskUsageMonitor.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskUsageMonitor.java
@@ -30,38 +30,36 @@ import static 
org.apache.fluss.utils.Preconditions.checkNotNull;
 
 /**
  * Periodically samples the local data disk usage ratio and toggles the tablet 
server write-lock
- * state with a fixed 10% hysteresis: writes are locked when the usage reaches 
the configured
- * write-limit ratio and resume only after the usage drops below {@code (limit 
- 0.10)}. The monitor
- * is single-state and intended to be driven by a scheduler thread; it never 
blocks.
+ * state with a configurable hysteresis: writes are locked when the usage 
reaches the configured
+ * write-limit ratio and resume only after the usage reaches or drops below 
the configured
+ * write-recover ratio. The monitor is single-state and intended to be driven 
by a scheduler thread;
+ * it never blocks.
  */
 @Internal
 public final class DiskUsageMonitor {
 
-    /** Fixed hysteresis between the lock and unlock thresholds. */
-    public static final double RECOVER_GAP = 0.10;
-
     private static final Logger LOG = 
LoggerFactory.getLogger(DiskUsageMonitor.class);
 
     private final int serverId;
     private final DiskUsageCollector collector;
     private volatile double writeLimitRatio;
-    private volatile double recoverThreshold;
+    private volatile double writeRecoverRatio;
     private final Listener listener;
 
     private volatile boolean locked;
     private volatile double lastUsageRatio;
 
     public DiskUsageMonitor(
-            int serverId, DiskUsageCollector collector, double 
writeLimitRatio, Listener listener) {
-        checkArgument(
-                writeLimitRatio > 0.0 && writeLimitRatio <= 1.0,
-                "%s must be within (0.0, 1.0], but was %s",
-                "server.data-disk.write-limit-ratio",
-                writeLimitRatio);
+            int serverId,
+            DiskUsageCollector collector,
+            double writeLimitRatio,
+            double writeRecoverRatio,
+            Listener listener) {
+        checkValidWriteLimitConfig(writeLimitRatio, writeRecoverRatio);
         this.serverId = serverId;
         this.collector = checkNotNull(collector, "collector");
         this.writeLimitRatio = writeLimitRatio;
-        this.recoverThreshold = Math.max(0.0, writeLimitRatio - RECOVER_GAP);
+        this.writeRecoverRatio = writeRecoverRatio;
         this.listener = checkNotNull(listener, "listener");
     }
 
@@ -85,7 +83,7 @@ public final class DiskUsageMonitor {
                     serverId,
                     String.format("%.4f", usage * 100),
                     String.format("%.2f", writeLimitRatio * 100),
-                    String.format("%.2f", recoverThreshold * 100),
+                    String.format("%.2f", writeRecoverRatio * 100),
                     locked);
         }
         update(usage);
@@ -108,19 +106,19 @@ public final class DiskUsageMonitor {
             locked = true;
             LOG.warn(
                     "TabletServer {} disk usage reached {}% (limit {}%); 
rejecting writes "
-                            + "until usage drops below {}%.",
+                            + "until usage reaches or drops below {}%.",
                     serverId,
                     String.format("%.2f", usage * 100),
                     String.format("%.2f", writeLimitRatio * 100),
-                    String.format("%.2f", recoverThreshold * 100));
-        } else if (wasLocked && usage <= recoverThreshold) {
+                    String.format("%.2f", writeRecoverRatio * 100));
+        } else if (wasLocked && usage <= writeRecoverRatio) {
             locked = false;
             LOG.info(
                     "TabletServer {} disk usage dropped to {}% (recover 
threshold {}%); "
                             + "resuming writes.",
                     serverId,
                     String.format("%.2f", usage * 100),
-                    String.format("%.2f", recoverThreshold * 100));
+                    String.format("%.2f", writeRecoverRatio * 100));
         }
         listener.onSample(lastUsageRatio, locked);
     }
@@ -137,23 +135,39 @@ public final class DiskUsageMonitor {
         return writeLimitRatio;
     }
 
-    public double getRecoverThreshold() {
-        return recoverThreshold;
+    public double getWriteRecoverRatio() {
+        return writeRecoverRatio;
     }
 
     /**
-     * Dynamically updates the write-limit ratio and the derived recover 
threshold. The new ratio
-     * takes effect on the next {@link #runOnce()} invocation or {@link 
#update(double)} call.
+     * Dynamically updates the write-limit and write-recover ratios. The new 
values take effect on
+     * the next {@link #runOnce()} invocation or {@link #update(double)} call.
      *
-     * @param newRatio the new write-limit ratio, must be within (0.0, 1.0]
+     * @param newRatio the new write-limit ratio, must be within 
(newRecoverRatio, 1.0]
+     * @param newRecoverRatio the new write-recover ratio, must be within 
(0.0, newRatio)
      */
-    public void updateWriteLimitRatio(double newRatio) {
-        checkArgument(
-                newRatio > 0.0 && newRatio <= 1.0,
-                "server.data-disk.write-limit-ratio must be within (0.0, 1.0], 
but was %s",
-                newRatio);
+    public void updateWriteLimitConfig(double newRatio, double 
newRecoverRatio) {
+        checkValidWriteLimitConfig(newRatio, newRecoverRatio);
         this.writeLimitRatio = newRatio;
-        this.recoverThreshold = Math.max(0.0, newRatio - RECOVER_GAP);
+        this.writeRecoverRatio = newRecoverRatio;
+    }
+
+    /**
+     * Dynamically updates the write-limit ratio while keeping the current 
write-recover ratio.
+     *
+     * @param newRatio the new write-limit ratio, must be greater than the 
current write-recover
+     *     ratio and no greater than 1.0
+     */
+    public void updateWriteLimitRatio(double newRatio) {
+        updateWriteLimitConfig(newRatio, writeRecoverRatio);
+    }
+
+    private static void checkValidWriteLimitConfig(
+            double writeLimitRatio, double writeRecoverRatio) {
+        String validationError =
+                DiskWriteLimitConfigValidator.getValidationError(
+                        writeLimitRatio, writeRecoverRatio);
+        checkArgument(validationError == null, validationError);
     }
 
     /** Receives every sample for downstream state synchronization (e.g. 
metrics gauges). */
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitConfigValidator.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitConfigValidator.java
new file mode 100644
index 000000000..fcc103fdb
--- /dev/null
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitConfigValidator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.fluss.server.storage;
+
+import org.apache.fluss.config.ConfigOptions;
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.config.cluster.ServerReconfigurable;
+import org.apache.fluss.exception.ConfigException;
+
+/**
+ * Validator for the data disk write-limit configuration.
+ *
+ * <p>This validator is registered on the CoordinatorServer so invalid dynamic 
updates are rejected
+ * before they are persisted to ZooKeeper. The TabletServer performs the same 
validation through
+ * {@link LocalDiskManager}.
+ */
+public class DiskWriteLimitConfigValidator implements ServerReconfigurable {
+
+    @Override
+    public void validate(Configuration newConfig) throws ConfigException {
+        String validationError =
+                getValidationError(
+                        
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO),
+                        
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO));
+        if (validationError != null) {
+            throw new ConfigException(validationError);
+        }
+    }
+
+    @Override
+    public void reconfigure(Configuration newConfig) {}
+
+    static String getValidationError(double writeLimitRatio, double 
writeRecoverRatio) {
+        if (writeRecoverRatio > 0.0
+                && writeRecoverRatio < writeLimitRatio
+                && writeLimitRatio <= 1.0) {
+            return null;
+        }
+        return String.format(
+                "Invalid disk write-limit configuration: %s must be within 
(0.0, %s), and %s "
+                        + "must be no greater than 1.0; %s=%s, %s=%s",
+                ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
+                ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                writeLimitRatio,
+                ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
+                writeRecoverRatio);
+    }
+}
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitRatioValidator.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitRatioValidator.java
deleted file mode 100644
index 14fbbaa2b..000000000
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/DiskWriteLimitRatioValidator.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.fluss.server.storage;
-
-import org.apache.fluss.config.ConfigOptions;
-import org.apache.fluss.config.cluster.ConfigValidator;
-import org.apache.fluss.exception.ConfigException;
-
-import javax.annotation.Nullable;
-
-/**
- * Stateless validator for {@link 
ConfigOptions#SERVER_DATA_DISK_WRITE_LIMIT_RATIO}.
- *
- * <p>This validator ensures the disk write-limit ratio stays within the valid 
range (0.1, 1.0]. The
- * lower bound of 0.1 (exclusive) guarantees the recover threshold (ratio - 
0.10) remains positive,
- * ensuring the system can always recover from a write-locked state. It is 
registered on the
- * CoordinatorServer so that invalid values are rejected upfront during {@code 
AlterConfigs}, before
- * being persisted to ZooKeeper.
- *
- * <p>The same range check is also performed on the TabletServer side by 
{@link LocalDiskManager}
- * (via {@link 
org.apache.fluss.config.cluster.ServerReconfigurable#validate}), providing
- * defense-in-depth.
- */
-public class DiskWriteLimitRatioValidator implements ConfigValidator<Double> {
-
-    @Override
-    public String configKey() {
-        return ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key();
-    }
-
-    @Override
-    public void validate(@Nullable Double oldValue, @Nullable Double newValue)
-            throws ConfigException {
-        if (newValue == null) {
-            // Deletion (reset to default) is always valid.
-            return;
-        }
-        if (newValue <= 0.1 || newValue > 1.0) {
-            throw new ConfigException(
-                    String.format(
-                            "Invalid %s: must be within (0.1, 1.0], but was 
%s",
-                            
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(), newValue));
-        }
-    }
-}
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/LocalDiskManager.java
 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/LocalDiskManager.java
index a1afe73bc..b12e897ab 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/storage/LocalDiskManager.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/storage/LocalDiskManager.java
@@ -102,12 +102,17 @@ public final class LocalDiskManager implements Closeable, 
ServerReconfigurable {
     /**
      * Configured high-water-mark ratio: writes are rejected once any single 
backing {@link
      * java.nio.file.FileStore}'s usage ratio reaches this value. Writes 
resume after the usage
-     * drops below {@code diskWriteLimitRatio - 0.10} (the recover gap is 
owned by {@link
-     * DiskUsageMonitor}). This field is volatile because it can be changed at 
runtime via dynamic
-     * reconfiguration.
+     * reaches or drops below {@link #diskWriteRecoverRatio}. This field is 
volatile because it can
+     * be changed at runtime via dynamic reconfiguration.
      */
     private volatile double diskWriteLimitRatio;
 
+    /**
+     * Configured low-water-mark ratio at which writes resume. This field is 
volatile because it can
+     * be changed at runtime via dynamic reconfiguration.
+     */
+    private volatile double diskWriteRecoverRatio;
+
     /**
      * Whether the local tablet server is currently rejecting writes because 
the data disk usage has
      * reached {@link #diskWriteLimitRatio}. Updated by the {@link 
DiskUsageMonitor} scheduler
@@ -124,23 +129,13 @@ public final class LocalDiskManager implements Closeable, 
ServerReconfigurable {
 
     private LocalDiskManager(Configuration conf) throws IOException {
         this.serverId = conf.getInt(ConfigOptions.TABLET_SERVER_ID);
-        List<File> configuredDataDirs = resolveConfiguredDataDirs(conf);
-        List<File> initializedDataDirs;
-        try {
-            initializedDataDirs = initializeDataDirs(configuredDataDirs);
-        } catch (Throwable throwable) {
-            close();
-            throw throwable;
-        }
-        this.dataDirs = Collections.unmodifiableList(initializedDataDirs);
-
         this.diskWriteLimitRatio = 
conf.get(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO);
-        if (diskWriteLimitRatio <= 0.0 || diskWriteLimitRatio > 1.0) {
-            throw new IllegalConfigurationException(
-                    String.format(
-                            "%s must be within (0.0, 1.0], but was %s",
-                            
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
-                            diskWriteLimitRatio));
+        this.diskWriteRecoverRatio = 
conf.get(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO);
+        String diskWriteLimitValidationError =
+                DiskWriteLimitConfigValidator.getValidationError(
+                        diskWriteLimitRatio, diskWriteRecoverRatio);
+        if (diskWriteLimitValidationError != null) {
+            throw new 
IllegalConfigurationException(diskWriteLimitValidationError);
         }
         this.diskCheckIntervalMs =
                 
conf.get(ConfigOptions.SERVER_DATA_DISK_CHECK_INTERVAL).toMillis();
@@ -151,11 +146,22 @@ public final class LocalDiskManager implements Closeable, 
ServerReconfigurable {
                             
ConfigOptions.SERVER_DATA_DISK_CHECK_INTERVAL.key(),
                             diskCheckIntervalMs));
         }
+
+        List<File> configuredDataDirs = resolveConfiguredDataDirs(conf);
+        List<File> initializedDataDirs;
+        try {
+            initializedDataDirs = initializeDataDirs(configuredDataDirs);
+        } catch (Throwable throwable) {
+            close();
+            throw throwable;
+        }
+        this.dataDirs = Collections.unmodifiableList(initializedDataDirs);
         this.diskUsageMonitor =
                 new DiskUsageMonitor(
                         serverId,
                         new DiskUsageCollector(this.dataDirs),
                         diskWriteLimitRatio,
+                        diskWriteRecoverRatio,
                         (usage, locked) -> {
                             this.lastDiskUsageRatio = usage;
                             this.diskWriteLocked = locked;
@@ -497,6 +503,13 @@ public final class LocalDiskManager implements Closeable, 
ServerReconfigurable {
         return diskWriteLimitRatio;
     }
 
+    /**
+     * @return the configured low-water-mark ratio at which writes resume.
+     */
+    public double getDiskWriteRecoverRatio() {
+        return diskWriteRecoverRatio;
+    }
+
     /**
      * Throws {@link DiskWriteLockedException} when the local data disk usage 
has crossed the
      * configured write-limit ratio. Only client-driven writes ({@code 
appendLog} / {@code putKv})
@@ -515,41 +528,56 @@ public final class LocalDiskManager implements Closeable, 
ServerReconfigurable {
     }
 
     // ------------------------------------------------------------------------
-    // ServerReconfigurable: dynamic write-limit-ratio update
+    // ServerReconfigurable: dynamic disk write-limit update
     // ------------------------------------------------------------------------
 
     @Override
     public void validate(Configuration newConfig) throws ConfigException {
         double newRatio = 
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO);
-        if (newRatio <= 0.1 || newRatio > 1.0) {
-            throw new ConfigException(
-                    String.format(
-                            "Invalid %s: must be within (0.1, 1.0], but was 
%s",
-                            
ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(), newRatio));
-        }
+        double newRecoverRatio = 
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO);
+        validateDiskWriteLimitConfig(newRatio, newRecoverRatio);
     }
 
     @Override
-    public void reconfigure(Configuration newConfig) {
+    public void reconfigure(Configuration newConfig) throws ConfigException {
         double newRatio = 
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO);
-        if (Double.compare(newRatio, diskWriteLimitRatio) == 0) {
+        double newRecoverRatio = 
newConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO);
+        validateDiskWriteLimitConfig(newRatio, newRecoverRatio);
+        if (Double.compare(newRatio, diskWriteLimitRatio) == 0
+                && Double.compare(newRecoverRatio, diskWriteRecoverRatio) == 
0) {
             LOG.debug(
-                    "{} unchanged: {}",
+                    "{}/{} unchanged: {}/{}",
                     ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
-                    newRatio);
+                    ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
+                    newRatio,
+                    newRecoverRatio);
             return;
         }
         double oldRatio = diskWriteLimitRatio;
+        double oldRecoverRatio = diskWriteRecoverRatio;
         diskWriteLimitRatio = newRatio;
-        diskUsageMonitor.updateWriteLimitRatio(newRatio);
+        diskWriteRecoverRatio = newRecoverRatio;
+        diskUsageMonitor.updateWriteLimitConfig(newRatio, newRecoverRatio);
         // Trigger an immediate check so the new threshold takes effect 
without waiting
         // for the next scheduled tick.
         diskUsageMonitor.runOnce();
         LOG.info(
-                "{} reconfigured: {} -> {} (immediate check triggered)",
+                "{}/{} reconfigured: {}/{} -> {}/{} (immediate check 
triggered)",
                 ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
                 oldRatio,
-                newRatio);
+                oldRecoverRatio,
+                newRatio,
+                newRecoverRatio);
+    }
+
+    private static void validateDiskWriteLimitConfig(double newRatio, double 
newRecoverRatio)
+            throws ConfigException {
+        String validationError =
+                DiskWriteLimitConfigValidator.getValidationError(newRatio, 
newRecoverRatio);
+        if (validationError != null) {
+            throw new ConfigException(validationError);
+        }
     }
 
     /**
diff --git 
a/fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java 
b/fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java
index adf9a59bd..b73390fdc 100644
--- 
a/fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java
+++ 
b/fluss-server/src/main/java/org/apache/fluss/server/tablet/TabletServer.java
@@ -323,7 +323,7 @@ public class TabletServer extends ServerBase {
             
dynamicConfigManager.register(replicaManager.getKvSnapshotContext());
             // Register replicaManager to dynamicConfigManager for dynamic 
config
             dynamicConfigManager.register(replicaManager);
-            // Register localDiskManager for dynamic 
server.data-disk.write-limit-ratio
+            // Register localDiskManager for dynamic disk write-limit and 
recover ratios.
             dynamicConfigManager.register(localDiskManager);
             
rpcServer.getServerReconfigurables().forEach(dynamicConfigManager::register);
 
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/DynamicConfigChangeTest.java
 
b/fluss-server/src/test/java/org/apache/fluss/server/DynamicConfigChangeTest.java
index 839c8c69a..a311a2986 100644
--- 
a/fluss-server/src/test/java/org/apache/fluss/server/DynamicConfigChangeTest.java
+++ 
b/fluss-server/src/test/java/org/apache/fluss/server/DynamicConfigChangeTest.java
@@ -28,6 +28,7 @@ import 
org.apache.fluss.server.coordinator.LakeCatalogDynamicLoader;
 import org.apache.fluss.server.coordinator.remote.RemoteDirDynamicLoader;
 import org.apache.fluss.server.coordinator.remote.RoundRobinRemoteDirSelector;
 import 
org.apache.fluss.server.coordinator.remote.WeightedRoundRobinRemoteDirSelector;
+import org.apache.fluss.server.storage.DiskWriteLimitConfigValidator;
 import org.apache.fluss.server.storage.LocalDiskManager;
 import org.apache.fluss.server.zk.NOPErrorHandler;
 import org.apache.fluss.server.zk.ZooKeeperClient;
@@ -58,9 +59,7 @@ import static org.apache.fluss.metadata.DataLakeFormat.PAIMON;
 import static org.apache.fluss.record.TestData.DEFAULT_REMOTE_DATA_DIR;
 import static org.apache.fluss.testutils.common.CommonTestUtils.retry;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.assertj.core.api.Assertions.within;
 
 /** Test for {@link DynamicConfigManager}. */
 public class DynamicConfigChangeTest {
@@ -207,15 +206,10 @@ public class DynamicConfigChangeTest {
 
             // Setting `datalake.paimon.*` without setting `datalake.format` 
should pass because
             // prefix validation is skipped.
-            assertThatCode(
-                            () ->
-                                    dynamicConfigManager.alterConfigs(
-                                            Collections.singletonList(
-                                                    new AlterConfig(
-                                                            
"datalake.iceberg.type",
-                                                            "rest",
-                                                            
AlterConfigOpType.SET))))
-                    .doesNotThrowAnyException();
+            dynamicConfigManager.alterConfigs(
+                    Collections.singletonList(
+                            new AlterConfig(
+                                    "datalake.iceberg.type", "rest", 
AlterConfigOpType.SET)));
 
             
assertThat(lakeCatalogDynamicLoader.getLakeCatalogContainer().getDataLakeFormat())
                     .isNull();
@@ -351,17 +345,12 @@ public class DynamicConfigChangeTest {
         dynamicConfigManager.startup();
 
         // Adjust rate limiter value - should succeed
-        assertThatCode(
-                        () ->
-                                dynamicConfigManager.alterConfigs(
-                                        Collections.singletonList(
-                                                new AlterConfig(
-                                                        ConfigOptions
-                                                                
.KV_SHARED_RATE_LIMITER_BYTES_PER_SEC
-                                                                .key(),
-                                                        "200MB",
-                                                        
AlterConfigOpType.SET))))
-                .doesNotThrowAnyException();
+        dynamicConfigManager.alterConfigs(
+                Collections.singletonList(
+                        new AlterConfig(
+                                
ConfigOptions.KV_SHARED_RATE_LIMITER_BYTES_PER_SEC.key(),
+                                "200MB",
+                                AlterConfigOpType.SET)));
 
         // Verify config was persisted to ZK
         Map<String, String> zkConfig = zookeeperClient.fetchEntityConfig();
@@ -448,15 +437,12 @@ public class DynamicConfigChangeTest {
         dynamicConfigManager.startup();
 
         // Change snapshot interval to 5 minutes - should succeed
-        assertThatCode(
-                        () ->
-                                dynamicConfigManager.alterConfigs(
-                                        Collections.singletonList(
-                                                new AlterConfig(
-                                                        
ConfigOptions.KV_SNAPSHOT_INTERVAL.key(),
-                                                        "5min",
-                                                        
AlterConfigOpType.SET))))
-                .doesNotThrowAnyException();
+        dynamicConfigManager.alterConfigs(
+                Collections.singletonList(
+                        new AlterConfig(
+                                ConfigOptions.KV_SNAPSHOT_INTERVAL.key(),
+                                "5min",
+                                AlterConfigOpType.SET)));
 
         // Verify config was persisted to ZK
         Map<String, String> zkConfig = zookeeperClient.fetchEntityConfig();
@@ -569,17 +555,12 @@ public class DynamicConfigChangeTest {
         dynamicConfigManager.startup();
 
         // Change min-in-sync-replicas to 2 - should succeed
-        assertThatCode(
-                        () ->
-                                dynamicConfigManager.alterConfigs(
-                                        Collections.singletonList(
-                                                new AlterConfig(
-                                                        ConfigOptions
-                                                                
.LOG_REPLICA_MIN_IN_SYNC_REPLICAS_NUMBER
-                                                                .key(),
-                                                        "2",
-                                                        
AlterConfigOpType.SET))))
-                .doesNotThrowAnyException();
+        dynamicConfigManager.alterConfigs(
+                Collections.singletonList(
+                        new AlterConfig(
+                                
ConfigOptions.LOG_REPLICA_MIN_IN_SYNC_REPLICAS_NUMBER.key(),
+                                "2",
+                                AlterConfigOpType.SET)));
 
         // Verify config was persisted to ZK
         Map<String, String> zkConfig = zookeeperClient.fetchEntityConfig();
@@ -614,7 +595,50 @@ public class DynamicConfigChangeTest {
     }
 
     @Test
-    void testDynamicDiskWriteLimitRatioChange(@TempDir File tempDir) throws 
Exception {
+    void testCoordinatorValidatesDiskWriteLimitConfigAgainstDefaults() throws 
Exception {
+        DynamicConfigManager dynamicConfigManager = 
createDiskWriteLimitDynamicConfigManager();
+
+        alterDiskWriteRecoverRatio(dynamicConfigManager, "0.70");
+
+        assertThatThrownBy(() -> 
alterDiskWriteRecoverRatio(dynamicConfigManager, "0.0"))
+                .isInstanceOf(ConfigException.class);
+
+        assertThatThrownBy(() -> 
alterDiskWriteLimitRatio(dynamicConfigManager, "0.70"))
+                .isInstanceOf(ConfigException.class);
+
+        alterDiskWriteLimitRatio(dynamicConfigManager, "1.0");
+
+        assertThatThrownBy(() -> 
alterDiskWriteLimitRatio(dynamicConfigManager, "1.1"))
+                .isInstanceOf(ConfigException.class);
+
+        assertThat(zookeeperClient.fetchEntityConfig())
+                
.containsEntry(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(), "1.0")
+                
.containsEntry(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(), 
"0.70");
+    }
+
+    @Test
+    void testCoordinatorValidatesRecoverRatioWhenLoweringDiskWriteLimitRatio() 
throws Exception {
+        DynamicConfigManager dynamicConfigManager = 
createDiskWriteLimitDynamicConfigManager();
+
+        assertThatThrownBy(() -> 
alterDiskWriteLimitRatio(dynamicConfigManager, "0.70"))
+                .isInstanceOf(ConfigException.class);
+
+        assertThat(zookeeperClient.fetchEntityConfig())
+                
.doesNotContainKey(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key());
+
+        alterDiskWriteRecoverRatio(dynamicConfigManager, "0.60");
+        alterDiskWriteLimitRatio(dynamicConfigManager, "0.70");
+
+        assertThatThrownBy(() -> 
alterDiskWriteRecoverRatio(dynamicConfigManager, "0.70"))
+                .isInstanceOf(ConfigException.class);
+
+        assertThat(zookeeperClient.fetchEntityConfig())
+                
.containsEntry(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(), "0.70")
+                
.containsEntry(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(), 
"0.60");
+    }
+
+    @Test
+    void testDynamicDiskWriteLimitConfigChange(@TempDir File tempDir) throws 
Exception {
         File dataDir = new File(tempDir, "data-0");
         assertThat(dataDir.mkdirs()).isTrue();
 
@@ -633,70 +657,89 @@ public class DynamicConfigChangeTest {
 
             // Verify initial state
             
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.85);
+            
assertThat(localDiskManager.getDiskWriteRecoverRatio()).isEqualTo(0.80);
             
assertThat(localDiskManager.getDiskUsageMonitor().getWriteLimitRatio()).isEqualTo(0.85);
-            
assertThat(localDiskManager.getDiskUsageMonitor().getRecoverThreshold())
-                    .isEqualTo(0.75);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteRecoverRatio())
+                    .isEqualTo(0.80);
 
-            // Lower the limit to 0.70 via dynamic config
-            assertThatCode(
-                            () ->
-                                    dynamicConfigManager.alterConfigs(
-                                            Collections.singletonList(
-                                                    new AlterConfig(
-                                                            ConfigOptions
-                                                                    
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
-                                                                    .key(),
-                                                            "0.70",
-                                                            
AlterConfigOpType.SET))))
-                    .doesNotThrowAnyException();
+            // Lower the recover ratio before lowering the limit.
+            alterDiskWriteRecoverRatio(dynamicConfigManager, "0.60");
+            alterDiskWriteLimitRatio(dynamicConfigManager, "0.70");
 
-            // Verify the new ratio took effect immediately (reconfigure 
triggers runOnce)
+            // Verify both ratios took effect immediately (reconfigure 
triggers runOnce).
             
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.70);
+            
assertThat(localDiskManager.getDiskWriteRecoverRatio()).isEqualTo(0.60);
             
assertThat(localDiskManager.getDiskUsageMonitor().getWriteLimitRatio()).isEqualTo(0.70);
-            
assertThat(localDiskManager.getDiskUsageMonitor().getRecoverThreshold())
-                    .isCloseTo(0.60, within(1e-9));
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteRecoverRatio())
+                    .isEqualTo(0.60);
+
+            alterDiskWriteRecoverRatio(dynamicConfigManager, "0.65");
+
+            
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.70);
+            
assertThat(localDiskManager.getDiskWriteRecoverRatio()).isEqualTo(0.65);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteLimitRatio()).isEqualTo(0.70);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteRecoverRatio())
+                    .isEqualTo(0.65);
 
             // Verify config was persisted to ZK
             Map<String, String> zkConfig = zookeeperClient.fetchEntityConfig();
             
assertThat(zkConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key()))
                     .isEqualTo("0.70");
-        }
-    }
+            
assertThat(zkConfig.get(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key()))
+                    .isEqualTo("0.65");
 
-    @Test
-    void testPreventInvalidDiskWriteLimitRatio(@TempDir File tempDir) throws 
Exception {
-        File dataDir = new File(tempDir, "data-0");
-        assertThat(dataDir.mkdirs()).isTrue();
+            assertThatThrownBy(() -> 
alterDiskWriteLimitRatio(dynamicConfigManager, "0.0"))
+                    .isInstanceOf(ConfigException.class);
 
-        Configuration configuration = new Configuration();
-        configuration.setInt(ConfigOptions.TABLET_SERVER_ID, 0);
-        configuration.setString(ConfigOptions.DATA_DIR, 
dataDir.getAbsolutePath());
-        configuration.set(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO, 
0.85);
+            assertThatThrownBy(() -> 
alterDiskWriteRecoverRatio(dynamicConfigManager, "0.0"))
+                    .isInstanceOf(ConfigException.class);
+
+            assertThatThrownBy(() -> 
alterDiskWriteRecoverRatio(dynamicConfigManager, "0.70"))
+                    .isInstanceOf(ConfigException.class);
 
+            Configuration invalidReconfigure = new 
Configuration(configuration);
+            
invalidReconfigure.set(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO, 0.65);
+            
invalidReconfigure.set(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO, 
0.65);
+            assertThatThrownBy(() -> 
localDiskManager.reconfigure(invalidReconfigure))
+                    .isInstanceOf(ConfigException.class);
+
+            
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.70);
+            
assertThat(localDiskManager.getDiskWriteRecoverRatio()).isEqualTo(0.65);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteLimitRatio()).isEqualTo(0.70);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteRecoverRatio())
+                    .isEqualTo(0.65);
+        }
+    }
+
+    private static DynamicConfigManager 
createDiskWriteLimitDynamicConfigManager()
+            throws Exception {
         DynamicConfigManager dynamicConfigManager =
-                new DynamicConfigManager(zookeeperClient, configuration, true);
+                new DynamicConfigManager(zookeeperClient, new Configuration(), 
true);
+        dynamicConfigManager.register(new DiskWriteLimitConfigValidator());
+        dynamicConfigManager.startup();
+        return dynamicConfigManager;
+    }
 
-        try (LocalDiskManager localDiskManager = 
LocalDiskManager.create(configuration)) {
-            dynamicConfigManager.register(localDiskManager);
-            dynamicConfigManager.startup();
+    private static void alterDiskWriteLimitRatio(
+            DynamicConfigManager dynamicConfigManager, String value) throws 
Exception {
+        alterConfig(
+                dynamicConfigManager,
+                ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO.key(),
+                value);
+    }
 
-            // Try to set to invalid value 0.0 - should be rejected
-            assertThatThrownBy(
-                            () ->
-                                    dynamicConfigManager.alterConfigs(
-                                            Collections.singletonList(
-                                                    new AlterConfig(
-                                                            ConfigOptions
-                                                                    
.SERVER_DATA_DISK_WRITE_LIMIT_RATIO
-                                                                    .key(),
-                                                            "0.0",
-                                                            
AlterConfigOpType.SET))))
-                    .isInstanceOf(ConfigException.class)
-                    .hasMessageContaining("must be within (0.1, 1.0]");
+    private static void alterDiskWriteRecoverRatio(
+            DynamicConfigManager dynamicConfigManager, String value) throws 
Exception {
+        alterConfig(
+                dynamicConfigManager,
+                ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO.key(),
+                value);
+    }
 
-            // Verify the ratio was NOT changed
-            
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.85);
-        }
+    private static void alterConfig(
+            DynamicConfigManager dynamicConfigManager, String key, String 
value) throws Exception {
+        dynamicConfigManager.alterConfigs(
+                Collections.singletonList(new AlterConfig(key, value, 
AlterConfigOpType.SET)));
     }
 
     @Test
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/replica/ReplicaManagerTest.java
 
b/fluss-server/src/test/java/org/apache/fluss/server/replica/ReplicaManagerTest.java
index 6dd9ce7fe..b738c5a85 100644
--- 
a/fluss-server/src/test/java/org/apache/fluss/server/replica/ReplicaManagerTest.java
+++ 
b/fluss-server/src/test/java/org/apache/fluss/server/replica/ReplicaManagerTest.java
@@ -516,7 +516,7 @@ class ReplicaManagerTest extends ReplicaTestBase {
                 .isInstanceOf(DiskWriteLockedException.class)
                 .hasMessageContaining("data disk usage");
 
-        // recover when usage drops below (limit - 0.10) -> 0.75
+        // recover when usage drops below (limit - 0.05) -> 0.80
         replicaManager.getDiskUsageMonitor().update(0.50);
         assertThat(replicaManager.isDiskWriteLocked()).isFalse();
 
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/storage/DiskUsageMonitorTest.java
 
b/fluss-server/src/test/java/org/apache/fluss/server/storage/DiskUsageMonitorTest.java
index dc1291d08..8bc9b06f4 100644
--- 
a/fluss-server/src/test/java/org/apache/fluss/server/storage/DiskUsageMonitorTest.java
+++ 
b/fluss-server/src/test/java/org/apache/fluss/server/storage/DiskUsageMonitorTest.java
@@ -26,25 +26,43 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.assertj.core.api.Assertions.within;
 
 /** Test for {@link DiskUsageMonitor}. */
 class DiskUsageMonitorTest {
 
     private static final int SERVER_ID = 7;
+    private static final double WRITE_RECOVER_RATIO = 0.80;
 
     @Test
-    void testInvalidLimitRatioRejected() {
+    void testInvalidLimitConfigRejected() {
         DiskUsageCollector collector = new 
DiskUsageCollector(Collections.emptyList());
         assertThatThrownBy(
                         () ->
                                 new DiskUsageMonitor(
-                                        SERVER_ID, collector, 0.0, (usage, 
locked) -> {}))
+                                        SERVER_ID,
+                                        collector,
+                                        0.0,
+                                        WRITE_RECOVER_RATIO,
+                                        (usage, locked) -> {}))
                 .isInstanceOf(IllegalArgumentException.class);
         assertThatThrownBy(
                         () ->
                                 new DiskUsageMonitor(
-                                        SERVER_ID, collector, 1.5, (usage, 
locked) -> {}))
+                                        SERVER_ID,
+                                        collector,
+                                        1.1,
+                                        WRITE_RECOVER_RATIO,
+                                        (usage, locked) -> {}))
+                .isInstanceOf(IllegalArgumentException.class);
+        assertThatThrownBy(
+                        () ->
+                                new DiskUsageMonitor(
+                                        SERVER_ID, collector, 0.85, 0.0, 
(usage, locked) -> {}))
+                .isInstanceOf(IllegalArgumentException.class);
+        assertThatThrownBy(
+                        () ->
+                                new DiskUsageMonitor(
+                                        SERVER_ID, collector, 0.85, 0.85, 
(usage, locked) -> {}))
                 .isInstanceOf(IllegalArgumentException.class);
     }
 
@@ -81,8 +99,8 @@ class DiskUsageMonitorTest {
         monitor.update(0.86);
         assertThat(monitor.isLocked()).isTrue();
 
-        // recover threshold is 0.75, 0.80 still above it -> stay locked
-        monitor.update(0.80);
+        // recover threshold is 0.80, 0.81 still above it -> stay locked
+        monitor.update(0.81);
         assertThat(monitor.isLocked()).isTrue();
     }
 
@@ -94,16 +112,16 @@ class DiskUsageMonitorTest {
         monitor.update(0.90);
         assertThat(monitor.isLocked()).isTrue();
 
-        monitor.update(0.75);
+        monitor.update(monitor.getWriteRecoverRatio());
         assertThat(monitor.isLocked()).isFalse();
         assertThat(recorder.lastLocked.get()).isFalse();
     }
 
     @Test
-    void testRecoverThresholdNeverNegative() {
-        DiskUsageMonitor monitor = newMonitor(0.05, new Recorder());
-        assertThat(monitor.getRecoverThreshold()).isEqualTo(0.0);
-        assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.05);
+    void testRecoverThresholdUsesConfiguredRecoverRatio() {
+        DiskUsageMonitor monitor = newMonitor(0.85, 0.65, new Recorder());
+        assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.85);
+        assertThat(monitor.getWriteRecoverRatio()).isEqualTo(0.65);
     }
 
     @Test
@@ -115,7 +133,8 @@ class DiskUsageMonitorTest {
                         Collections.singletonList(
                                 new 
File("/__fluss_disk_monitor_does_not_exist__/x")));
         Recorder recorder = new Recorder();
-        DiskUsageMonitor monitor = new DiskUsageMonitor(SERVER_ID, failing, 
0.85, recorder);
+        DiskUsageMonitor monitor =
+                new DiskUsageMonitor(SERVER_ID, failing, 0.85, 
WRITE_RECOVER_RATIO, recorder);
 
         // First put the monitor into the locked state via update().
         monitor.update(0.95);
@@ -137,7 +156,8 @@ class DiskUsageMonitorTest {
         // Reuse a real collector backed by an empty data dirs list -> always 
returns 0.0.
         DiskUsageCollector collector = new 
DiskUsageCollector(Collections.emptyList());
         Recorder recorder = new Recorder();
-        DiskUsageMonitor monitor = new DiskUsageMonitor(SERVER_ID, collector, 
0.85, recorder);
+        DiskUsageMonitor monitor =
+                new DiskUsageMonitor(SERVER_ID, collector, 0.85, 
WRITE_RECOVER_RATIO, recorder);
 
         monitor.runOnce();
         assertThat(monitor.isLocked()).isFalse();
@@ -147,66 +167,86 @@ class DiskUsageMonitorTest {
     }
 
     private DiskUsageMonitor newMonitor(double limit, 
DiskUsageMonitor.Listener listener) {
+        return newMonitor(limit, WRITE_RECOVER_RATIO, listener);
+    }
+
+    private DiskUsageMonitor newMonitor(
+            double limit, double writeRecoverRatio, DiskUsageMonitor.Listener 
listener) {
         return new DiskUsageMonitor(
-                SERVER_ID, new DiskUsageCollector(Collections.emptyList()), 
limit, listener);
+                SERVER_ID,
+                new DiskUsageCollector(Collections.emptyList()),
+                limit,
+                writeRecoverRatio,
+                listener);
     }
 
     @Test
-    void testUpdateWriteLimitRatioChangesThresholds() {
+    void testUpdateWriteLimitRatioKeepsRecoverRatio() {
         Recorder recorder = new Recorder();
         DiskUsageMonitor monitor = newMonitor(0.85, recorder);
 
-        // Initially ratio=0.85, recover=0.75
+        // Initially ratio=0.85, recover=0.80
         assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.85);
-        assertThat(monitor.getRecoverThreshold()).isEqualTo(0.75);
+        
assertThat(monitor.getWriteRecoverRatio()).isEqualTo(WRITE_RECOVER_RATIO);
 
         // Simulate usage at 0.82 — should NOT lock (below 0.85)
         monitor.update(0.82);
         assertThat(monitor.isLocked()).isFalse();
 
-        // Lower the limit to 0.80 — now 0.82 exceeds the new limit
-        monitor.updateWriteLimitRatio(0.80);
-        assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.80);
-        assertThat(monitor.getRecoverThreshold()).isCloseTo(0.70, 
within(1e-9));
+        // Lower the limit to 0.81; the absolute recover ratio remains 0.80.
+        monitor.updateWriteLimitRatio(0.81);
+        assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.81);
+        assertThat(monitor.getWriteRecoverRatio()).isEqualTo(0.80);
 
         // Re-evaluate with same usage — should lock
         monitor.update(0.82);
         assertThat(monitor.isLocked()).isTrue();
         assertThat(recorder.lastLocked.get()).isTrue();
 
-        // Raise the limit to 0.90 and recover threshold becomes 0.80
-        // 0.82 > 0.80 so should remain locked
+        // Raise the limit to 0.90; the recover ratio remains 0.80.
         monitor.updateWriteLimitRatio(0.90);
-        monitor.update(0.82);
+        monitor.update(0.86);
         assertThat(monitor.isLocked()).isTrue();
 
-        // Drop usage to 0.79 — now below new recover threshold 0.80, should 
unlock
-        monitor.update(0.79);
+        // Drop usage to 0.80; at the recover ratio, writes should resume.
+        monitor.update(0.80);
         assertThat(monitor.isLocked()).isFalse();
     }
 
+    @Test
+    void testUpdateWriteLimitConfigChangesRecoverRatio() {
+        Recorder recorder = new Recorder();
+        DiskUsageMonitor monitor = newMonitor(0.85, recorder);
+
+        monitor.updateWriteLimitConfig(0.85, 0.75);
+        assertThat(monitor.getWriteLimitRatio()).isEqualTo(0.85);
+        assertThat(monitor.getWriteRecoverRatio()).isEqualTo(0.75);
+
+        monitor.update(0.90);
+        assertThat(monitor.isLocked()).isTrue();
+        monitor.update(0.76);
+        assertThat(monitor.isLocked()).isTrue();
+        monitor.update(0.75);
+        assertThat(monitor.isLocked()).isFalse();
+        assertThat(recorder.lastLocked.get()).isFalse();
+    }
+
     @Test
     void testDisabledWhenRatioIsOne() {
         Recorder recorder = new Recorder();
         DiskUsageMonitor monitor = newMonitor(1.0, recorder);
 
-        // Even at 100% disk usage, should NOT lock when ratio = 1.0
         monitor.update(1.0);
         assertThat(monitor.isLocked()).isFalse();
         assertThat(recorder.lastLocked.get()).isFalse();
         assertThat(recorder.lastUsage.get()).isEqualTo(1.0);
 
-        // Any usage below 1.0 should also remain unlocked
-        monitor.update(0.99);
-        assertThat(monitor.isLocked()).isFalse();
-
-        // If previously locked (via dynamic update), switching to 1.0 should 
unlock immediately
-        DiskUsageMonitor monitor2 = newMonitor(0.80, recorder);
-        monitor2.update(0.85); // lock it
+        DiskUsageMonitor monitor2 = newMonitor(0.85, recorder);
+        monitor2.update(0.85);
         assertThat(monitor2.isLocked()).isTrue();
 
         monitor2.updateWriteLimitRatio(1.0);
-        monitor2.update(0.85); // same usage, but ratio=1.0 -> should unlock
+        monitor2.update(0.85);
         assertThat(monitor2.isLocked()).isFalse();
         assertThat(recorder.lastLocked.get()).isFalse();
     }
@@ -218,10 +258,16 @@ class DiskUsageMonitorTest {
 
         assertThatThrownBy(() -> monitor.updateWriteLimitRatio(0.0))
                 .isInstanceOf(IllegalArgumentException.class);
+        assertThatThrownBy(() -> 
monitor.updateWriteLimitRatio(WRITE_RECOVER_RATIO))
+                .isInstanceOf(IllegalArgumentException.class);
         assertThatThrownBy(() -> monitor.updateWriteLimitRatio(1.1))
                 .isInstanceOf(IllegalArgumentException.class);
         assertThatThrownBy(() -> monitor.updateWriteLimitRatio(-0.5))
                 .isInstanceOf(IllegalArgumentException.class);
+        assertThatThrownBy(() -> monitor.updateWriteLimitConfig(0.85, 0.0))
+                .isInstanceOf(IllegalArgumentException.class);
+        assertThatThrownBy(() -> monitor.updateWriteLimitConfig(0.85, 0.85))
+                .isInstanceOf(IllegalArgumentException.class);
     }
 
     /** Captures the latest sample observed by the listener. */
diff --git 
a/fluss-server/src/test/java/org/apache/fluss/server/storage/LocalDiskManagerTest.java
 
b/fluss-server/src/test/java/org/apache/fluss/server/storage/LocalDiskManagerTest.java
index 78c5bcfd5..ec4f30426 100644
--- 
a/fluss-server/src/test/java/org/apache/fluss/server/storage/LocalDiskManagerTest.java
+++ 
b/fluss-server/src/test/java/org/apache/fluss/server/storage/LocalDiskManagerTest.java
@@ -358,6 +358,40 @@ class LocalDiskManagerTest {
                 .hasMessageContaining("None of the specified data dirs");
     }
 
+    @Test
+    void testDiskWriteLimitConfigValidation() throws Exception {
+        File dataDir = new File(tempDir, "data-1");
+
+        try (LocalDiskManager localDiskManager = 
LocalDiskManager.create(createConf(dataDir))) {
+            
assertThat(localDiskManager.getDiskWriteLimitRatio()).isEqualTo(0.85);
+            
assertThat(localDiskManager.getDiskWriteRecoverRatio()).isEqualTo(0.80);
+            
assertThat(localDiskManager.getDiskUsageMonitor().getWriteRecoverRatio())
+                    .isEqualTo(0.80);
+        }
+
+        Configuration disabledRatioConf = createConf(new File(tempDir, 
"disabled-ratio"));
+        
disabledRatioConf.set(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO, 1.0);
+        try (LocalDiskManager localDiskManager = 
LocalDiskManager.create(disabledRatioConf)) {
+            localDiskManager.getDiskUsageMonitor().update(1.0);
+            assertThat(localDiskManager.isDiskWriteLocked()).isFalse();
+        }
+
+        Configuration invalidRatioConf = createConf(new File(tempDir, 
"invalid-ratio"));
+        invalidRatioConf.set(ConfigOptions.SERVER_DATA_DISK_WRITE_LIMIT_RATIO, 
1.1);
+        assertThatThrownBy(() -> LocalDiskManager.create(invalidRatioConf))
+                .isInstanceOf(IllegalConfigurationException.class);
+
+        Configuration zeroRecoverRatioConf = createConf(new File(tempDir, 
"zero-recover-ratio"));
+        
zeroRecoverRatioConf.set(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO, 
0.0);
+        assertThatThrownBy(() -> LocalDiskManager.create(zeroRecoverRatioConf))
+                .isInstanceOf(IllegalConfigurationException.class);
+
+        Configuration equalRecoverRatioConf = createConf(new File(tempDir, 
"equal-recover-ratio"));
+        
equalRecoverRatioConf.set(ConfigOptions.SERVER_DATA_DISK_WRITE_RECOVER_RATIO, 
0.85);
+        assertThatThrownBy(() -> 
LocalDiskManager.create(equalRecoverRatioConf))
+                .isInstanceOf(IllegalConfigurationException.class);
+    }
+
     private Configuration createConf(File... dataDirs) {
         Configuration conf = new Configuration();
         conf.set(ConfigOptions.TABLET_SERVER_ID, TABLET_SERVER_ID);
diff --git a/website/docs/maintenance/configuration.md 
b/website/docs/maintenance/configuration.md
index fae8b45f8..a8f6d966f 100644
--- a/website/docs/maintenance/configuration.md
+++ b/website/docs/maintenance/configuration.md
@@ -67,22 +67,23 @@ during the Fluss cluster working.
 
 ## TabletServer
 
-| Option                                           | Type       | Default      
   | Description                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
-|--------------------------------------------------|------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| tablet-server.id                                 | Integer    | (None)       
   | The id for the tablet server.                                              
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
-| tablet-server.rack                               | String     | (None)       
   | The rack for the TabletServer. This will be used in rack aware bucket 
assignment for fault tolerance. Examples: `RACK1`, `cn-hangzhou-server10`       
                                                                                
                                                                                
                                                                                
                   [...]
-| data.dir                                         | String     | 
/tmp/fluss-data | This configuration controls the directory where Fluss will 
store its data. The default value is /tmp/fluss-data                            
                                                                                
                                                                                
                                                                                
                              [...]
-| server.writer-id.expiration-time                 | Duration   | 7d           
   | The time that the tablet server will wait without receiving any write 
request from a client before expiring the related status. The default value is 
7 days.                                                                         
                                                                                
                                                                                
                    [...]
-| server.writer-id.expiration-check-interval       | Duration   | 10min        
   | The interval at which to remove writer ids that have expired due to 
`server.writer-id.expiration-time passing. The default value is 10 minutes.     
                                                                                
                                                                                
                                                                                
                     [...]
-| server.background.threads                        | Integer    | 10           
   | The number of threads to use for various background processing tasks. The 
default value is 10.                                                            
                                                                                
                                                                                
                                                                                
               [...]
-| server.buffer.memory-size                        | MemorySize | 256mb        
   | The total bytes of memory the server can use, e.g, buffer write-ahead-log 
rows.                                                                           
                                                                                
                                                                                
                                                                                
               [...]
-| server.buffer.page-size                          | MemorySize | 128kb        
   | Size of every page in memory buffers (`server.buffer.memory-size`).        
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
-| server.buffer.per-request-memory-size            | MemorySize | 16mb         
   | The minimum number of bytes that will be allocated by the writer rounded 
down to the closest multiple of server.buffer.page-size. It must be greater 
than or equal to server.buffer.page-size. This option allows to allocate memory 
in batches to have better CPU-cached friendliness due to contiguous segments.   
                                                                                
                    [...]
-| server.buffer.wait-timeout                       | Duration   | 2^(63)-1ns   
   | Defines how long the buffer pool will block when waiting for segments.     
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
-| tablet-server.controlled-shutdown.max-retries    | Integer    | 3            
   | Maximum number of attempts to transfer leadership before proceeding with 
an unclean shutdown during a controlled shutdown procedure.                     
                                                                                
                                                                                
                                                                                
                [...]
-| tablet-server.controlled-shutdown.retry-interval | Duration   | 1000ms       
   | Time interval between retry attempts when trying to transfer leadership 
during controlled shutdown.                                                     
                                                                                
                                                                                
                                                                                
                 [...]
-| server.data-disk.write-limit-ratio               | Double     | 0.85         
   | Reject writes when the tablet server data disk usage exceeds this ratio. 
Writes resume after the usage drops below `(ratio - 0.10)`. The monitor reports 
the maximum usage across all distinct file stores so that a single nearly-full 
disk is never masked by other low-usage disks. Set to `1.0` to disable the 
disk-usage protection entirely. The valid range is `(0.1, 1.0]`. This 
configuration can be updated dy [...]
-| server.data-disk.check-interval                  | Duration   | 30s          
   | The interval at which the tablet server samples the local data disk usage 
for the write-protection state machine. A shorter interval narrows the time 
window during which writes can still flow in after the disk crosses the limit 
ratio, at the cost of slightly more frequent `statvfs` calls (which are 
in-memory and cheap). The default 30s is suitable for typical write workloads.  
                             [...]
+| Option                                           | Type       | Default      
    | Description                                                               
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+|--------------------------------------------------|------------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
+| tablet-server.id                                 | Integer    | (None)       
    | The id for the tablet server.                                             
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| tablet-server.rack                               | String     | (None)       
    | The rack for the TabletServer. This will be used in rack aware bucket 
assignment for fault tolerance. Examples: `RACK1`, `cn-hangzhou-server10`       
                                                                                
                                                                                
                                                                                
                  [...]
+| data.dir                                         | String     | 
/tmp/fluss-data  | This configuration controls the directory where Fluss will 
store its data. The default value is /tmp/fluss-data                            
                                                                                
                                                                                
                                                                                
                             [...]
+| server.writer-id.expiration-time                 | Duration   | 7d           
    | The time that the tablet server will wait without receiving any write 
request from a client before expiring the related status. The default value is 
7 days.                                                                         
                                                                                
                                                                                
                   [...]
+| server.writer-id.expiration-check-interval       | Duration   | 10min        
    | The interval at which to remove writer ids that have expired due to 
`server.writer-id.expiration-time passing. The default value is 10 minutes.     
                                                                                
                                                                                
                                                                                
                    [...]
+| server.background.threads                        | Integer    | 10           
    | The number of threads to use for various background processing tasks. The 
default value is 10.                                                            
                                                                                
                                                                                
                                                                                
              [...]
+| server.buffer.memory-size                        | MemorySize | 256mb        
    | The total bytes of memory the server can use, e.g, buffer write-ahead-log 
rows.                                                                           
                                                                                
                                                                                
                                                                                
              [...]
+| server.buffer.page-size                          | MemorySize | 128kb        
    | Size of every page in memory buffers (`server.buffer.memory-size`).       
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| server.buffer.per-request-memory-size            | MemorySize | 16mb         
    | The minimum number of bytes that will be allocated by the writer rounded 
down to the closest multiple of server.buffer.page-size. It must be greater 
than or equal to server.buffer.page-size. This option allows to allocate memory 
in batches to have better CPU-cached friendliness due to contiguous segments.   
                                                                                
                   [...]
+| server.buffer.wait-timeout                       | Duration   | 2^(63)-1ns   
    | Defines how long the buffer pool will block when waiting for segments.    
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
+| tablet-server.controlled-shutdown.max-retries    | Integer    | 3            
    | Maximum number of attempts to transfer leadership before proceeding with 
an unclean shutdown during a controlled shutdown procedure.                     
                                                                                
                                                                                
                                                                                
               [...]
+| tablet-server.controlled-shutdown.retry-interval | Duration   | 1000ms       
    | Time interval between retry attempts when trying to transfer leadership 
during controlled shutdown.                                                     
                                                                                
                                                                                
                                                                                
                [...]
+| server.data-disk.write-limit-ratio               | Double     | 0.85         
    | Reject writes when the tablet server data disk usage reaches this ratio. 
Writes resume when the usage reaches or drops below 
`server.data-disk.write-recover-ratio`. The monitor reports the maximum usage 
across all distinct file stores so that a single nearly-full disk is never 
masked by other low-usage disks. Set to `1.0` to disable the disk-usage 
protection entirely. The valid range is `(server.data-dis [...]
+| server.data-disk.write-recover-ratio             | Double     | 0.80         
    | Resume writes when the tablet server data disk usage reaches or drops 
below this ratio. The valid range is `(0.0, 
server.data-disk.write-limit-ratio)`. This configuration can be updated 
dynamically without server restart.                                             
                                                                                
                                                              [...]
+| server.data-disk.check-interval                  | Duration   | 30s          
    | The interval at which the tablet server samples the local data disk usage 
for the write-protection state machine. A shorter interval narrows the time 
window during which writes can still flow in after the disk crosses the limit 
ratio, at the cost of slightly more frequent `statvfs` calls (which are 
in-memory and cheap). The default 30s is suitable for typical write workloads.  
                            [...]
 
 ## Zookeeper
 

Reply via email to