You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by lj...@apache.org on 2021/12/22 07:31:57 UTC

[ozone] branch master updated: HDDS-5602. make it configurable to choose the nearest one as the target in the candidates according to networkTopology (#2756)

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

ljain 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 e01b471  HDDS-5602. make it configurable to choose the nearest one as the target in the candidates according to networkTopology (#2756)
e01b471 is described below

commit e01b47184818365dcb7baf528fb2d874209b6b47
Author: Jackson Yao <ja...@tencent.com>
AuthorDate: Wed Dec 22 15:31:31 2021 +0800

    HDDS-5602. make it configurable to choose the nearest one as the target in the candidates according to networkTopology (#2756)
---
 ...etGreedy.java => AbstractFindTargetGreedy.java} |  87 +++++----
 .../scm/container/balancer/ContainerBalancer.java  |  26 ++-
 .../balancer/ContainerBalancerConfiguration.java   |  27 ++-
 .../FindTargetGreedyByNetworkTopology.java         |  79 +++++++++
 .../balancer/FindTargetGreedyByUsageInfo.java      |  52 ++++++
 .../hdds/scm/server/StorageContainerManager.java   |   4 +-
 .../container/balancer/TestContainerBalancer.java  |   4 +-
 .../container/balancer/TestFindTargetStrategy.java | 196 +++++++++++++++++++++
 8 files changed, 428 insertions(+), 47 deletions(-)

diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedy.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/AbstractFindTargetGreedy.java
similarity index 80%
rename from hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedy.java
rename to hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/AbstractFindTargetGreedy.java
index 5508943..a975f04 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedy.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/AbstractFindTargetGreedy.java
@@ -18,6 +18,7 @@
 
 package org.apache.hadoop.hdds.scm.container.balancer;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.apache.hadoop.hdds.protocol.DatanodeDetails;
 import org.apache.hadoop.hdds.scm.ContainerPlacementStatus;
 import org.apache.hadoop.hdds.scm.PlacementPolicy;
@@ -29,32 +30,29 @@ import org.apache.hadoop.hdds.scm.container.ContainerReplica;
 import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo;
 import org.apache.hadoop.hdds.scm.node.NodeManager;
 import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
 /**
- * Find a target giving preference to more under-utilized nodes.
+ * Find a target for a source datanode with greedy strategy.
  */
-public class FindTargetGreedy implements FindTargetStrategy {
-  private static final Logger LOG =
-      LoggerFactory.getLogger(FindTargetGreedy.class);
-
+public abstract class AbstractFindTargetGreedy implements FindTargetStrategy {
+  private Logger logger;
   private ContainerManager containerManager;
   private PlacementPolicy placementPolicy;
   private Map<DatanodeDetails, Long> sizeEnteringNode;
   private NodeManager nodeManager;
   private ContainerBalancerConfiguration config;
   private Double upperLimit;
-  private TreeSet<DatanodeUsageInfo> potentialTargets;
+  private Collection<DatanodeUsageInfo> potentialTargets;
 
-  public FindTargetGreedy(
+  protected AbstractFindTargetGreedy(
       ContainerManager containerManager,
       PlacementPolicy placementPolicy,
       NodeManager nodeManager) {
@@ -62,37 +60,36 @@ public class FindTargetGreedy implements FindTargetStrategy {
     this.containerManager = containerManager;
     this.placementPolicy = placementPolicy;
     this.nodeManager = nodeManager;
+  }
 
-    potentialTargets = new TreeSet<>((a, b) -> {
-      double currentUsageOfA = a.calculateUtilization(
-          sizeEnteringNode.get(a.getDatanodeDetails()));
-      double currentUsageOfB = b.calculateUtilization(
-          sizeEnteringNode.get(b.getDatanodeDetails()));
-      int ret = Double.compare(currentUsageOfA, currentUsageOfB);
-      if (ret != 0) {
-        return ret;
-      }
-      UUID uuidA = a.getDatanodeDetails().getUuid();
-      UUID uuidB = b.getDatanodeDetails().getUuid();
-      return uuidA.compareTo(uuidB);
-    });
+  protected void setLogger(Logger log) {
+    logger = log;
+  }
+
+  protected void setPotentialTargets(Collection<DatanodeUsageInfo> pt) {
+    potentialTargets = pt;
   }
 
   private void setUpperLimit(Double upperLimit){
     this.upperLimit = upperLimit;
   }
 
-  private void setPotentialTargets(
-      List<DatanodeUsageInfo> potentialTargetDataNodes) {
-    sizeEnteringNode.clear();
-    potentialTargetDataNodes.forEach(
-        p -> sizeEnteringNode.put(p.getDatanodeDetails(), 0L));
-    potentialTargets.clear();
-    potentialTargets.addAll(potentialTargetDataNodes);
+  protected int compareByUsage(DatanodeUsageInfo a, DatanodeUsageInfo b) {
+    double currentUsageOfA = a.calculateUtilization(
+        sizeEnteringNode.get(a.getDatanodeDetails()));
+    double currentUsageOfB = b.calculateUtilization(
+        sizeEnteringNode.get(b.getDatanodeDetails()));
+    int ret = Double.compare(currentUsageOfA, currentUsageOfB);
+    if (ret != 0) {
+      return ret;
+    }
+    UUID uuidA = a.getDatanodeDetails().getUuid();
+    UUID uuidB = b.getDatanodeDetails().getUuid();
+    return uuidA.compareTo(uuidB);
   }
 
   private void setConfiguration(ContainerBalancerConfiguration conf) {
-    this.config = conf;
+    config = conf;
   }
 
   /**
@@ -109,6 +106,7 @@ public class FindTargetGreedy implements FindTargetStrategy {
   @Override
   public ContainerMoveSelection findTargetForContainerMove(
       DatanodeDetails source, Set<ContainerID> candidateContainers) {
+    sortTargetForSource(source);
     for (DatanodeUsageInfo targetInfo : potentialTargets) {
       DatanodeDetails target = targetInfo.getDatanodeDetails();
       for (ContainerID container : candidateContainers) {
@@ -118,7 +116,7 @@ public class FindTargetGreedy implements FindTargetStrategy {
           replicas = containerManager.getContainerReplicas(container);
           containerInfo = containerManager.getContainer(container);
         } catch (ContainerNotFoundException e) {
-          LOG.warn("Could not get Container {} from Container Manager for " +
+          logger.warn("Could not get Container {} from Container Manager for " +
               "obtaining replicas in Container Balancer.", container, e);
           continue;
         }
@@ -132,8 +130,8 @@ public class FindTargetGreedy implements FindTargetStrategy {
         }
       }
     }
-    LOG.info("Container Balancer could not find a target for source datanode " +
-        "{}", source.getUuidString());
+    logger.info("Container Balancer could not find a target for " +
+        "source datanode {}", source.getUuidString());
     return null;
   }
 
@@ -153,7 +151,7 @@ public class FindTargetGreedy implements FindTargetStrategy {
     try {
       containerInfo = containerManager.getContainer(containerID);
     } catch (ContainerNotFoundException e) {
-      LOG.warn("Could not get Container {} from Container Manager while " +
+      logger.warn("Could not get Container {} from Container Manager while " +
           "checking if container move satisfies placement policy in " +
           "Container Balancer.", containerID.toString(), e);
       return false;
@@ -212,7 +210,7 @@ public class FindTargetGreedy implements FindTargetStrategy {
       }
       return;
     }
-    LOG.warn("Cannot find {} in the candidates target nodes",
+    logger.warn("Cannot find {} in the candidates target nodes",
         target.getUuid());
   }
 
@@ -225,6 +223,23 @@ public class FindTargetGreedy implements FindTargetStrategy {
                            Double upLimit) {
     setConfiguration(conf);
     setUpperLimit(upLimit);
-    setPotentialTargets(potentialDataNodes);
+    sizeEnteringNode.clear();
+    potentialDataNodes.forEach(
+        p -> sizeEnteringNode.put(p.getDatanodeDetails(), 0L));
+    potentialTargets.clear();
+    potentialTargets.addAll(potentialDataNodes);
+  }
+
+  @VisibleForTesting
+  public Collection<DatanodeUsageInfo> getPotentialTargets() {
+    return potentialTargets;
   }
+
+  /**
+   * sort potentialTargets for specified source datanode according to
+   * network topology if enabled.
+   * @param source the specified source datanode
+   */
+  @VisibleForTesting
+  public abstract void sortTargetForSource(DatanodeDetails source);
 }
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 91dd1b2..5e31bee 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
@@ -30,6 +30,7 @@ import org.apache.hadoop.hdds.scm.container.ContainerManager;
 import org.apache.hadoop.hdds.scm.container.ReplicationManager;
 import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat;
 import org.apache.hadoop.hdds.scm.ha.SCMContext;
+import org.apache.hadoop.hdds.scm.net.NetworkTopology;
 import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo;
 import org.apache.hadoop.hdds.scm.node.NodeManager;
 import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException;
@@ -85,6 +86,8 @@ public class ContainerBalancer {
   private long clusterUsed;
   private long clusterRemaining;
   private double clusterAvgUtilisation;
+  private PlacementPolicy placementPolicy;
+  private NetworkTopology networkTopology;
   private double upperLimit;
   private double lowerLimit;
   private volatile boolean balancerRunning;
@@ -115,6 +118,7 @@ public class ContainerBalancer {
       ReplicationManager replicationManager,
       OzoneConfiguration ozoneConfiguration,
       final SCMContext scmContext,
+      NetworkTopology networkTopology,
       PlacementPolicy placementPolicy) {
     this.nodeManager = nodeManager;
     this.containerManager = containerManager;
@@ -129,10 +133,10 @@ public class ContainerBalancer {
     this.underUtilizedNodes = new ArrayList<>();
     this.withinThresholdUtilizedNodes = new ArrayList<>();
     this.unBalancedNodes = new ArrayList<>();
+    this.placementPolicy = placementPolicy;
+    this.networkTopology = networkTopology;
 
     this.lock = new ReentrantLock();
-    findTargetStrategy = new FindTargetGreedy(
-        containerManager, placementPolicy, nodeManager);
     findSourceStrategy = new FindSourceGreedy(nodeManager);
   }
 
@@ -179,6 +183,13 @@ public class ContainerBalancer {
     this.maxDatanodesRatioToInvolvePerIteration =
         config.getMaxDatanodesRatioToInvolvePerIteration();
     this.maxSizeToMovePerIteration = config.getMaxSizeToMovePerIteration();
+    if (config.getNetworkTopologyEnable()) {
+      findTargetStrategy = new FindTargetGreedyByNetworkTopology(
+          containerManager, placementPolicy, nodeManager, networkTopology);
+    } else {
+      findTargetStrategy = new FindTargetGreedyByUsageInfo(containerManager,
+          placementPolicy, nodeManager);
+    }
     for (int i = 0; i < idleIteration && balancerRunning; i++) {
       // stop balancing if iteration is not initialized
       if (!initializeIteration()) {
@@ -367,11 +378,6 @@ public class ContainerBalancer {
     try {
       // match each overUtilized node with a target
       while (true) {
-        DatanodeDetails source =
-            findSourceStrategy.getNextCandidateSourceDataNode();
-        if (source == null) {
-          break;
-        }
         if (!isBalancerRunning()) {
           return IterationResult.ITERATION_INTERRUPTED;
         }
@@ -381,6 +387,12 @@ public class ContainerBalancer {
           return result;
         }
 
+        DatanodeDetails source =
+            findSourceStrategy.getNextCandidateSourceDataNode();
+        if (source == null) {
+          break;
+        }
+
         ContainerMoveSelection moveSelection = matchSourceWithTarget(source);
         if (moveSelection != null) {
           isMoveGenerated = true;
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 a0199e0..698b3b0 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
@@ -118,12 +118,19 @@ public final class ContainerBalancerConfiguration {
   private String includeNodes = "";
 
   @Config(key = "exclude.datanodes", type = ConfigType.STRING, defaultValue =
-      "", tags = ConfigTag.BALANCER, description = "A list of Datanode " +
+      "", 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 = "";
 
+  @Config(key = "move.networkTopology.enable", type = ConfigType.BOOLEAN,
+      defaultValue = "false", tags = {ConfigTag.BALANCER},
+      description = "whether to take network topology into account when " +
+          "selecting a target for a source. " +
+          "This configuration is false by default.")
+  private boolean networkTopologyEnable = false;
+
   private DUFactory.Conf duConf;
 
   /**
@@ -197,6 +204,24 @@ public final class ContainerBalancerConfiguration {
   }
 
   /**
+   * Get the NetworkTopologyEnable value for Container Balancer.
+   *
+   * @return the boolean value of networkTopologyEnable
+   */
+  public Boolean getNetworkTopologyEnable() {
+    return networkTopologyEnable;
+  }
+
+  /**
+   * Set the NetworkTopologyEnable value for Container Balancer.
+   *
+   * @param enable the boolean value to be set to networkTopologyEnable
+   */
+  public void setNetworkTopologyEnable(Boolean enable) {
+    networkTopologyEnable = enable;
+  }
+
+  /**
    * Gets the ratio of maximum number of datanodes that will be involved in
    * balancing by Container Balancer in one iteration to the total number of
    * healthy, in-service nodes known to balancer.
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByNetworkTopology.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByNetworkTopology.java
new file mode 100644
index 0000000..ad1c8de
--- /dev/null
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByNetworkTopology.java
@@ -0,0 +1,79 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.hdds.scm.container.balancer;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.scm.PlacementPolicy;
+import org.apache.hadoop.hdds.scm.container.ContainerManager;
+import org.apache.hadoop.hdds.scm.net.NetworkTopology;
+import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo;
+import org.apache.hadoop.hdds.scm.node.NodeManager;
+import org.slf4j.LoggerFactory;
+
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * an implementation of FindTargetGreedy, which will always select the
+ * target with the shortest distance according to network topology
+ * distance to the give source datanode.
+ */
+public class FindTargetGreedyByNetworkTopology
+    extends AbstractFindTargetGreedy {
+
+  private NetworkTopology networkTopology;
+  private List potentialTargets;
+
+  public FindTargetGreedyByNetworkTopology(
+      ContainerManager containerManager,
+      PlacementPolicy placementPolicy,
+      NodeManager nodeManager,
+      NetworkTopology networkTopology) {
+    super(containerManager, placementPolicy, nodeManager);
+    setLogger(LoggerFactory.getLogger(FindTargetGreedyByNetworkTopology.class));
+    potentialTargets = new LinkedList<>();
+    setPotentialTargets(potentialTargets);
+    this.networkTopology = networkTopology;
+  }
+
+  /**
+   * sort potentialTargets for specified source datanode according to
+   * network topology.
+   * @param source the specified source datanode
+   */
+  @VisibleForTesting
+  public void sortTargetForSource(DatanodeDetails source) {
+    Collections.sort(potentialTargets,
+        (DatanodeUsageInfo da, DatanodeUsageInfo db) -> {
+        DatanodeDetails a = da.getDatanodeDetails();
+        DatanodeDetails b = db.getDatanodeDetails();
+        // sort by network topology first
+        int distanceToA = networkTopology.getDistanceCost(source, a);
+        int distanceToB = networkTopology.getDistanceCost(source, b);
+        if (distanceToA != distanceToB) {
+          return distanceToA - distanceToB;
+        }
+        // if distance to source is equal , sort by usage
+        return compareByUsage(da, db);
+      });
+  }
+}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByUsageInfo.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByUsageInfo.java
new file mode 100644
index 0000000..03cdc51
--- /dev/null
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/container/balancer/FindTargetGreedyByUsageInfo.java
@@ -0,0 +1,52 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.hdds.scm.container.balancer;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.scm.PlacementPolicy;
+import org.apache.hadoop.hdds.scm.container.ContainerManager;
+import org.apache.hadoop.hdds.scm.node.NodeManager;
+import org.slf4j.LoggerFactory;
+
+import java.util.TreeSet;
+
+/**
+ * an implementation of FindTargetGreedy, which will always select the
+ * target with the lowest space usage.
+ */
+public class FindTargetGreedyByUsageInfo extends AbstractFindTargetGreedy {
+  public FindTargetGreedyByUsageInfo(
+      ContainerManager containerManager,
+      PlacementPolicy placementPolicy,
+      NodeManager nodeManager) {
+    super(containerManager, placementPolicy, nodeManager);
+    setLogger(LoggerFactory.getLogger(FindTargetGreedyByUsageInfo.class));
+    setPotentialTargets(new TreeSet<>((a, b) -> compareByUsage(a, b)));
+  }
+
+  /**
+   * do nothing , since TreeSet is ordered itself.
+   */
+  @VisibleForTesting
+  public void sortTargetForSource(DatanodeDetails source) {
+    //noop, Treeset is naturally sorted.
+    return;
+  }
+}
diff --git a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
index 0db807c..a3a1abb 100644
--- a/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
+++ b/hadoop-hdds/server-scm/src/main/java/org/apache/hadoop/hdds/scm/server/StorageContainerManager.java
@@ -380,8 +380,8 @@ public final class StorageContainerManager extends ServiceRuntimeInfoImpl
 
     initializeEventHandlers();
 
-    containerBalancer = new ContainerBalancer(scmNodeManager,
-        containerManager, replicationManager, configuration, scmContext,
+    containerBalancer = new ContainerBalancer(scmNodeManager, containerManager,
+        replicationManager, configuration, scmContext, clusterMap,
         ContainerPlacementPolicyFactory
             .getPolicy(conf, scmNodeManager, clusterMap, true,
                 placementMetrics));
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 e260bb3..c2978b2 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
@@ -37,6 +37,7 @@ import org.apache.hadoop.hdds.scm.container.placement.algorithms.SCMContainerPla
 import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat;
 import org.apache.hadoop.hdds.scm.exceptions.SCMException;
 import org.apache.hadoop.hdds.scm.ha.SCMContext;
+import org.apache.hadoop.hdds.scm.net.NetworkTopologyImpl;
 import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo;
 import org.apache.hadoop.hdds.scm.node.NodeStatus;
 import org.apache.hadoop.hdds.scm.node.states.NodeNotFoundException;
@@ -145,7 +146,8 @@ public class TestContainerBalancer {
         .thenReturn(new ArrayList<>(cidToInfoMap.values()));
 
     containerBalancer = new ContainerBalancer(mockNodeManager, containerManager,
-        replicationManager, conf, SCMContext.emptyContext(), placementPolicy);
+        replicationManager, conf, SCMContext.emptyContext(),
+        new NetworkTopologyImpl(conf), placementPolicy);
   }
 
   @Test
diff --git a/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestFindTargetStrategy.java b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestFindTargetStrategy.java
new file mode 100644
index 0000000..6d5a22d
--- /dev/null
+++ b/hadoop-hdds/server-scm/src/test/java/org/apache/hadoop/hdds/scm/container/balancer/TestFindTargetStrategy.java
@@ -0,0 +1,196 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.hadoop.hdds.scm.container.balancer;
+
+import org.apache.hadoop.hdds.protocol.DatanodeDetails;
+import org.apache.hadoop.hdds.protocol.MockDatanodeDetails;
+import org.apache.hadoop.hdds.scm.container.placement.metrics.SCMNodeStat;
+import org.apache.hadoop.hdds.scm.net.NetworkTopology;
+import org.apache.hadoop.hdds.scm.net.NetworkTopologyImpl;
+import org.apache.hadoop.hdds.scm.net.NodeSchema;
+import org.apache.hadoop.hdds.scm.net.NodeSchemaManager;
+import org.apache.hadoop.hdds.scm.node.DatanodeUsageInfo;
+import org.junit.Test;
+import org.junit.Assert;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.apache.hadoop.hdds.scm.net.NetConstants.LEAF_SCHEMA;
+import static org.apache.hadoop.hdds.scm.net.NetConstants.NODEGROUP_SCHEMA;
+import static org.apache.hadoop.hdds.scm.net.NetConstants.RACK_SCHEMA;
+import static org.apache.hadoop.hdds.scm.net.NetConstants.ROOT_SCHEMA;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for all the implementations of FindTargetStrategy.
+ */
+public class TestFindTargetStrategy {
+  /**
+   * Checks whether FindTargetGreedyByUsage always choose target
+   * for a given source by Usage.
+   */
+  @Test
+  public void testFindTargetGreedyByUsage() {
+    FindTargetGreedyByUsageInfo findTargetStrategyByUsageInfo =
+        new FindTargetGreedyByUsageInfo(null, null, null);
+    List<DatanodeUsageInfo> overUtilizedDatanodes = new ArrayList<>();
+
+    //create three datanodes with different usageinfo
+    DatanodeUsageInfo dui1 = new DatanodeUsageInfo(MockDatanodeDetails
+        .randomDatanodeDetails(), new SCMNodeStat(100, 0, 40));
+    DatanodeUsageInfo dui2 = new DatanodeUsageInfo(MockDatanodeDetails
+        .randomDatanodeDetails(), new SCMNodeStat(100, 0, 60));
+    DatanodeUsageInfo dui3 = new DatanodeUsageInfo(MockDatanodeDetails
+        .randomDatanodeDetails(), new SCMNodeStat(100, 0, 80));
+
+    //insert in ascending order
+    overUtilizedDatanodes.add(dui1);
+    overUtilizedDatanodes.add(dui2);
+    overUtilizedDatanodes.add(dui3);
+    findTargetStrategyByUsageInfo.reInitialize(
+        overUtilizedDatanodes, null, null);
+
+    //no need to set the datanode usage for source.
+    findTargetStrategyByUsageInfo.sortTargetForSource(
+        MockDatanodeDetails.randomDatanodeDetails());
+
+    Collection<DatanodeUsageInfo> potentialTargets =
+        findTargetStrategyByUsageInfo.getPotentialTargets();
+
+    Object[] sortedPotentialTargetArray = potentialTargets.toArray();
+
+    Assert.assertEquals(sortedPotentialTargetArray.length, 3);
+
+    //make sure after sorting target for source, the potentialTargets is
+    //sorted in descending order of usage
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[0])
+        .getDatanodeDetails(), dui3.getDatanodeDetails());
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[1])
+        .getDatanodeDetails(), dui2.getDatanodeDetails());
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[2])
+        .getDatanodeDetails(), dui1.getDatanodeDetails());
+
+  }
+
+  /**
+   * Checks whether FindTargetGreedyByNetworkTopology always choose target
+   * for a given source by network topology distance.
+   */
+  @Test
+  public void testFindTargetGreedyByNetworkTopology() {
+    // network topology with default cost
+    List<NodeSchema> schemas = new ArrayList<>();
+    schemas.add(ROOT_SCHEMA);
+    schemas.add(RACK_SCHEMA);
+    schemas.add(NODEGROUP_SCHEMA);
+    schemas.add(LEAF_SCHEMA);
+
+    NodeSchemaManager manager = NodeSchemaManager.getInstance();
+    manager.init(schemas.toArray(new NodeSchema[0]), true);
+    NetworkTopology newCluster =
+        new NetworkTopologyImpl(manager);
+
+    DatanodeDetails source =
+        MockDatanodeDetails.createDatanodeDetails("1.1.1.1", "/r1/ng1");
+    //create one target in the same rack and same node group
+    DatanodeDetails target1 =
+        MockDatanodeDetails.createDatanodeDetails("2.2.2.2", "/r1/ng1");
+    //create tree targets in the same rack but different node group
+    DatanodeDetails target2 =
+        MockDatanodeDetails.createDatanodeDetails("3.3.3.3", "/r1/ng2");
+    DatanodeDetails target3 =
+        MockDatanodeDetails.createDatanodeDetails("4.4.4.4", "/r1/ng2");
+    DatanodeDetails target4 =
+        MockDatanodeDetails.createDatanodeDetails("5.5.5.5", "/r1/ng2");
+    //create one target in different rack
+    DatanodeDetails target5 =
+        MockDatanodeDetails.createDatanodeDetails("6.6.6.6", "/r2/ng1");
+
+    //add all datanode to cluster map
+    newCluster.add(source);
+    newCluster.add(target1);
+    newCluster.add(target2);
+    newCluster.add(target3);
+    newCluster.add(target4);
+    newCluster.add(target5);
+
+    //make sure targets have different network topology distance to source
+    assertEquals(2, newCluster.getDistanceCost(source, target1));
+    assertEquals(4, newCluster.getDistanceCost(source, target2));
+    assertEquals(4, newCluster.getDistanceCost(source, target3));
+    assertEquals(4, newCluster.getDistanceCost(source, target4));
+    assertEquals(6, newCluster.getDistanceCost(source, target5));
+
+
+
+    //insert in ascending order of network topology distance
+    List<DatanodeUsageInfo> overUtilizedDatanodes = new ArrayList<>();
+    //set the farthest target with the lowest usage info
+    overUtilizedDatanodes.add(
+        new DatanodeUsageInfo(target5, new SCMNodeStat(100, 0, 90)));
+    //set the tree targets, which have the same network topology distance
+    //to source , with different usage info
+    overUtilizedDatanodes.add(
+        new DatanodeUsageInfo(target2, new SCMNodeStat(100, 0, 20)));
+    overUtilizedDatanodes.add(
+        new DatanodeUsageInfo(target3, new SCMNodeStat(100, 0, 40)));
+    overUtilizedDatanodes.add(
+        new DatanodeUsageInfo(target4, new SCMNodeStat(100, 0, 60)));
+    //set the nearest target with the highest usage info
+    overUtilizedDatanodes.add(
+        new DatanodeUsageInfo(target1, new SCMNodeStat(100, 0, 10)));
+
+
+    FindTargetGreedyByNetworkTopology findTargetGreedyByNetworkTopology =
+        new FindTargetGreedyByNetworkTopology(null, null, null, newCluster);
+
+    findTargetGreedyByNetworkTopology.reInitialize(
+        overUtilizedDatanodes, null, null);
+
+    findTargetGreedyByNetworkTopology.sortTargetForSource(source);
+
+    Collection<DatanodeUsageInfo> potentialTargets =
+        findTargetGreedyByNetworkTopology.getPotentialTargets();
+
+    Object[] sortedPotentialTargetArray = potentialTargets.toArray();
+    Assert.assertEquals(sortedPotentialTargetArray.length, 5);
+
+    // although target1 has the highest usage, it has the nearest network
+    // topology distance to source, so it should be at the head of the
+    // sorted PotentialTargetArray
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[0])
+        .getDatanodeDetails(), target1);
+
+    // these targets have same network topology distance to source,
+    // so they should be sorted by usage
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[1])
+        .getDatanodeDetails(), target4);
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[2])
+        .getDatanodeDetails(), target3);
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[3])
+        .getDatanodeDetails(), target2);
+
+    //target5 has the lowest usage , but it has the farthest distance to source
+    //so it should be at the tail of the sorted PotentialTargetArray
+    Assert.assertEquals(((DatanodeUsageInfo)sortedPotentialTargetArray[4])
+        .getDatanodeDetails(), target5);
+  }
+}

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