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

ashishkumar50 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new cce1ca28944 HDDS-16160. Reduce container balancer idle wait time. 
(#11000)
cce1ca28944 is described below

commit cce1ca28944886218132f01c0e271c1fd0700078
Author: Ashish Kumar <[email protected]>
AuthorDate: Wed Oct 14 17:08:11 2026 +0530

    HDDS-16160. Reduce container balancer idle wait time. (#11000)
---
 .../balancer/ContainerBalancerConfiguration.java   |  8 +++--
 .../docs/content/feature/ContainerBalancer.md      |  2 +-
 .../scm/container/balancer/ContainerBalancer.java  | 41 +++++++++++++++++-----
 .../container/balancer/TestContainerBalancer.java  | 27 ++++++++++++++
 .../TestContainerBalancerDatanodeNodeLimit.java    | 13 +++++--
 .../scm/cli/ContainerBalancerConfigOptions.java    |  2 +-
 .../ozone/TestContainerBalancerOperations.java     |  2 +-
 7 files changed, 79 insertions(+), 16 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
index af4a2304a36..3c909003965 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
@@ -112,9 +112,11 @@ public final class ContainerBalancerConfiguration {
   private long moveReplicationTimeout = Duration.ofMinutes(50).toMillis();
 
   @Config(key = "hdds.container.balancer.balancing.iteration.interval", type = 
ConfigType.TIME,
-      defaultValue = "70m", tags = {ConfigTag.BALANCER}, description =
-      "The interval period between each iteration of Container Balancer.")
-  private long balancingInterval = Duration.ofMinutes(70).toMillis();
+      defaultValue = "3m", tags = {ConfigTag.BALANCER}, description =
+      "The interval to wait between Container Balancer iterations. " +
+      "Container Balancer runs iterations continuously and waits only briefly 
" +
+      "so SCM can receive refreshed datanode usage information before the next 
iteration starts..")
+  private long balancingInterval = Duration.ofMinutes(3).toMillis();
 
   @Config(key = "hdds.container.balancer.include.datanodes", type = 
ConfigType.STRING, defaultValue =
       "", tags = {ConfigTag.BALANCER}, description = "A list of Datanode " +
diff --git a/hadoop-hdds/docs/content/feature/ContainerBalancer.md 
b/hadoop-hdds/docs/content/feature/ContainerBalancer.md
index 94689d53223..b6270c985b8 100644
--- a/hadoop-hdds/docs/content/feature/ContainerBalancer.md
+++ b/hadoop-hdds/docs/content/feature/ContainerBalancer.md
@@ -105,7 +105,7 @@ The Container Balancer can also be configured through the 
`ozone-site.xml` file.
 | `hdds.container.balancer.exclude.containers`           | A comma-separated 
list of container IDs to exclude from balancing.                                
                                     | ""            |
 | `hdds.container.balancer.move.timeout`                 | The amount of time 
to allow a single container to move from source to target.                      
                                    | 65m           |
 | `hdds.container.balancer.move.replication.timeout`     | The amount of time 
to allow a single container's replication from source to target as part of a 
container move.                        | 50m           |
-| `hdds.container.balancer.balancing.iteration.interval` | The interval period 
between each iteration of Container Balancer.                                   
                                   | 70m           |
+| `hdds.container.balancer.balancing.iteration.interval` | The interval to 
wait between iterations. The balancer runs continuously, this wait lets SCM 
receive refreshed Datanode usage information before the next iteration starts.  
| 3m            |
 | `hdds.container.balancer.include.datanodes`            | A comma-separated 
list of Datanode hostnames or IP addresses. Only the Datanodes specified in 
this list are balanced.                  | ""            |
 | `hdds.container.balancer.exclude.datanodes`            | A comma-separated 
list of Datanode hostnames or IP addresses. The Datanodes specified in this 
list are excluded from balancing.        | ""            |
 | `hdds.container.balancer.move.networkTopology.enable`  | Whether to take 
network topology into account when selecting a target for a source.             
                                       | false         |
diff --git 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
index 7515d93a747..d00f92d2065 100644
--- 
a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
+++ 
b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancer.java
@@ -17,6 +17,8 @@
 
 package org.apache.hadoop.hdds.scm.container.balancer;
 
+import static org.apache.hadoop.hdds.HddsConfigKeys.HDDS_NODE_REPORT_INTERVAL;
+import static 
org.apache.hadoop.hdds.HddsConfigKeys.HDDS_NODE_REPORT_INTERVAL_DEFAULT;
 import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeOperationalState.IN_SERVICE;
 import static 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.NodeState.HEALTHY;
 
@@ -32,7 +34,6 @@
 import java.util.concurrent.locks.ReentrantLock;
 import org.apache.hadoop.hdds.conf.OzoneConfiguration;
 import org.apache.hadoop.hdds.conf.StorageUnit;
-import org.apache.hadoop.hdds.fs.DUFactory;
 import org.apache.hadoop.hdds.protocol.DatanodeDetails;
 import 
org.apache.hadoop.hdds.protocol.proto.HddsProtos.ContainerBalancerConfigurationProto;
 import org.apache.hadoop.hdds.scm.ScmConfigKeys;
@@ -308,6 +309,10 @@ public void startBalancer(ContainerBalancerConfiguration 
configuration)
       saveConfiguration(configuration, true, 0);
       this.config = configuration;
 
+      LOG.info("Container Balancer starting with balancing iteration interval 
{} seconds " +
+              "(hdds.container.balancer.balancing.iteration.interval).",
+          configuration.getBalancingInterval().getSeconds());
+
       // Set startedAt only after validation has passed, so a rejected start
       // does not overwrite the recorded start time of the previous run.
       startedAt = OffsetDateTime.now();
@@ -505,13 +510,20 @@ private void 
validateConfiguration(ContainerBalancerConfiguration conf)
               + ".iteration");
     }
 
-    // balancing interval should be greater than DUFactory refresh period
-    DUFactory.Conf duConf = ozoneConfiguration.getObject(DUFactory.Conf.class);
-    long refreshPeriod = duConf.getRefreshPeriod().toMillis();
-    if (conf.getBalancingInterval().toMillis() <= refreshPeriod) {
-      LOG.warn("hdds.container.balancer.balancing.iteration.interval {} " +
-              "should be greater than hdds.datanode.du.refresh.period {}",
-          conf.getBalancingInterval().toMillis(), refreshPeriod);
+    // Balancing interval should be greater than the node report interval so
+    // that SCM receives refreshed datanode usage info between iterations.
+    // Datanode usage is tracked incrementally in memory and reported every
+    // hdds.node.report.interval, so the node report interval (not the hourly
+    // du refresh period) is the relevant freshness bound between iterations.
+    long nodeReportInterval = ozoneConfiguration.getTimeDuration(
+        HDDS_NODE_REPORT_INTERVAL, HDDS_NODE_REPORT_INTERVAL_DEFAULT,
+        TimeUnit.MILLISECONDS);
+    if (isBalancingIntervalBelowNodeReportInterval(
+        conf.getBalancingInterval().toMillis(), nodeReportInterval)) {
+      LOG.warn("hdds.container.balancer.balancing.iteration.interval {}ms " +
+              "should be greater than hdds.node.report.interval {}ms so that " 
+
+              "datanode usage info is refreshed between iterations.",
+          conf.getBalancingInterval().toMillis(), nodeReportInterval);
     }
 
     // "move.replication.timeout" should be lesser than "move.timeout"
@@ -550,6 +562,19 @@ private void 
validateConfiguration(ContainerBalancerConfiguration conf)
     validateEligibleDatanodePool(conf);
   }
 
+  /**
+   * A balancing interval that is not greater than the node report interval 
means
+   * SCM may not receive refreshed datanode usage info before the next 
iteration
+   * starts. Datanode usage is tracked incrementally in memory and reported 
every
+   * node report interval, so that interval (not the hourly du refresh period) 
is
+   * the relevant freshness bound between iterations.
+   */
+  @VisibleForTesting
+  static boolean isBalancingIntervalBelowNodeReportInterval(
+      long balancingIntervalMs, long nodeReportIntervalMs) {
+    return balancingIntervalMs < nodeReportIntervalMs;
+  }
+
   /**
    * Rejects include lists that are fully covered by the corresponding exclude
    * lists, which would leave no datanodes or containers to balance.
diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
index 440cf590f02..fe6c71c3f4c 100644
--- 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancer.java
@@ -460,6 +460,33 @@ public void testRejectInvalidStartupConfiguration() throws 
Exception {
     assertSame(ContainerBalancerTask.Status.STOPPED, 
containerBalancer.getBalancerStatus());
   }
 
+  /**
+   * Container Balancer runs iterations continuously with only a short wait in
+   * between, so the default balancing interval is 3 minutes.
+   */
+  @Test
+  public void testDefaultBalancingIntervalIsThreeMinutes() {
+    ContainerBalancerConfiguration defaultConfig =
+        new 
OzoneConfiguration().getObject(ContainerBalancerConfiguration.class);
+    assertEquals(3, defaultConfig.getBalancingInterval().toMinutes());
+  }
+
+  /**
+   * The freshness bound for the balancing interval is the node report interval
+   * and not the hourly du refresh period. The
+   * default 3m interval is above a typical 1m node report interval.
+   */
+  @Test
+  public void testBalancingIntervalNodeReportFreshnessBoundary() {
+    long nodeReportIntervalMs = TimeUnit.MINUTES.toMillis(1);
+    // Below the node report interval -> too short.
+    assertTrue(ContainerBalancer.isBalancingIntervalBelowNodeReportInterval(
+        TimeUnit.SECONDS.toMillis(30), nodeReportIntervalMs));
+    // The default 3m interval is above the node report interval -> acceptable.
+    assertFalse(ContainerBalancer.isBalancingIntervalBelowNodeReportInterval(
+        TimeUnit.MINUTES.toMillis(3), nodeReportIntervalMs));
+  }
+
   /**
    * Verifies that a start request rejected during validation does not update 
startedAt of last run.
    * Prevents startedAt from advancing past stoppedAt, which previously caused 
a negative duration and CLI status crash.
diff --git 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java
 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java
index a094a84240f..79e192e3d7b 100644
--- 
a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java
+++ 
b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestContainerBalancerDatanodeNodeLimit.java
@@ -620,8 +620,17 @@ public void checkIterationResultTimeout(@Nonnull MockedSCM 
mockedSCM)
 
     ContainerBalancerTask task = mockedSCM.startBalancerTask(config);
     assertEquals(ContainerBalancerTask.IterationResult.ITERATION_COMPLETED, 
task.getIterationResult());
-    assertEquals(1, 
task.getMetrics().getNumContainerMovesCompletedInLatestIteration());
-    
assertThat(task.getMetrics().getNumContainerMovesTimeoutInLatestIteration()).isGreaterThanOrEqualTo(1);
+
+    // The random cluster layout decides how many moves get scheduled. The 
first mocked move completes
+    // instantly; subsequent moves take 150ms which exceeds moveTimeout 
(50ms). If the cluster layout
+    // allows more than one move, exactly the first completes and the rest 
time out. If only one move
+    // is possible in this layout, it completes and no timeouts occur.
+    ContainerBalancerMetrics metrics = task.getMetrics();
+    
assertThat(metrics.getNumContainerMovesCompletedInLatestIteration()).isGreaterThanOrEqualTo(1);
+    if (metrics.getNumContainerMovesScheduledInLatestIteration() > 1) {
+      assertEquals(1, 
metrics.getNumContainerMovesCompletedInLatestIteration());
+      
assertThat(metrics.getNumContainerMovesTimeoutInLatestIteration()).isGreaterThanOrEqualTo(1);
+    }
   }
 
   @ParameterizedTest(name = "MockedSCM #{index}: {0}")
diff --git 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerConfigOptions.java
 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerConfigOptions.java
index e82a954f2fd..fde63d28987 100644
--- 
a/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerConfigOptions.java
+++ 
b/hadoop-ozone/cli-admin/src/main/java/org/apache/hadoop/hdds/scm/cli/ContainerBalancerConfigOptions.java
@@ -69,7 +69,7 @@ public class ContainerBalancerConfigOptions {
 
   @Option(names = {"--balancing-iteration-interval-minutes"},
       description = "The interval period in minutes between each iteration of 
Container Balancer. " +
-          "The value should be positive, with a default of 70 (specify '70' 
for 70 minutes).")
+          "The value should be positive, with a default of 3 (specify '3' for 
3 minutes).")
   private Optional<Integer> balancingInterval;
 
   @Option(names = {"--move-timeout-minutes"},
diff --git 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
index c4f76c2cfd6..72e53562c54 100644
--- 
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
+++ 
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/TestContainerBalancerOperations.java
@@ -193,7 +193,7 @@ public void testIfCBCLIOverridesConfigs() throws Exception {
 
     //If config value is not added in ozone-site.xml and CLI option is not 
passed
     //then it takes the default configuration
-    assertEquals(70, config.getBalancingInterval().toMinutes());
+    assertEquals(3, config.getBalancingInterval().toMinutes());
 
     //If config value is added in ozone-site.xml and CLI option is not passed
     //then it takes the value from ozone-site.xml


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to