You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by ad...@apache.org on 2021/11/29 08:07:36 UTC

[ozone] branch master updated: HDDS-5897. Support configuration for including/excluding datanodes for balancing (#2786)

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

adoroszlai 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 7afd64f  HDDS-5897. Support configuration for including/excluding datanodes for balancing (#2786)
7afd64f is described below

commit 7afd64f3d6a454a64b81015ec2d887e990634ae4
Author: Siddhant Sangwan <si...@gmail.com>
AuthorDate: Mon Nov 29 13:37:20 2021 +0530

    HDDS-5897. Support configuration for including/excluding datanodes for balancing (#2786)
---
 .../scm/container/balancer/ContainerBalancer.java  | 25 ++++++++
 .../balancer/ContainerBalancerConfiguration.java   | 64 +++++++++++++++++++
 .../container/balancer/TestContainerBalancer.java  | 74 ++++++++++++++++++++++
 3 files changed, 163 insertions(+)

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 a234682..91dd1b2 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
@@ -77,6 +77,8 @@ public class ContainerBalancer {
   private List<DatanodeUsageInfo> overUtilizedNodes;
   private List<DatanodeUsageInfo> underUtilizedNodes;
   private List<DatanodeUsageInfo> withinThresholdUtilizedNodes;
+  private Set<String> excludeNodes;
+  private Set<String> includeNodes;
   private ContainerBalancerConfiguration config;
   private ContainerBalancerMetrics metrics;
   private long clusterCapacity;
@@ -242,6 +244,11 @@ public class ContainerBalancer {
       }
       return false;
     }
+    this.excludeNodes = config.getExcludeNodes();
+    this.includeNodes = config.getIncludeNodes();
+    // include/exclude nodes from balancing according to configs
+    datanodeUsageInfos.removeIf(datanodeUsageInfo -> shouldExcludeDatanode(
+        datanodeUsageInfo.getDatanodeDetails()));
 
     this.totalNodesInCluster = datanodeUsageInfos.size();
     this.clusterCapacity = 0L;
@@ -668,6 +675,24 @@ public class ContainerBalancer {
   }
 
   /**
+   * Consults the configurations {@link ContainerBalancer#includeNodes} and
+   * {@link ContainerBalancer#excludeNodes} to check if the specified
+   * Datanode should be excluded from balancing.
+   * @param datanode DatanodeDetails to check
+   * @return true if Datanode should be excluded, else false
+   */
+  boolean shouldExcludeDatanode(DatanodeDetails datanode) {
+    if (excludeNodes.contains(datanode.getHostName()) ||
+        excludeNodes.contains(datanode.getIpAddress())) {
+      return true;
+    } else if (!includeNodes.isEmpty()) {
+      return !includeNodes.contains(datanode.getHostName()) &&
+          !includeNodes.contains(datanode.getIpAddress());
+    }
+    return false;
+  }
+
+  /**
    * Updates conditions for balancing, such as total size moved by balancer,
    * total size that has entered a datanode, and total size that has left a
    * datanode in this iteration.
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
index a77c1b7..a0199e0 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/ContainerBalancerConfiguration.java
@@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
 
 import java.time.Duration;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -109,6 +110,20 @@ public final class ContainerBalancerConfiguration {
       "iteration of Container Balancer.")
   private long balancingInterval;
 
+  @Config(key = "include.datanodes", type = ConfigType.STRING, defaultValue =
+      "", tags = {ConfigTag.BALANCER}, description = "A list of Datanode " +
+      "hostnames or ip addresses separated by commas. Only the Datanodes " +
+      "specified in this list are balanced. This configuration is empty by " +
+      "default and is applicable only if it is non-empty.")
+  private String includeNodes = "";
+
+  @Config(key = "exclude.datanodes", type = ConfigType.STRING, defaultValue =
+      "", tags = ConfigTag.BALANCER, description = "A list of Datanode " +
+      "hostnames or ip addresses separated by commas. The Datanodes specified" +
+      " in this list are excluded from balancing. This configuration is empty" +
+      " by default.")
+  private String excludeNodes = "";
+
   private DUFactory.Conf duConf;
 
   /**
@@ -293,6 +308,55 @@ public final class ContainerBalancerConfiguration {
   }
 
   /**
+   * Gets a set of datanode hostnames or ip addresses that will be the exclusive
+   * participants in balancing.
+   * @return Set of hostname or ip address strings, or an empty set if the
+   * configuration is empty
+   */
+  public Set<String> getIncludeNodes() {
+    if (includeNodes.isEmpty()) {
+      return Collections.emptySet();
+    }
+    return Arrays.stream(includeNodes.split(","))
+        .map(String::trim)
+        .collect(Collectors.toSet());
+  }
+
+  /**
+   * Sets the datanodes that will be the exclusive participants in balancing.
+   * Applicable only if the specified string is non-empty.
+   * @param includeNodes a String of datanode hostnames or ip addresses
+   *                     separated by commas
+   */
+  public void setIncludeNodes(String includeNodes) {
+    this.includeNodes = includeNodes;
+  }
+
+  /**
+   * Gets a set of datanode hostnames or ip addresses that will be excluded
+   * from balancing.
+   * @return Set of hostname or ip address strings, or an empty set if the
+   * configuration is empty
+   */
+  public Set<String> getExcludeNodes() {
+    if (excludeNodes.isEmpty()) {
+      return Collections.emptySet();
+    }
+    return Arrays.stream(excludeNodes.split(","))
+        .map(String::trim)
+        .collect(Collectors.toSet());
+  }
+
+  /**
+   * Sets the datanodes that will be excluded from balancing.
+   * @param excludeNodes a String of datanode hostnames or ip addresses
+   *                     separated by commas
+   */
+  public void setExcludeNodes(String excludeNodes) {
+    this.excludeNodes = excludeNodes;
+  }
+
+  /**
    * Gets the {@link OzoneConfiguration} using which this configuration was
    * constructed.
    * @return the {@link OzoneConfiguration} being used by this configuration
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 314f378..e260bb3 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
@@ -44,7 +44,9 @@ import org.apache.hadoop.ozone.OzoneConsts;
 import org.apache.ozone.test.GenericTestUtils;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.mockito.Mockito;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -88,6 +90,9 @@ public class TestContainerBalancer {
       new HashMap<>();
   private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
 
+  @Rule
+  public TemporaryFolder tempFolder = new TemporaryFolder();
+
   /**
    * Sets up configuration values and creates a mock cluster.
    */
@@ -525,6 +530,66 @@ public class TestContainerBalancer {
   }
 
   /**
+   * Tests if {@link ContainerBalancer} follows the includeNodes and
+   * excludeNodes configurations in {@link ContainerBalancerConfiguration}.
+   * If the includeNodes configuration is not empty, only the specified
+   * includeNodes should be included in balancing. excludeNodes should be
+   * excluded from balancing. If a datanode is specified in both include and
+   * exclude configurations, then it should be excluded.
+   */
+  @Test
+  public void balancerShouldFollowExcludeAndIncludeDatanodesConfigurations() {
+    balancerConfiguration.setThreshold(0.1);
+    balancerConfiguration.setIdleIteration(1);
+    balancerConfiguration.setMaxSizeEnteringTarget(10 * OzoneConsts.GB);
+    balancerConfiguration.setMaxSizeToMovePerIteration(100 * OzoneConsts.GB);
+    balancerConfiguration.setMaxDatanodesRatioToInvolvePerIteration(1.0);
+
+    // only these nodes should be included
+    // the ones also specified in excludeNodes should be excluded
+    int firstIncludeIndex = 0, secondIncludeIndex = 1;
+    int thirdIncludeIndex = nodesInCluster.size() - 2;
+    int fourthIncludeIndex = nodesInCluster.size() - 1;
+    String includeNodes =
+        nodesInCluster.get(firstIncludeIndex).getDatanodeDetails()
+            .getIpAddress() + ", " +
+            nodesInCluster.get(secondIncludeIndex).getDatanodeDetails()
+                .getIpAddress() + ", " +
+            nodesInCluster.get(thirdIncludeIndex).getDatanodeDetails()
+                .getHostName() + ", " +
+            nodesInCluster.get(fourthIncludeIndex).getDatanodeDetails()
+                .getHostName();
+
+    // these nodes should be excluded
+    int firstExcludeIndex = 0, secondExcludeIndex = nodesInCluster.size() - 1;
+    String excludeNodes =
+        nodesInCluster.get(firstExcludeIndex).getDatanodeDetails()
+            .getIpAddress() + ", " +
+            nodesInCluster.get(secondExcludeIndex).getDatanodeDetails()
+                .getHostName();
+
+    balancerConfiguration.setExcludeNodes(excludeNodes);
+    balancerConfiguration.setIncludeNodes(includeNodes);
+    containerBalancer.start(balancerConfiguration);
+    sleepWhileBalancing(500);
+    containerBalancer.stop();
+
+    // finally, these should be the only nodes included in balancing
+    // (included - excluded)
+    DatanodeDetails dn1 =
+        nodesInCluster.get(secondIncludeIndex).getDatanodeDetails();
+    DatanodeDetails dn2 =
+        nodesInCluster.get(thirdIncludeIndex).getDatanodeDetails();
+    for (Map.Entry<DatanodeDetails, ContainerMoveSelection> entry :
+        containerBalancer.getSourceToTargetMap().entrySet()) {
+      DatanodeDetails source = entry.getKey();
+      DatanodeDetails target = entry.getValue().getTargetNode();
+      Assert.assertTrue(source.equals(dn1) || source.equals(dn2));
+      Assert.assertTrue(target.equals(dn1) || target.equals(dn2));
+    }
+  }
+
+  /**
    * Determines unBalanced nodes, that is, over and under utilized nodes,
    * according to the generated utilization values for nodes and the threshold.
    *
@@ -696,4 +761,13 @@ public class TestContainerBalancer {
         .setBytesUsed(usedBytes)
         .build();
   }
+
+  private void sleepWhileBalancing(long millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException exception) {
+      Thread.currentThread().interrupt();
+    }
+  }
+
 }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org