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 550894314655..a975f04cfc0b 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.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 sizeEnteringNode; private NodeManager nodeManager; private ContainerBalancerConfiguration config; private Double upperLimit; - private TreeSet potentialTargets; + private Collection potentialTargets; - public FindTargetGreedy( + protected AbstractFindTargetGreedy( ContainerManager containerManager, PlacementPolicy placementPolicy, NodeManager nodeManager) { @@ -62,37 +60,36 @@ public FindTargetGreedy( 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 pt) { + potentialTargets = pt; } private void setUpperLimit(Double upperLimit){ this.upperLimit = upperLimit; } - private void setPotentialTargets( - List 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 @@ private void setConfiguration(ContainerBalancerConfiguration conf) { @Override public ContainerMoveSelection findTargetForContainerMove( DatanodeDetails source, Set candidateContainers) { + sortTargetForSource(source); for (DatanodeUsageInfo targetInfo : potentialTargets) { DatanodeDetails target = targetInfo.getDatanodeDetails(); for (ContainerID container : candidateContainers) { @@ -118,7 +116,7 @@ public ContainerMoveSelection findTargetForContainerMove( 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 ContainerMoveSelection findTargetForContainerMove( } } } - 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 @@ private boolean containerMoveSatisfiesPlacementPolicy( 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 void increaseSizeEntering(DatanodeDetails target, long size) { } 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 void reInitialize(List potentialDataNodes, 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 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 91dd1b240807..5e31bee94fc3 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.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 ContainerBalancer( ReplicationManager replicationManager, OzoneConfiguration ozoneConfiguration, final SCMContext scmContext, + NetworkTopology networkTopology, PlacementPolicy placementPolicy) { this.nodeManager = nodeManager; this.containerManager = containerManager; @@ -129,10 +133,10 @@ public 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 @@ private void balance() { 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 @@ private IterationResult doIteration() { 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 @@ private IterationResult doIteration() { 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 a0199e0c748f..698b3b0ef7aa 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; /** @@ -196,6 +203,24 @@ public void setIdleIteration(int count) { this.idleIterations = count; } + /** + * 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 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 000000000000..ad1c8de52d62 --- /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 + *

+ * 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.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 000000000000..03cdc51ba41d --- /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 + *

+ * 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.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 0db807c64778..a3a1abbbd62a 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 @@ private StorageContainerManager(OzoneConfiguration conf, 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 e260bb3487d1..c2978b20f069 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.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 void setup() throws SCMException, NodeNotFoundException { .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 000000000000..6d5a22dff10c --- /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 + *

+ * 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.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 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 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 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 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 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); + } +}