diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java index e0b8153..02d073a 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeAssignmentHelper.java @@ -72,19 +72,19 @@ public class FavoredNodeAssignmentHelper { public final static short FAVORED_NODES_NUM = 3; public FavoredNodeAssignmentHelper(final List servers, Configuration conf) { + this(servers, new RackManager(conf)); + } + + public FavoredNodeAssignmentHelper(final List servers, + final RackManager rackManager) { this.servers = servers; - this.rackManager = new RackManager(conf); + this.rackManager = rackManager; this.rackToRegionServerMap = new HashMap>(); this.regionServerToRackMap = new HashMap(); this.uniqueRackList = new ArrayList(); 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,46 @@ 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, , r4:s5, r1:s5, r2:s6, ... void placePrimaryRSAsRoundRobin(Map> assignmentMap, Map primaryRSMap, List regions) { List rackList = new ArrayList(rackToRegionServerMap.size()); rackList.addAll(rackToRegionServerMap.keySet()); Map currentProcessIndexMap = new HashMap(); - int rackIndex = 0; + int rackIndex = random.nextInt(rackList.size()); + int maxRackSize = 0; + for (Map.Entry> r : rackToRegionServerMap.entrySet()) { + if (r.getValue().size() > maxRackSize) { + maxRackSize = r.getValue().size(); + } + } + int firstServerIndex = random.nextInt(maxRackSize); for (HRegionInfo regionInfo : regions) { - String rackName = rackList.get(rackIndex); + List currentServerList; + String rackName; // 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(); + int serverIndex = firstServerIndex; + while (true) { + rackName = rackList.get(rackIndex); + // Restore the current process index from the currentProcessIndexMap + Integer currentProcessIndex = currentProcessIndexMap.get(rackName); + if (currentProcessIndex != null) { + serverIndex = currentProcessIndex.intValue(); + } + // Get the server list for the current rack + currentServerList = rackToRegionServerMap.get(rackName); + if (serverIndex >= currentServerList.size()) { //not enough machines in this rack + if ((++rackIndex) >= rackList.size()) { + rackIndex = 0; // reset the rack index to 0 + } + } else break; } - // Get the server list for the current rack - List currentServerList = rackToRegionServerMap.get(rackName); // Get the current process region server ServerName currentServer = currentServerList.get(serverIndex); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java index 6309779..c76ce44 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java @@ -30,9 +30,13 @@ import org.apache.commons.logging.LogFactory; 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 extends BaseLoadBalancer { 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 extends BaseLoadBalancer { Map> 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>, List> segregatedRegions = + segregateRegionsAndAssignRegionsWithFavoredNodes(regions, servers); + Map> regionsWithFavoredNodesMap = segregatedRegions.getFirst(); + List regionsWithNoFavoredNodes = segregatedRegions.getSecond(); assignmentMap = new HashMap>(); - 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 extends BaseLoadBalancer { public ServerName randomAssignment(HRegionInfo regionInfo, List 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 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 regions = new ArrayList(1); regions.add(regionInfo); Map primaryRSMap = new HashMap(1); @@ -114,6 +153,90 @@ public class FavoredNodeLoadBalancer extends BaseLoadBalancer { } } + private Pair>, List> + segregateRegionsAndAssignRegionsWithFavoredNodes(List regions, + List availableServers) { + Map> assignmentMapForFavoredNodes = + new HashMap>(regions.size() / 2); + List regionsWithNoFavoredNodes = new ArrayList(regions.size()/2); + for (HRegionInfo region : regions) { + List 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>, List>( + 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 servers, ServerName favoredNode) { + for (ServerName server : servers) { + if (ServerName.isSameHostnameAndPort(favoredNode, server)) { + return server; + } + } + return null; + } + + private void assignRegionToAvailableFavoredNode(Map> 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> assignmentMapForFavoredNodes, + HRegionInfo region, ServerName host) { + List regionsOnServer = null; + if ((regionsOnServer = assignmentMapForFavoredNodes.get(host)) == null) { + regionsOnServer = new ArrayList(); + assignmentMapForFavoredNodes.put(host, regionsOnServer); + } + regionsOnServer.add(region); + } + public List getFavoredNodes(HRegionInfo regionInfo) { return this.globalFavoredNodesAssignmentPlan.getFavoredNodes(regionInfo); } @@ -135,12 +258,18 @@ public class FavoredNodeLoadBalancer extends BaseLoadBalancer { 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 favoredNodesForRegion = new ArrayList(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 extends BaseLoadBalancer { void noteFavoredNodes(final Map favoredNodesMap) { for (Map.Entry entry : favoredNodesMap.entrySet()) { + // the META should already have favorednode ServerName objects without startcode globalFavoredNodesAssignmentPlan.updateFavoredNodesMap(entry.getKey(), Arrays.asList(entry.getValue())); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java index b59a408..7acb863 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodes.java @@ -73,4 +73,25 @@ public class FavoredNodes { public synchronized List 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 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; + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java index 6b644b2..87894f8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java @@ -25,9 +25,11 @@ import static org.junit.Assert.assertTrue; 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.Category; 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 favoredNodesAssignmentPlan = new HashMap(); + 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 { @@ -85,27 +90,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 servers = new ArrayList(); - 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 regions = new ArrayList(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 regions = new ArrayList(1); + HRegionInfo region = new HRegionInfo(("foobar").getBytes()); + regions.add(region); + Map> assignmentMap = balancer.roundRobinAssignment(regions, + servers); + Set serverBefore = assignmentMap.keySet(); + List 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 removedServers = removeMatchingServers(serverBefore, servers); + // call roundRobinAssignment with the modified servers list + assignmentMap = balancer.roundRobinAssignment(regions, servers); + List 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 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 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 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 servers = new ArrayList(); + for (int i = 0; i < SLAVES; i++) { + ServerName server = TEST_UTIL.getMiniHBaseCluster().getRegionServer(i).getServerName(); + servers.add(server); } + List regions = new ArrayList(1); + HRegionInfo region = new HRegionInfo(("foobar").getBytes()); + regions.add(region); + ServerName serverBefore = balancer.randomAssignment(region, servers); + List 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 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 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 +209,27 @@ public class TestRegionPlacement { verifyRegionServerUpdated(); } + private List removeMatchingServers(ServerName serverWithoutStartCode, + List servers) { + List serversToRemove = new ArrayList(); + for (ServerName s : servers) { + if (ServerName.isSameHostnameAndPort(s, serverWithoutStartCode)) { + serversToRemove.add(s); + } + } + servers.removeAll(serversToRemove); + return serversToRemove; + } + + private List removeMatchingServers(Collection serversWithoutStartCode, + List servers) { + List serversToRemove = new ArrayList(); + 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 +311,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 +323,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++) { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java index 843331c..62f1446 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestFavoredNodeAssignmentHelper.java @@ -113,7 +113,7 @@ public class TestFavoredNodeAssignmentHelper { primaryRSPlacement(600, null); } - //@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 +245,9 @@ public class TestFavoredNodeAssignmentHelper { List servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, new Configuration()); - helper = new FavoredNodeAssignmentHelper(servers, new Configuration()); + helper = new FavoredNodeAssignmentHelper(servers, rackManager); Map> assignmentMap = new HashMap>(); - helper.setRackManager(rackManager); helper.initialize(); // create regions List regions = new ArrayList(regionCount); @@ -269,8 +268,7 @@ public class TestFavoredNodeAssignmentHelper { rackToServerCount.put("rack3", 10); List servers = getServersFromRack(rackToServerCount); FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, - new Configuration()); - helper.setRackManager(rackManager); + rackManager); helper.initialize(); assertTrue(helper.canPlaceFavoredNodes()); @@ -291,14 +289,46 @@ public class TestFavoredNodeAssignmentHelper { int regionsOnRack1 = 0; int regionsOnRack2 = 0; int regionsOnRack3 = 0; - for (Map.Entry entry : primaryRSMap.entrySet()) { - if (rackManager.getRack(entry.getValue()).equals("rack1")) { + String prevRack = null; + int prevIdx = 0; + 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++; } + // Verify that the regions got placed in the way we expect (documented in + // FavoredNodeAssignmentHelper#placePrimaryRSAsRoundRobin) + String host = primaryRSMap.get(region).getHostname(); + // we make use the number that is present in the hostnames. We created + // hostnames with increasing numbers. Since there are 10 machines in each + // rack (as per the #setupBeforeClass method of this test), the machines in + // the racks would be foo0-9, foo10-19, foo20-29 respectively. + int idx = Integer.parseInt(host.substring(host.indexOf("foo") + + "foo".length())) % 10; + if (prevRack == null) { + prevRack = rackManager.getRack(primaryRSMap.get(region)); + prevIdx = idx; + } else { + String currRack = rackManager.getRack(primaryRSMap.get(region)); + assertTrue(!prevRack.equals(currRack)); + assertTrue(prevIdx == idx); + prevRack = currRack; + //after every 3rd iteration of the loop, we will see the idx incremented by + //1 (the placement policy is round robin as described in the + //FavoredNodeAssignmentHelper#placePrimaryRSAsRoundRobin, and in this + //case we have 3 racks + //set prevIdx so that it is ready for the next iteration + if ((regionsOnRack1 + regionsOnRack2 + regionsOnRack3) % 3 == 0) { + if (idx == 9) prevIdx = 0; //we have 10 hosts per rack (wrap around) + else prevIdx = idx + 1; + } else { + prevIdx = idx; + } + } } int numRegionsPerRack = (int)Math.ceil((double)regionCount/3); //since there are 3 servers assertTrue(regionsOnRack1 == numRegionsPerRack && regionsOnRack2 == numRegionsPerRack