You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by dd...@apache.org on 2013/06/12 00:30:06 UTC
svn commit: r1491996 - in /hbase/branches/0.95/hbase-server/src:
main/java/org/apache/hadoop/hbase/master/balancer/
test/java/org/apache/hadoop/hbase/master/
test/java/org/apache/hadoop/hbase/master/balancer/
Author: ddas
Date: Tue Jun 11 22:30:05 2013
New Revision: 1491996
URL: http://svn.apache.org/r1491996
Log:
HBASE-8344. Improves the assignment when node failures happen to choose the secondary RS as the new primary RS
Modified:
hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java
hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java
hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java
hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java
Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java?rev=1491996&r1=1491995&r2=1491996&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java Tue Jun 11 22:30:05 2013
@@ -72,19 +72,19 @@ public class FavoredNodeAssignmentHelper
public final static short FAVORED_NODES_NUM = 3;
public FavoredNodeAssignmentHelper(final List<ServerName> servers, Configuration conf) {
+ this(servers, new RackManager(conf));
+ }
+
+ public FavoredNodeAssignmentHelper(final List<ServerName> servers,
+ final RackManager rackManager) {
this.servers = servers;
- this.rackManager = new RackManager(conf);
+ this.rackManager = rackManager;
this.rackToRegionServerMap = new HashMap<String, List<ServerName>>();
this.regionServerToRackMap = new HashMap<ServerName, String>();
this.uniqueRackList = new ArrayList<String>();
this.random = new Random();
}
- // For unit tests
- void setRackManager(RackManager rackManager) {
- this.rackManager = rackManager;
- }
-
/**
* Perform full scan of the meta table similar to
* {@link MetaReader#fullScan(CatalogTracker, Set, boolean)} except that this is
@@ -204,25 +204,47 @@ public class FavoredNodeAssignmentHelper
}
// Place the regions round-robin across the racks picking one server from each
- // rack at a time. For example, if 2 racks (r1 and r2) with 8 servers (s1..s8) each, it will
- // choose s1 from r1, s1 from r2, s2 from r1, s2 from r2, ...
+ // rack at a time. Start with a random rack, and a random server from every rack.
+ // If a rack doesn't have enough servers it will go to the next rack and so on.
+ // for choosing a primary.
+ // For example, if 4 racks (r1 .. r4) with 8 servers (s1..s8) each, one possible
+ // placement could be r2:s5, r3:s5, r4:s5, r1:s5, r2:s6, r3:s6..
+ // If there were fewer servers in one rack, say r3, which had 3 servers, one possible
+ // placement could be r2:s5, <skip-r3>, r4:s5, r1:s5, r2:s6, <skip-r3> ...
+ // The regions should be distributed proportionately to the racksizes
void placePrimaryRSAsRoundRobin(Map<ServerName, List<HRegionInfo>> assignmentMap,
Map<HRegionInfo, ServerName> primaryRSMap, List<HRegionInfo> regions) {
List<String> rackList = new ArrayList<String>(rackToRegionServerMap.size());
rackList.addAll(rackToRegionServerMap.keySet());
- Map<String, Integer> currentProcessIndexMap = new HashMap<String, Integer>();
- int rackIndex = 0;
+ int rackIndex = random.nextInt(rackList.size());
+ int maxRackSize = 0;
+ for (Map.Entry<String,List<ServerName>> r : rackToRegionServerMap.entrySet()) {
+ if (r.getValue().size() > maxRackSize) {
+ maxRackSize = r.getValue().size();
+ }
+ }
+ int numIterations = 0;
+ int firstServerIndex = random.nextInt(maxRackSize);
+ // Initialize the current processing host index.
+ int serverIndex = firstServerIndex;
for (HRegionInfo regionInfo : regions) {
- String rackName = rackList.get(rackIndex);
- // Initialize the current processing host index.
- int serverIndex = 0;
- // Restore the current process index from the currentProcessIndexMap
- Integer currentProcessIndex = currentProcessIndexMap.get(rackName);
- if (currentProcessIndex != null) {
- serverIndex = currentProcessIndex.intValue();
+ List<ServerName> currentServerList;
+ String rackName;
+ while (true) {
+ rackName = rackList.get(rackIndex);
+ numIterations++;
+ // Get the server list for the current rack
+ currentServerList = rackToRegionServerMap.get(rackName);
+
+ if (serverIndex >= currentServerList.size()) { //not enough machines in this rack
+ if (numIterations % rackList.size() == 0) {
+ if (++serverIndex >= maxRackSize) serverIndex = 0;
+ }
+ if ((++rackIndex) >= rackList.size()) {
+ rackIndex = 0; // reset the rack index to 0
+ }
+ } else break;
}
- // Get the server list for the current rack
- List<ServerName> currentServerList = rackToRegionServerMap.get(rackName);
// Get the current process region server
ServerName currentServer = currentServerList.get(serverIndex);
@@ -237,12 +259,9 @@ public class FavoredNodeAssignmentHelper
regionsForServer.add(regionInfo);
// Set the next processing index
- if ((++serverIndex) >= currentServerList.size()) {
- // Reset the server index for the current rack
- serverIndex = 0;
+ if (numIterations % rackList.size() == 0) {
+ ++serverIndex;
}
- // Keep track of the next processing index
- currentProcessIndexMap.put(rackName, serverIndex);
if ((++rackIndex) >= rackList.size()) {
rackIndex = 0; // reset the rack index to 0
}
Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java?rev=1491996&r1=1491995&r2=1491996&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java Tue Jun 11 22:30:05 2013
@@ -30,9 +30,13 @@ import org.apache.commons.logging.LogFac
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.ServerLoad;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.master.LoadBalancer;
+import org.apache.hadoop.hbase.master.RackManager;
import org.apache.hadoop.hbase.master.RegionPlan;
+import org.apache.hadoop.hbase.master.balancer.FavoredNodes.Position;
+import org.apache.hadoop.hbase.util.Pair;
/**
* An implementation of the {@link LoadBalancer} that assigns favored nodes for
@@ -52,12 +56,12 @@ public class FavoredNodeLoadBalancer ext
private static final Log LOG = LogFactory.getLog(FavoredNodeLoadBalancer.class);
private FavoredNodes globalFavoredNodesAssignmentPlan;
- private Configuration configuration;
+ private RackManager rackManager;
@Override
public void setConf(Configuration conf) {
- this.configuration = conf;
globalFavoredNodesAssignmentPlan = new FavoredNodes();
+ this.rackManager = new RackManager(conf);
}
@Override
@@ -76,13 +80,36 @@ public class FavoredNodeLoadBalancer ext
Map<ServerName, List<HRegionInfo>> assignmentMap;
try {
FavoredNodeAssignmentHelper assignmentHelper =
- new FavoredNodeAssignmentHelper(servers, configuration);
+ new FavoredNodeAssignmentHelper(servers, rackManager);
assignmentHelper.initialize();
if (!assignmentHelper.canPlaceFavoredNodes()) {
return super.roundRobinAssignment(regions, servers);
}
+ // Segregate the regions into two types:
+ // 1. The regions that have favored node assignment, and where at least
+ // one of the favored node is still alive. In this case, try to adhere
+ // to the current favored nodes assignment as much as possible - i.e.,
+ // if the current primary is gone, then make the secondary or tertiary
+ // as the new host for the region (based on their current load).
+ // Note that we don't change the favored
+ // node assignments here (even though one or more favored node is currently
+ // down). It is up to the balanceCluster to do this hard work. The HDFS
+ // can handle the fact that some nodes in the favored nodes hint is down
+ // It'd allocate some other DNs. In combination with stale settings for HDFS,
+ // we should be just fine.
+ // 2. The regions that currently don't have favored node assignment. We will
+ // need to come up with favored nodes assignments for them. The corner case
+ // in (1) above is that all the nodes are unavailable and in that case, we
+ // will note that this region doesn't have favored nodes.
+ Pair<Map<ServerName,List<HRegionInfo>>, List<HRegionInfo>> segregatedRegions =
+ segregateRegionsAndAssignRegionsWithFavoredNodes(regions, servers);
+ Map<ServerName,List<HRegionInfo>> regionsWithFavoredNodesMap = segregatedRegions.getFirst();
+ List<HRegionInfo> regionsWithNoFavoredNodes = segregatedRegions.getSecond();
assignmentMap = new HashMap<ServerName, List<HRegionInfo>>();
- roundRobinAssignmentImpl(assignmentHelper, assignmentMap, regions, servers);
+ roundRobinAssignmentImpl(assignmentHelper, assignmentMap, regionsWithNoFavoredNodes,
+ servers);
+ // merge the assignment maps
+ assignmentMap.putAll(regionsWithFavoredNodesMap);
} catch (Exception ex) {
LOG.warn("Encountered exception while doing favored-nodes assignment " + ex +
" Falling back to regular assignment");
@@ -95,12 +122,24 @@ public class FavoredNodeLoadBalancer ext
public ServerName randomAssignment(HRegionInfo regionInfo, List<ServerName> servers) {
try {
FavoredNodeAssignmentHelper assignmentHelper =
- new FavoredNodeAssignmentHelper(servers, configuration);
+ new FavoredNodeAssignmentHelper(servers, rackManager);
assignmentHelper.initialize();
ServerName primary = super.randomAssignment(regionInfo, servers);
if (!assignmentHelper.canPlaceFavoredNodes()) {
return primary;
}
+ List<ServerName> favoredNodes = globalFavoredNodesAssignmentPlan.getFavoredNodes(regionInfo);
+ // check if we have a favored nodes mapping for this region and if so, return
+ // a server from the favored nodes list if the passed 'servers' contains this
+ // server as well (available servers, that is)
+ if (favoredNodes != null) {
+ for (ServerName s : favoredNodes) {
+ ServerName serverWithLegitStartCode = availableServersContains(servers, s);
+ if (serverWithLegitStartCode != null) {
+ return serverWithLegitStartCode;
+ }
+ }
+ }
List<HRegionInfo> regions = new ArrayList<HRegionInfo>(1);
regions.add(regionInfo);
Map<HRegionInfo, ServerName> primaryRSMap = new HashMap<HRegionInfo, ServerName>(1);
@@ -114,6 +153,90 @@ public class FavoredNodeLoadBalancer ext
}
}
+ private Pair<Map<ServerName, List<HRegionInfo>>, List<HRegionInfo>>
+ segregateRegionsAndAssignRegionsWithFavoredNodes(List<HRegionInfo> regions,
+ List<ServerName> availableServers) {
+ Map<ServerName, List<HRegionInfo>> assignmentMapForFavoredNodes =
+ new HashMap<ServerName, List<HRegionInfo>>(regions.size() / 2);
+ List<HRegionInfo> regionsWithNoFavoredNodes = new ArrayList<HRegionInfo>(regions.size()/2);
+ for (HRegionInfo region : regions) {
+ List<ServerName> favoredNodes = globalFavoredNodesAssignmentPlan.getFavoredNodes(region);
+ ServerName primaryHost = null;
+ ServerName secondaryHost = null;
+ ServerName tertiaryHost = null;
+ if (favoredNodes != null) {
+ for (ServerName s : favoredNodes) {
+ ServerName serverWithLegitStartCode = availableServersContains(availableServers, s);
+ if (serverWithLegitStartCode != null) {
+ FavoredNodes.Position position =
+ FavoredNodes.getFavoredServerPosition(favoredNodes, s);
+ if (Position.PRIMARY.equals(position)) {
+ primaryHost = serverWithLegitStartCode;
+ } else if (Position.SECONDARY.equals(position)) {
+ secondaryHost = serverWithLegitStartCode;
+ } else if (Position.TERTIARY.equals(position)) {
+ tertiaryHost = serverWithLegitStartCode;
+ }
+ }
+ }
+ assignRegionToAvailableFavoredNode(assignmentMapForFavoredNodes, region,
+ primaryHost, secondaryHost, tertiaryHost);
+ }
+ if (primaryHost == null && secondaryHost == null && tertiaryHost == null) {
+ //all favored nodes unavailable
+ regionsWithNoFavoredNodes.add(region);
+ }
+ }
+ return new Pair<Map<ServerName, List<HRegionInfo>>, List<HRegionInfo>>(
+ assignmentMapForFavoredNodes, regionsWithNoFavoredNodes);
+ }
+
+ // Do a check of the hostname and port and return the servername from the servers list
+ // that matched (the favoredNode will have a startcode of -1 but we want the real
+ // server with the legit startcode
+ private ServerName availableServersContains(List<ServerName> servers, ServerName favoredNode) {
+ for (ServerName server : servers) {
+ if (ServerName.isSameHostnameAndPort(favoredNode, server)) {
+ return server;
+ }
+ }
+ return null;
+ }
+
+ private void assignRegionToAvailableFavoredNode(Map<ServerName,
+ List<HRegionInfo>> assignmentMapForFavoredNodes, HRegionInfo region, ServerName primaryHost,
+ ServerName secondaryHost, ServerName tertiaryHost) {
+ if (primaryHost != null) {
+ addRegionToMap(assignmentMapForFavoredNodes, region, primaryHost);
+ } else if (secondaryHost != null && tertiaryHost != null) {
+ // assign the region to the one with a lower load
+ // (both have the desired hdfs blocks)
+ ServerName s;
+ ServerLoad tertiaryLoad = super.services.getServerManager().getLoad(tertiaryHost);
+ ServerLoad secondaryLoad = super.services.getServerManager().getLoad(secondaryHost);
+ if (secondaryLoad.getLoad() < tertiaryLoad.getLoad()) {
+ s = secondaryHost;
+ } else {
+ s = tertiaryHost;
+ }
+ addRegionToMap(assignmentMapForFavoredNodes, region, s);
+ } else if (secondaryHost != null) {
+ addRegionToMap(assignmentMapForFavoredNodes, region, secondaryHost);
+ } else if (tertiaryHost != null) {
+ addRegionToMap(assignmentMapForFavoredNodes, region, tertiaryHost);
+ }
+ }
+
+ private void addRegionToMap(Map<ServerName, List<HRegionInfo>> assignmentMapForFavoredNodes,
+ HRegionInfo region, ServerName host) {
+ List<HRegionInfo> regionsOnServer = null;
+ if ((regionsOnServer = assignmentMapForFavoredNodes.get(host)) == null) {
+ regionsOnServer = new ArrayList<HRegionInfo>();
+ assignmentMapForFavoredNodes.put(host, regionsOnServer);
+ }
+ regionsOnServer.add(region);
+ }
+
public List<ServerName> getFavoredNodes(HRegionInfo regionInfo) {
return this.globalFavoredNodesAssignmentPlan.getFavoredNodes(regionInfo);
}
@@ -135,12 +258,18 @@ public class FavoredNodeLoadBalancer ext
assignmentHelper.placeSecondaryAndTertiaryRS(primaryRSMap);
// now record all the assignments so that we can serve queries later
for (HRegionInfo region : regions) {
+ // Store the favored nodes without startCode for the ServerName objects
+ // We don't care about the startcode; but only the hostname really
List<ServerName> favoredNodesForRegion = new ArrayList<ServerName>(3);
- favoredNodesForRegion.add(primaryRSMap.get(region));
+ ServerName sn = primaryRSMap.get(region);
+ favoredNodesForRegion.add(new ServerName(sn.getHostname(), sn.getPort(),
+ ServerName.NON_STARTCODE));
ServerName[] secondaryAndTertiaryNodes = secondaryAndTertiaryRSMap.get(region);
if (secondaryAndTertiaryNodes != null) {
- favoredNodesForRegion.add(secondaryAndTertiaryNodes[0]);
- favoredNodesForRegion.add(secondaryAndTertiaryNodes[1]);
+ favoredNodesForRegion.add(new ServerName(secondaryAndTertiaryNodes[0].getHostname(),
+ secondaryAndTertiaryNodes[0].getPort(), ServerName.NON_STARTCODE));
+ favoredNodesForRegion.add(new ServerName(secondaryAndTertiaryNodes[1].getHostname(),
+ secondaryAndTertiaryNodes[1].getPort(), ServerName.NON_STARTCODE));
}
globalFavoredNodesAssignmentPlan.updateFavoredNodesMap(region, favoredNodesForRegion);
}
@@ -148,6 +277,7 @@ public class FavoredNodeLoadBalancer ext
void noteFavoredNodes(final Map<HRegionInfo, ServerName[]> favoredNodesMap) {
for (Map.Entry<HRegionInfo, ServerName[]> entry : favoredNodesMap.entrySet()) {
+ // the META should already have favorednode ServerName objects without startcode
globalFavoredNodesAssignmentPlan.updateFavoredNodesMap(entry.getKey(),
Arrays.asList(entry.getValue()));
}
Modified: hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java?rev=1491996&r1=1491995&r2=1491996&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java (original)
+++ hbase/branches/0.95/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java Tue Jun 11 22:30:05 2013
@@ -73,4 +73,25 @@ public class FavoredNodes {
public synchronized List<ServerName> getFavoredNodes(HRegionInfo region) {
return favoredNodesMap.get(region);
}
+
+ /**
+ * Return the position of the server in the favoredNodes list. Assumes the
+ * favoredNodes list is of size 3.
+ * @param favoredNodes
+ * @param server
+ * @return position
+ */
+ static Position getFavoredServerPosition(
+ List<ServerName> favoredNodes, ServerName server) {
+ if (favoredNodes == null || server == null ||
+ favoredNodes.size() != FavoredNodeAssignmentHelper.FAVORED_NODES_NUM) {
+ return null;
+ }
+ for (Position p : Position.values()) {
+ if (favoredNodes.get(p.ordinal()).equals(server)) {
+ return p;
+ }
+ }
+ return null;
+ }
}
Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java?rev=1491996&r1=1491995&r2=1491996&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java Tue Jun 11 22:30:05 2013
@@ -25,9 +25,11 @@ import static org.junit.Assert.assertTru
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
@@ -62,12 +64,15 @@ import org.junit.experimental.categories
public class TestRegionPlacement {
final static Log LOG = LogFactory.getLog(TestRegionPlacement.class);
private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
- private final static int SLAVES = 4;
+ private final static int SLAVES = 10;
private static HBaseAdmin admin;
private static Position[] positions = Position.values();
private int REGION_NUM = 10;
private Map<HRegionInfo, ServerName[]> favoredNodesAssignmentPlan =
new HashMap<HRegionInfo, ServerName[]>();
+ private final static int PRIMARY = Position.PRIMARY.ordinal();
+ private final static int SECONDARY = Position.SECONDARY.ordinal();
+ private final static int TERTIARY = Position.TERTIARY.ordinal();
@BeforeClass
public static void setupBeforeClass() throws Exception {
@@ -75,6 +80,7 @@ public class TestRegionPlacement {
// Enable the favored nodes based load balancer
conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
FavoredNodeLoadBalancer.class, LoadBalancer.class);
+ conf.setBoolean("hbase.tests.use.shortcircuit.reads", false);
TEST_UTIL.startMiniCluster(SLAVES);
admin = new HBaseAdmin(conf);
}
@@ -85,27 +91,108 @@ public class TestRegionPlacement {
}
@Test
- public void testGetFavoredNodes() {
+ public void testFavoredNodesPresentForRoundRobinAssignment() {
LoadBalancer balancer = LoadBalancerFactory.getLoadBalancer(TEST_UTIL.getConfiguration());
- HRegionInfo regionInfo = new HRegionInfo("oneregion".getBytes());
+ balancer.setMasterServices(TEST_UTIL.getMiniHBaseCluster().getMaster());
List<ServerName> servers = new ArrayList<ServerName>();
- for (int i = 0; i < 10; i++) {
- ServerName server = new ServerName("foo"+i+":1234",-1);
+ for (int i = 0; i < SLAVES; i++) {
+ ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName();
servers.add(server);
}
- // test that we have enough favored nodes after we call randomAssignment
- balancer.randomAssignment(regionInfo, servers);
- assertTrue(((FavoredNodeLoadBalancer)balancer).getFavoredNodes(regionInfo).size() == 3);
- List<HRegionInfo> regions = new ArrayList<HRegionInfo>(100);
- for (int i = 0; i < 100; i++) {
- HRegionInfo region = new HRegionInfo(("foobar"+i).getBytes());
- regions.add(region);
- }
- // test that we have enough favored nodes after we call roundRobinAssignment
- balancer.roundRobinAssignment(regions, servers);
- for (int i = 0; i < 100; i++) {
- assertTrue(((FavoredNodeLoadBalancer)balancer).getFavoredNodes(regions.get(i)).size() == 3);
+ List<HRegionInfo> regions = new ArrayList<HRegionInfo>(1);
+ HRegionInfo region = new HRegionInfo(("foobar").getBytes());
+ regions.add(region);
+ Map<ServerName,List<HRegionInfo>> assignmentMap = balancer.roundRobinAssignment(regions,
+ servers);
+ Set<ServerName> serverBefore = assignmentMap.keySet();
+ List<ServerName> favoredNodesBefore =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesBefore.size() == 3);
+ // the primary RS should be the one that the balancer's assignment returns
+ assertTrue(ServerName.isSameHostnameAndPort(serverBefore.iterator().next(),
+ favoredNodesBefore.get(PRIMARY)));
+ // now remove the primary from the list of available servers
+ List<ServerName> removedServers = removeMatchingServers(serverBefore, servers);
+ // call roundRobinAssignment with the modified servers list
+ assignmentMap = balancer.roundRobinAssignment(regions, servers);
+ List<ServerName> favoredNodesAfter =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesAfter.size() == 3);
+ // We don't expect the favored nodes assignments to change in multiple calls
+ // to the roundRobinAssignment method in the balancer (relevant for AssignmentManager.assign
+ // failures)
+ assertTrue(favoredNodesAfter.containsAll(favoredNodesBefore));
+ Set<ServerName> serverAfter = assignmentMap.keySet();
+ // We expect the new RegionServer assignee to be one of the favored nodes
+ // chosen earlier.
+ assertTrue(ServerName.isSameHostnameAndPort(serverAfter.iterator().next(),
+ favoredNodesBefore.get(SECONDARY)) ||
+ ServerName.isSameHostnameAndPort(serverAfter.iterator().next(),
+ favoredNodesBefore.get(TERTIARY)));
+
+ // put back the primary in the list of available servers
+ servers.addAll(removedServers);
+ // now roundRobinAssignment with the modified servers list should return the primary
+ // as the regionserver assignee
+ assignmentMap = balancer.roundRobinAssignment(regions, servers);
+ Set<ServerName> serverWithPrimary = assignmentMap.keySet();
+ assertTrue(serverBefore.containsAll(serverWithPrimary));
+
+ // Make all the favored nodes unavailable for assignment
+ removeMatchingServers(favoredNodesAfter, servers);
+ // call roundRobinAssignment with the modified servers list
+ assignmentMap = balancer.roundRobinAssignment(regions, servers);
+ List<ServerName> favoredNodesNow =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesNow.size() == 3);
+ assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) &&
+ !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) &&
+ !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY)));
+ }
+
+ @Test
+ public void testFavoredNodesPresentForRandomAssignment() {
+ LoadBalancer balancer = LoadBalancerFactory.getLoadBalancer(TEST_UTIL.getConfiguration());
+ balancer.setMasterServices(TEST_UTIL.getMiniHBaseCluster().getMaster());
+ List<ServerName> servers = new ArrayList<ServerName>();
+ for (int i = 0; i < SLAVES; i++) {
+ ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName();
+ servers.add(server);
}
+ List<HRegionInfo> regions = new ArrayList<HRegionInfo>(1);
+ HRegionInfo region = new HRegionInfo(("foobar").getBytes());
+ regions.add(region);
+ ServerName serverBefore = balancer.randomAssignment(region, servers);
+ List<ServerName> favoredNodesBefore =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesBefore.size() == 3);
+ // the primary RS should be the one that the balancer's assignment returns
+ assertTrue(ServerName.isSameHostnameAndPort(serverBefore,favoredNodesBefore.get(PRIMARY)));
+ // now remove the primary from the list of servers
+ removeMatchingServers(serverBefore, servers);
+ // call randomAssignment with the modified servers list
+ ServerName serverAfter = balancer.randomAssignment(region, servers);
+ List<ServerName> favoredNodesAfter =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesAfter.size() == 3);
+ // We don't expect the favored nodes assignments to change in multiple calls
+ // to the randomAssignment method in the balancer (relevant for AssignmentManager.assign
+ // failures)
+ assertTrue(favoredNodesAfter.containsAll(favoredNodesBefore));
+ // We expect the new RegionServer assignee to be one of the favored nodes
+ // chosen earlier.
+ assertTrue(ServerName.isSameHostnameAndPort(serverAfter, favoredNodesBefore.get(SECONDARY)) ||
+ ServerName.isSameHostnameAndPort(serverAfter, favoredNodesBefore.get(TERTIARY)));
+ // Make all the favored nodes unavailable for assignment
+ removeMatchingServers(favoredNodesAfter, servers);
+ // call randomAssignment with the modified servers list
+ balancer.randomAssignment(region, servers);
+ List<ServerName> favoredNodesNow =
+ ((FavoredNodeLoadBalancer)balancer).getFavoredNodes(region);
+ assertTrue(favoredNodesNow.size() == 3);
+ assertTrue(!favoredNodesNow.contains(favoredNodesAfter.get(PRIMARY)) &&
+ !favoredNodesNow.contains(favoredNodesAfter.get(SECONDARY)) &&
+ !favoredNodesNow.contains(favoredNodesAfter.get(TERTIARY)));
}
@Test(timeout = 180000)
@@ -123,6 +210,27 @@ public class TestRegionPlacement {
verifyRegionServerUpdated();
}
+ private List<ServerName> removeMatchingServers(ServerName serverWithoutStartCode,
+ List<ServerName> servers) {
+ List<ServerName> serversToRemove = new ArrayList<ServerName>();
+ for (ServerName s : servers) {
+ if (ServerName.isSameHostnameAndPort(s, serverWithoutStartCode)) {
+ serversToRemove.add(s);
+ }
+ }
+ servers.removeAll(serversToRemove);
+ return serversToRemove;
+ }
+
+ private List<ServerName> removeMatchingServers(Collection<ServerName> serversWithoutStartCode,
+ List<ServerName> servers) {
+ List<ServerName> serversToRemove = new ArrayList<ServerName>();
+ for (ServerName s : serversWithoutStartCode) {
+ serversToRemove.addAll(removeMatchingServers(s, servers));
+ }
+ return serversToRemove;
+ }
+
/**
* Verify the number of user regions is assigned to the primary
* region server based on the plan is expected
@@ -204,8 +312,6 @@ public class TestRegionPlacement {
HRegionInfo info = MetaScanner.getHRegionInfo(result);
byte[] server = result.getValue(HConstants.CATALOG_FAMILY,
HConstants.SERVER_QUALIFIER);
- byte[] startCode = result.getValue(HConstants.CATALOG_FAMILY,
- HConstants.STARTCODE_QUALIFIER);
byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY,
FavoredNodeAssignmentHelper.FAVOREDNODES_QUALIFIER);
// Add the favored nodes into assignment plan
@@ -218,7 +324,7 @@ public class TestRegionPlacement {
totalRegionNum.incrementAndGet();
if (server != null) {
ServerName serverName =
- new ServerName(Bytes.toString(server),Bytes.toLong(startCode));
+ new ServerName(Bytes.toString(server), -1);
if (favoredNodes != null) {
String placement = "[NOT FAVORED NODE]";
for (int i = 0; i < favoredServerList.length; i++) {
Modified: hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java?rev=1491996&r1=1491995&r2=1491996&view=diff
==============================================================================
--- hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java (original)
+++ hbase/branches/0.95/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java Tue Jun 11 22:30:05 2013
@@ -24,10 +24,12 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HRegionInfo;
-import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.SmallTests;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.master.RackManager;
import org.apache.hadoop.hbase.util.Triple;
@@ -36,7 +38,7 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
-@Category(MediumTests.class)
+@Category(SmallTests.class)
public class TestFavoredNodeAssignmentHelper {
private static List<ServerName> servers = new ArrayList<ServerName>();
@@ -108,12 +110,29 @@ public class TestFavoredNodeAssignmentHe
public void testPlacePrimaryRSAsRoundRobin() {
// Test the regular case where there are many servers in different racks
// Test once for few regions and once for many regions
- primaryRSPlacement(6, null);
+ primaryRSPlacement(6, null, 10, 10, 10);
// now create lots of regions and try to place them on the limited number of machines
- primaryRSPlacement(600, null);
+ primaryRSPlacement(600, null, 10, 10, 10);
+ }
+
+ @Test
+ public void testRoundRobinAssignmentsWithUnevenSizedRacks() {
+ //In the case of uneven racks, the regions should be distributed
+ //proportionately to the rack sizes
+ primaryRSPlacement(6, null, 10, 10, 10);
+ primaryRSPlacement(600, null, 10, 10, 5);
+ primaryRSPlacement(600, null, 10, 5, 10);
+ primaryRSPlacement(600, null, 5, 10, 10);
+ primaryRSPlacement(500, null, 10, 10, 5);
+ primaryRSPlacement(500, null, 10, 5, 10);
+ primaryRSPlacement(500, null, 5, 10, 10);
+ primaryRSPlacement(500, null, 9, 7, 8);
+ primaryRSPlacement(500, null, 8, 7, 9);
+ primaryRSPlacement(500, null, 7, 9, 8);
+ primaryRSPlacement(459, null, 7, 9, 8);
}
- //@Test
+ @Test
public void testSecondaryAndTertiaryPlacementWithSingleRack() {
// Test the case where there is a single rack and we need to choose
// Primary/Secondary/Tertiary from a single rack.
@@ -245,10 +264,9 @@ public class TestFavoredNodeAssignmentHe
List<ServerName> servers = getServersFromRack(rackToServerCount);
FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers,
new Configuration());
- helper = new FavoredNodeAssignmentHelper(servers, new Configuration());
+ helper = new FavoredNodeAssignmentHelper(servers, rackManager);
Map<ServerName, List<HRegionInfo>> assignmentMap =
new HashMap<ServerName, List<HRegionInfo>>();
- helper.setRackManager(rackManager);
helper.initialize();
// create regions
List<HRegionInfo> regions = new ArrayList<HRegionInfo>(regionCount);
@@ -262,15 +280,15 @@ public class TestFavoredNodeAssignmentHe
(primaryRSMap, helper, regions);
}
- private void primaryRSPlacement(int regionCount, Map<HRegionInfo, ServerName> primaryRSMap) {
+ private void primaryRSPlacement(int regionCount, Map<HRegionInfo, ServerName> primaryRSMap,
+ int firstRackSize, int secondRackSize, int thirdRackSize) {
Map<String,Integer> rackToServerCount = new HashMap<String,Integer>();
- rackToServerCount.put("rack1", 10);
- rackToServerCount.put("rack2", 10);
- rackToServerCount.put("rack3", 10);
+ rackToServerCount.put("rack1", firstRackSize);
+ rackToServerCount.put("rack2", secondRackSize);
+ rackToServerCount.put("rack3", thirdRackSize);
List<ServerName> servers = getServersFromRack(rackToServerCount);
FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers,
- new Configuration());
- helper.setRackManager(rackManager);
+ rackManager);
helper.initialize();
assertTrue(helper.canPlaceFavoredNodes());
@@ -291,21 +309,51 @@ public class TestFavoredNodeAssignmentHe
int regionsOnRack1 = 0;
int regionsOnRack2 = 0;
int regionsOnRack3 = 0;
- for (Map.Entry<HRegionInfo, ServerName> entry : primaryRSMap.entrySet()) {
- if (rackManager.getRack(entry.getValue()).equals("rack1")) {
+ for (HRegionInfo region : regions) {
+ if (rackManager.getRack(primaryRSMap.get(region)).equals("rack1")) {
regionsOnRack1++;
- } else if (rackManager.getRack(entry.getValue()).equals("rack2")) {
+ } else if (rackManager.getRack(primaryRSMap.get(region)).equals("rack2")) {
regionsOnRack2++;
- } else if (rackManager.getRack(entry.getValue()).equals("rack3")) {
+ } else if (rackManager.getRack(primaryRSMap.get(region)).equals("rack3")) {
regionsOnRack3++;
}
}
- int numRegionsPerRack = (int)Math.ceil((double)regionCount/3); //since there are 3 servers
- assertTrue(regionsOnRack1 == numRegionsPerRack && regionsOnRack2 == numRegionsPerRack
- && regionsOnRack3 == numRegionsPerRack);
- int numServersPerRack = (int)Math.ceil((double)regionCount/30); //since there are 30 servers
- for (Map.Entry<ServerName, List<HRegionInfo>> entry : assignmentMap.entrySet()) {
- assertTrue(entry.getValue().size() == numServersPerRack);
- }
+ // Verify that the regions got placed in the way we expect (documented in
+ // FavoredNodeAssignmentHelper#placePrimaryRSAsRoundRobin)
+ checkNumRegions(regionCount, firstRackSize, secondRackSize, thirdRackSize, regionsOnRack1,
+ regionsOnRack2, regionsOnRack3, assignmentMap);
+ }
+
+ private void checkNumRegions(int regionCount, int firstRackSize, int secondRackSize,
+ int thirdRackSize, int regionsOnRack1, int regionsOnRack2, int regionsOnRack3,
+ Map<ServerName, List<HRegionInfo>> assignmentMap) {
+ //The regions should be distributed proportionately to the racksizes
+ //Verify the ordering was as expected by inserting the racks and regions
+ //in sorted maps. The keys being the racksize and numregions; values are
+ //the relative positions of the racksizes and numregions respectively
+ SortedMap<Integer, Integer> rackMap = new TreeMap<Integer, Integer>();
+ rackMap.put(firstRackSize, 1);
+ rackMap.put(secondRackSize, 2);
+ rackMap.put(thirdRackSize, 3);
+ SortedMap<Integer, Integer> regionMap = new TreeMap<Integer, Integer>();
+ regionMap.put(regionsOnRack1, 1);
+ regionMap.put(regionsOnRack2, 2);
+ regionMap.put(regionsOnRack3, 3);
+ assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize,
+ regionsOnRack1, regionsOnRack2, regionsOnRack3),
+ rackMap.get(firstRackSize) == regionMap.get(regionsOnRack1));
+ assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize,
+ regionsOnRack1, regionsOnRack2, regionsOnRack3),
+ rackMap.get(secondRackSize) == regionMap.get(regionsOnRack2));
+ assertTrue(printProportions(firstRackSize, secondRackSize, thirdRackSize,
+ regionsOnRack1, regionsOnRack2, regionsOnRack3),
+ rackMap.get(thirdRackSize) == regionMap.get(regionsOnRack3));
+ }
+
+ private String printProportions(int firstRackSize, int secondRackSize,
+ int thirdRackSize, int regionsOnRack1, int regionsOnRack2, int regionsOnRack3) {
+ return "The rack sizes" + firstRackSize + " " + secondRackSize
+ + " " + thirdRackSize + " " + regionsOnRack1 + " " + regionsOnRack2 +
+ " " + regionsOnRack3;
}
}