From 31536351127f62db233540ec7ff5cfc5ac05bccb Mon Sep 17 00:00:00 2001 From: Elliott Clark Date: Thu, 25 Jul 2013 10:57:40 -0700 Subject: [PATCH] Starting to simplify the balancer --- .../hadoop/hbase/master/AssignmentManager.java | 44 ++-- .../org/apache/hadoop/hbase/master/HMaster.java | 34 +-- .../apache/hadoop/hbase/master/LoadBalancer.java | 41 +--- .../apache/hadoop/hbase/master/RegionStates.java | 57 +---- .../hbase/master/balancer/BaseLoadBalancer.java | 173 +++------------ .../master/balancer/FavoredNodeLoadBalancer.java | 44 ---- .../master/balancer/StochasticLoadBalancer.java | 7 +- .../handler/DispatchMergingRegionHandler.java | 4 +- .../org/apache/hadoop/hbase/TestZooKeeper.java | 15 +- .../TestRegionObserverScannerOpenHook.java | 2 +- .../hadoop/hbase/master/TestAssignmentManager.java | 12 +- .../hadoop/hbase/master/TestRegionPlacement.java | 60 ----- .../master/balancer/TestBaseLoadBalancer.java | 231 -------------------- 13 files changed, 96 insertions(+), 628 deletions(-) delete mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index 51c0709..4e1a110 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -2401,12 +2401,8 @@ public class AssignmentManager extends ZooKeeperListener { throw new IOException("Found no destination server to assign region(s)"); } - // Reuse existing assignment info - Map> bulkPlan = - balancer.retainAssignment(regions, servers); - - assign(regions.size(), servers.size(), - "retainAssignment=true", bulkPlan); + List plans = balancer.fastBalance(regionStates.getServerHoldings(), regions.keySet()); + balance(plans); } /** @@ -2427,14 +2423,9 @@ public class AssignmentManager extends ZooKeeperListener { if (servers == null || servers.isEmpty()) { throw new IOException("Found no destination server to assign region(s)"); } - - // Generate a round-robin bulk assignment plan - Map> bulkPlan - = balancer.roundRobinAssignment(regions, servers); + List plans = balancer.fastBalance(regionStates.getServerHoldings(), regions); processFavoredNodes(regions); - - assign(regions.size(), servers.size(), - "round-robin=true", bulkPlan); + balance(plans); } private void assign(int regions, int totalServers, @@ -3139,14 +3130,27 @@ public class AssignmentManager extends ZooKeeperListener { } } - /** - * @param plan Plan to execute. - */ - public void balance(final RegionPlan plan) { - synchronized (this.regionPlans) { - this.regionPlans.put(plan.getRegionName(), plan); + public void balance(final List plans) { + if (plans == null || plans.isEmpty()) { + return; + } + + Map> newMap = new HashMap>(1); + for (RegionPlan p:plans) { + if (p.getSource() != null) { + unassign(p.getRegionInfo(), false, p.getDestination()); + } else { + if (!newMap.containsKey(p.getDestination())) { + newMap.put(p.getDestination(), new ArrayList(1)); + } + newMap.get(p.getDestination()).add(p.getRegionInfo()); + } + } + + + for(Map.Entry> entry:newMap.entrySet()) { + assign(entry.getKey(), entry.getValue()); } - unassign(plan.getRegionInfo(), false, plan.getDestination()); } public void stop() { diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index 613c5b4..ee795a3 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -25,6 +25,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -1381,38 +1382,16 @@ MasterServices, Server { } } - Map>> assignmentsByTable = - this.assignmentManager.getRegionStates().getAssignmentsByTable(); - - List plans = new ArrayList(); //Give the balancer the current cluster state. this.balancer.setClusterStatus(getClusterStatus()); - for (Map> assignments : assignmentsByTable.values()) { - List partialPlans = this.balancer.balanceCluster(assignments); - if (partialPlans != null) plans.addAll(partialPlans); - } + List plans = this.balancer.balanceCluster(assignmentManager.getRegionStates().getServerHoldings()); + this.assignmentManager.balance(plans); + long cutoffTime = System.currentTimeMillis() + maximumBalanceTime; int rpCount = 0; // number of RegionPlans balanced so far long totalRegPlanExecTime = 0; balancerRan = plans != null; - if (plans != null && !plans.isEmpty()) { - for (RegionPlan plan: plans) { - LOG.info("balance " + plan); - long balStartTime = System.currentTimeMillis(); - //TODO: bulk assign - this.assignmentManager.balance(plan); - totalRegPlanExecTime += System.currentTimeMillis()-balStartTime; - rpCount++; - if (rpCount < plans.size() && - // if performing next balance exceeds cutoff time, exit the loop - (System.currentTimeMillis() + (totalRegPlanExecTime / rpCount)) > cutoffTime) { - //TODO: After balance, there should not be a cutoff time (keeping it as a security net for now) - LOG.debug("No more balancing till next balance run; maximumBalanceTime=" + - maximumBalanceTime); - break; - } - } - } + if (this.cpHost != null) { try { this.cpHost.postBalance(rpCount < plans.size() ? plans.subList(0, rpCount) : plans); @@ -1608,7 +1587,8 @@ MasterServices, Server { } } LOG.info("Added move plan " + rp + ", running balancer"); - this.assignmentManager.balance(rp); + List plans = Arrays.asList(rp); + this.assignmentManager.balance(plans); if (this.cpHost != null) { this.cpHost.postMove(hri, rp.getSource(), rp.getDestination()); } diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java index b1427c4..7fbc199 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java @@ -18,6 +18,7 @@ */ package org.apache.hadoop.hbase.master; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -34,13 +35,6 @@ import org.apache.hadoop.hbase.ServerName; *

Cluster-wide load balancing will occur only when there are no regions in * transition and according to a fixed period of a time using {@link #balanceCluster(Map)}. * - *

Inline region placement with {@link #immediateAssignment} can be used when - * the Master needs to handle closed regions that it currently does not have - * a destination set for. This can happen during master failover. - * - *

On cluster startup, bulk assignment can be used to determine - * locations for all Regions in a cluster. - * *

This classes produces plans for the {@link AssignmentManager} to execute. */ @InterfaceAudience.Public @@ -66,38 +60,7 @@ public interface LoadBalancer extends Configurable { */ List balanceCluster(Map> clusterState); - /** - * Perform a Round Robin assignment of regions. - * @param regions - * @param servers - * @return Map of servername to regioninfos - */ - Map> roundRobinAssignment( - List regions, - List servers - ); - - /** - * Assign regions to the previously hosting region server - * @param regions - * @param servers - * @return List of plans - */ - Map> retainAssignment( - Map regions, - List servers - ); - - /** - * Sync assign a region - * @param regions - * @param servers - * @return Map regioninfos to servernames - */ - Map immediateAssignment( - List regions, - List servers - ); + List fastBalance(Map> clusterState, Collection regions); /** * Get a random region server from the list diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java index 258ea44..1c1ea1b 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionStates.java @@ -479,53 +479,20 @@ public class RegionStates { (double)totalLoad / (double)numServers; } - /** - * This is an EXPENSIVE clone. Cloning though is the safest thing to do. - * Can't let out original since it can change and at least the load balancer - * wants to iterate this exported list. We need to synchronize on regions - * since all access to this.servers is under a lock on this.regions. - * - * @return A clone of current assignments by table. - */ - protected Map>> getAssignmentsByTable() { - Map>> result = - new HashMap>>(); + protected Map> getServerHoldings() { + Map> result = + new HashMap>(serverHoldings.size()); synchronized (this) { - if (!server.getConfiguration().getBoolean("hbase.master.loadbalance.bytable", false)) { - Map> svrToRegions = - new HashMap>(serverHoldings.size()); - for (Map.Entry> e: serverHoldings.entrySet()) { - svrToRegions.put(e.getKey(), new ArrayList(e.getValue())); - } - result.put("ensemble", svrToRegions); - } else { - for (Map.Entry> e: serverHoldings.entrySet()) { - for (HRegionInfo hri: e.getValue()) { - if (hri.isMetaRegion()) continue; - String tablename = hri.getTableNameAsString(); - Map> svrToRegions = result.get(tablename); - if (svrToRegions == null) { - svrToRegions = new HashMap>(serverHoldings.size()); - result.put(tablename, svrToRegions); - } - List regions = svrToRegions.get(e.getKey()); - if (regions == null) { - regions = new ArrayList(); - svrToRegions.put(e.getKey(), regions); - } - regions.add(hri); - } - } - } - } - Map - onlineSvrs = serverManager.getOnlineServers(); - // Take care of servers w/o assignments. - for (Map> map: result.values()) { - for (ServerName svr: onlineSvrs.keySet()) { - if (!map.containsKey(svr)) { - map.put(svr, new ArrayList()); + for (Map.Entry> e : serverHoldings.entrySet()) { + result.put(e.getKey(), new ArrayList(e.getValue())); + } + Map + onlineSvrs = serverManager.getOnlineServers(); + // Take care of servers w/o assignments. + for (ServerName svr : onlineSvrs.keySet()) { + if (!result.containsKey(svr)) { + result.put(svr, new ArrayList()); } } } diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java index 0326c20..208e8b7 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.master.balancer; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -28,6 +29,7 @@ import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.NavigableMap; +import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -39,6 +41,7 @@ import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.LoadBalancer; import org.apache.hadoop.hbase.master.MasterServices; +import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.util.Bytes; import com.google.common.base.Joiner; @@ -392,75 +395,6 @@ public abstract class BaseLoadBalancer implements LoadBalancer { } /** - * Generates a bulk assignment plan to be used on cluster startup using a - * simple round-robin assignment. - *

- * Takes a list of all the regions and all the servers in the cluster and - * returns a map of each server to the regions that it should be assigned. - *

- * Currently implemented as a round-robin assignment. Same invariant as load - * balancing, all servers holding floor(avg) or ceiling(avg). - * - * TODO: Use block locations from HDFS to place regions with their blocks - * - * @param regions all regions - * @param servers all servers - * @return map of server to the regions it should take, or null if no - * assignment is possible (ie. no regions or no servers) - */ - public Map> roundRobinAssignment(List regions, - List servers) { - if (regions.isEmpty() || servers.isEmpty()) { - return null; - } - Map> assignments = new TreeMap>(); - int numRegions = regions.size(); - int numServers = servers.size(); - int max = (int) Math.ceil((float) numRegions / numServers); - int serverIdx = 0; - if (numServers > 1) { - serverIdx = RANDOM.nextInt(numServers); - } - int regionIdx = 0; - for (int j = 0; j < numServers; j++) { - ServerName server = servers.get((j + serverIdx) % numServers); - List serverRegions = new ArrayList(max); - for (int i = regionIdx; i < numRegions; i += numServers) { - serverRegions.add(regions.get(i % numRegions)); - } - assignments.put(server, serverRegions); - regionIdx++; - } - return assignments; - } - - /** - * Generates an immediate assignment plan to be used by a new master for - * regions in transition that do not have an already known destination. - * - * Takes a list of regions that need immediate assignment and a list of all - * available servers. Returns a map of regions to the server they should be - * assigned to. - * - * This method will return quickly and does not do any intelligent balancing. - * The goal is to make a fast decision not the best decision possible. - * - * Currently this is random. - * - * @param regions - * @param servers - * @return map of regions to the server it should be assigned to - */ - public Map immediateAssignment(List regions, - List servers) { - Map assignments = new TreeMap(); - for (HRegionInfo region : regions) { - assignments.put(region, randomAssignment(region, servers)); - } - return assignments; - } - - /** * Used to assign a single region to a random server. */ public ServerName randomAssignment(HRegionInfo regionInfo, List servers) { @@ -471,89 +405,40 @@ public abstract class BaseLoadBalancer implements LoadBalancer { return servers.get(RANDOM.nextInt(servers.size())); } - /** - * Generates a bulk assignment startup plan, attempting to reuse the existing - * assignment information from META, but adjusting for the specified list of - * available/online servers available for assignment. - *

- * Takes a map of all regions to their existing assignment from META. Also - * takes a list of online servers for regions to be assigned to. Attempts to - * retain all assignment, so in some instances initial assignment will not be - * completely balanced. - *

- * Any leftover regions without an existing server to be assigned to will be - * assigned randomly to available servers. - * - * @param regions regions and existing assignment from meta - * @param servers available servers - * @return map of servers and regions to be assigned to them - */ - public Map> retainAssignment(Map regions, - List servers) { - // Group all of the old assignments by their hostname. - // We can't group directly by ServerName since the servers all have - // new start-codes. - - // Group the servers by their hostname. It's possible we have multiple - // servers on the same host on different ports. - ArrayListMultimap serversByHostname = ArrayListMultimap.create(); - for (ServerName server : servers) { - serversByHostname.put(server.getHostname(), server); - } + @Override + public List fastBalance(Map> clusterState, Collection unAssignedRegions) { + ServerName[] servers = clusterState.keySet().toArray(new ServerName[clusterState.size()]); - // Now come up with new assignments - Map> assignments = new TreeMap>(); + Map initialLocation = new HashMap(servers.length); - for (ServerName server : servers) { - assignments.put(server, new ArrayList()); + for(HRegionInfo regionInfo: unAssignedRegions) { + int index = RANDOM.nextInt(servers.length); + ServerName sn = servers[index]; + clusterState.get(sn).add(regionInfo); + initialLocation.put(regionInfo, sn); } - // Collection of the hostnames that used to have regions - // assigned, but for which we no longer have any RS running - // after the cluster restart. - Set oldHostsNoLongerPresent = Sets.newTreeSet(); - - int numRandomAssignments = 0; - int numRetainedAssigments = 0; - for (Map.Entry entry : regions.entrySet()) { - HRegionInfo region = entry.getKey(); - ServerName oldServerName = entry.getValue(); - List localServers = new ArrayList(); - if (oldServerName != null) { - localServers = serversByHostname.get(oldServerName.getHostname()); - } - if (localServers.isEmpty()) { - // No servers on the new cluster match up with this hostname, - // assign randomly. - ServerName randomServer = servers.get(RANDOM.nextInt(servers.size())); - assignments.get(randomServer).add(region); - numRandomAssignments++; - if (oldServerName != null) oldHostsNoLongerPresent.add(oldServerName.getHostname()); - } else if (localServers.size() == 1) { - // the usual case - one new server on same host - assignments.get(localServers.get(0)).add(region); - numRetainedAssigments++; - } else { - // multiple new servers in the cluster on this same host - int size = localServers.size(); - ServerName target = localServers.get(RANDOM.nextInt(size)); - assignments.get(target).add(region); - numRetainedAssigments++; + List plans = balanceCluster(clusterState); + + Set unAssignedRegionsWithoutPlan = new TreeSet(unAssignedRegions); + + if (plans == null) { + plans = new ArrayList(unAssignedRegions.size()); + } + for(RegionPlan p:plans) { + if (unAssignedRegionsWithoutPlan.contains(p.getRegionInfo())) { + unAssignedRegionsWithoutPlan.remove(p.getRegionInfo()); } } - String randomAssignMsg = ""; - if (numRandomAssignments > 0) { - randomAssignMsg = - numRandomAssignments + " regions were assigned " - + "to random hosts, since the old hosts for these regions are no " - + "longer present in the cluster. These hosts were:\n " - + Joiner.on("\n ").join(oldHostsNoLongerPresent); + for (HRegionInfo needsPlan:unAssignedRegionsWithoutPlan) { + plans.add(new RegionPlan(needsPlan, null, initialLocation.get(needsPlan))); } - LOG.info("Reassigned " + regions.size() + " regions. " + numRetainedAssigments - + " retained the pre-restart assignment. " + randomAssignMsg); - return assignments; - } + if (plans.size() == 0) { + plans = null; + } + return plans; + } } diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java index c76ce44..b97afe0 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredNodeLoadBalancer.java @@ -75,50 +75,6 @@ public class FavoredNodeLoadBalancer extends BaseLoadBalancer { } @Override - public Map> roundRobinAssignment(List regions, - List servers) { - Map> assignmentMap; - try { - FavoredNodeAssignmentHelper assignmentHelper = - 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, 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"); - assignmentMap = super.roundRobinAssignment(regions, servers); - } - return assignmentMap; - } - - @Override public ServerName randomAssignment(HRegionInfo regionInfo, List servers) { try { FavoredNodeAssignmentHelper assignmentHelper = diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java index 7609e5a..1c0f29f 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java @@ -174,7 +174,6 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { this.regionFinder.setServices(masterServices); this.localityCost.setServices(masterServices); this.localityPicker.setServices(masterServices); - } /** @@ -183,9 +182,9 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { */ @Override public List balanceCluster(Map> clusterState) { - //if (!needsBalance(new ClusterLoadState(clusterState))) { - // return null; - //} + if (!needsBalance(new ClusterLoadState(clusterState))) { + return null; + } long startTime = EnvironmentEdgeManager.currentTimeMillis(); diff --git hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DispatchMergingRegionHandler.java hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DispatchMergingRegionHandler.java index 55174c6..26d329a 100644 --- hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DispatchMergingRegionHandler.java +++ hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/DispatchMergingRegionHandler.java @@ -20,6 +20,8 @@ package org.apache.hadoop.hbase.master.handler; import java.io.IOException; import java.io.InterruptedIOException; +import java.util.ArrayList; +import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -119,7 +121,7 @@ public class DispatchMergingRegionHandler extends EventHandler { RegionPlan regionPlan = new RegionPlan(region_b, region_b_location, region_a_location); LOG.info("Moving regions to same server for merge: " + regionPlan.toString()); - masterServices.getAssignmentManager().balance(regionPlan); + masterServices.getAssignmentManager().balance(Arrays.asList(regionPlan)); while (!masterServices.isStopped()) { try { Thread.sleep(20); diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java index 4095ef7..e3b94be 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/TestZooKeeper.java @@ -28,6 +28,7 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.LoadBalancer; +import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.master.balancer.DefaultLoadBalancer; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Threads; @@ -487,13 +489,13 @@ public class TestZooKeeper { ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL); ZKAssign.blockUntilNoRIT(zooKeeperWatcher); m.getZooKeeperWatcher().close(); - MockLoadBalancer.retainAssignCalled = false; + MockLoadBalancer.fastBalanceCalled = false; m.abort("Test recovery from zk session expired", new KeeperException.SessionExpiredException()); assertFalse(m.isStopped()); // The recovered master should not call retainAssignment, as it is not a // clean startup. - assertFalse("Retain assignment should not be called", MockLoadBalancer.retainAssignCalled); + assertFalse("Retain assignment should not be called", MockLoadBalancer.fastBalanceCalled); // number of listeners should be same as the value before master aborted assertEquals(expectedNumOfListeners, zkw.getNumberOfListeners()); } finally { @@ -557,13 +559,12 @@ public class TestZooKeeper { } static class MockLoadBalancer extends DefaultLoadBalancer { - static boolean retainAssignCalled = false; + static boolean fastBalanceCalled = false; @Override - public Map> retainAssignment( - Map regions, List servers) { - retainAssignCalled = true; - return super.retainAssignment(regions, servers); + public List fastBalance(Map> clusterState, Collection regions) { + fastBalanceCalled = true; + return fastBalance(clusterState, regions); } } diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverScannerOpenHook.java hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverScannerOpenHook.java index 07033a6..91c3d27 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverScannerOpenHook.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverScannerOpenHook.java @@ -63,7 +63,7 @@ import org.apache.hadoop.hbase.util.Bytes; import org.junit.Test; import org.junit.experimental.categories.Category; -@Category(SmallTests.class) +@Category(MediumTests.class) public class TestRegionObserverScannerOpenHook { private static HBaseTestingUtility UTIL = new HBaseTestingUtility(); static final Path DIR = UTIL.getDataTestDir(); diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java index cd77853..2100408 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java @@ -25,6 +25,8 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -344,7 +346,7 @@ public class TestAssignmentManager { am.regionOnline(hri, from); // Balance region from 'from' to 'to'. It calls unassign setting CLOSING state // up in zk. Create a plan and balance - am.balance(new RegionPlan(hri, from, to)); + am.balance(Arrays.asList(new RegionPlan(hri, from, to))); } /** @@ -378,7 +380,7 @@ public class TestAssignmentManager { am.regionOnline(REGIONINFO, SERVERNAME_A); // Balance region from A to B. RegionPlan plan = new RegionPlan(REGIONINFO, SERVERNAME_A, SERVERNAME_B); - am.balance(plan); + am.balance(Arrays.asList(plan)); // Must be failed to close since the server is fake assertTrue(am.getRegionStates().isRegionFailedToClose(REGIONINFO)); @@ -822,10 +824,10 @@ public class TestAssignmentManager { } @Override - public Map> retainAssignment( - Map regions, List servers) { + public List fastBalance(Map> clusterState, Collection regions) { + List plans = super.fastBalance(clusterState, regions); this.gate.set(true); - return super.retainAssignment(regions, servers); + return plans; } } diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java index 4b2d598..3eaae34 100644 --- hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java +++ hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java @@ -91,66 +91,6 @@ public class TestRegionPlacement { } @Test - public void testFavoredNodesPresentForRoundRobinAssignment() { - 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); - 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()); diff --git hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java deleted file mode 100644 index 0264f56..0000000 --- hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java +++ /dev/null @@ -1,231 +0,0 @@ -/** - * 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.hbase.master.balancer; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.HBaseConfiguration; -import org.apache.hadoop.hbase.HRegionInfo; -import org.apache.hadoop.hbase.MediumTests; -import org.apache.hadoop.hbase.ServerName; -import org.apache.hadoop.hbase.master.LoadBalancer; -import org.apache.hadoop.hbase.master.RegionPlan; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category(MediumTests.class) -public class TestBaseLoadBalancer extends BalancerTestBase { - - private static LoadBalancer loadBalancer; - private static final Log LOG = LogFactory.getLog(TestStochasticLoadBalancer.class); - - int[][] regionsAndServersMocks = new int[][] { - // { num regions, num servers } - new int[] { 0, 0 }, new int[] { 0, 1 }, new int[] { 1, 1 }, new int[] { 2, 1 }, - new int[] { 10, 1 }, new int[] { 1, 2 }, new int[] { 2, 2 }, new int[] { 3, 2 }, - new int[] { 1, 3 }, new int[] { 2, 3 }, new int[] { 3, 3 }, new int[] { 25, 3 }, - new int[] { 2, 10 }, new int[] { 2, 100 }, new int[] { 12, 10 }, new int[] { 12, 100 }, }; - - @BeforeClass - public static void beforeAllTests() throws Exception { - Configuration conf = HBaseConfiguration.create(); - loadBalancer = new MockBalancer(); - loadBalancer.setConf(conf); - } - - public static class MockBalancer extends BaseLoadBalancer { - - @Override - public List balanceCluster(Map> clusterState) { - return null; - } - - } - - /** - * Tests immediate assignment. - * - * Invariant is that all regions have an assignment. - * - * @throws Exception - */ - @Test - public void testImmediateAssignment() throws Exception { - for (int[] mock : regionsAndServersMocks) { - LOG.debug("testImmediateAssignment with " + mock[0] + " regions and " + mock[1] + " servers"); - List regions = randomRegions(mock[0]); - List servers = randomServers(mock[1], 0); - List list = getListOfServerNames(servers); - Map assignments = loadBalancer.immediateAssignment(regions, list); - assertImmediateAssignment(regions, list, assignments); - returnRegions(regions); - returnServers(list); - } - } - - /** - * All regions have an assignment. - * @param regions - * @param servers - * @param assignments - */ - private void assertImmediateAssignment(List regions, List servers, - Map assignments) { - for (HRegionInfo region : regions) { - assertTrue(assignments.containsKey(region)); - } - } - - /** - * Tests the bulk assignment used during cluster startup. - * - * Round-robin. Should yield a balanced cluster so same invariant as the load - * balancer holds, all servers holding either floor(avg) or ceiling(avg). - * - * @throws Exception - */ - @Test - public void testBulkAssignment() throws Exception { - for (int[] mock : regionsAndServersMocks) { - LOG.debug("testBulkAssignment with " + mock[0] + " regions and " + mock[1] + " servers"); - List regions = randomRegions(mock[0]); - List servers = randomServers(mock[1], 0); - List list = getListOfServerNames(servers); - Map> assignments = - loadBalancer.roundRobinAssignment(regions, list); - float average = (float) regions.size() / servers.size(); - int min = (int) Math.floor(average); - int max = (int) Math.ceil(average); - if (assignments != null && !assignments.isEmpty()) { - for (List regionList : assignments.values()) { - assertTrue(regionList.size() == min || regionList.size() == max); - } - } - returnRegions(regions); - returnServers(list); - } - } - - /** - * Test the cluster startup bulk assignment which attempts to retain - * assignment info. - * @throws Exception - */ - @Test - public void testRetainAssignment() throws Exception { - // Test simple case where all same servers are there - List servers = randomServers(10, 10); - List regions = randomRegions(100); - Map existing = new TreeMap(); - for (int i = 0; i < regions.size(); i++) { - ServerName sn = servers.get(i % servers.size()).getServerName(); - // The old server would have had same host and port, but different - // start code! - ServerName snWithOldStartCode = - new ServerName(sn.getHostname(), sn.getPort(), sn.getStartcode() - 10); - existing.put(regions.get(i), snWithOldStartCode); - } - List listOfServerNames = getListOfServerNames(servers); - Map> assignment = - loadBalancer.retainAssignment(existing, listOfServerNames); - assertRetainedAssignment(existing, listOfServerNames, assignment); - - // Include two new servers that were not there before - List servers2 = new ArrayList(servers); - servers2.add(randomServer(10)); - servers2.add(randomServer(10)); - listOfServerNames = getListOfServerNames(servers2); - assignment = loadBalancer.retainAssignment(existing, listOfServerNames); - assertRetainedAssignment(existing, listOfServerNames, assignment); - - // Remove two of the servers that were previously there - List servers3 = new ArrayList(servers); - servers3.remove(0); - servers3.remove(0); - listOfServerNames = getListOfServerNames(servers3); - assignment = loadBalancer.retainAssignment(existing, listOfServerNames); - assertRetainedAssignment(existing, listOfServerNames, assignment); - } - - private List getListOfServerNames(final List sals) { - List list = new ArrayList(); - for (ServerAndLoad e : sals) { - list.add(e.getServerName()); - } - return list; - } - - /** - * Asserts a valid retained assignment plan. - *

- * Must meet the following conditions: - *

    - *
  • Every input region has an assignment, and to an online server - *
  • If a region had an existing assignment to a server with the same - * address a a currently online server, it will be assigned to it - *
- * @param existing - * @param servers - * @param assignment - */ - private void assertRetainedAssignment(Map existing, - List servers, Map> assignment) { - // Verify condition 1, every region assigned, and to online server - Set onlineServerSet = new TreeSet(servers); - Set assignedRegions = new TreeSet(); - for (Map.Entry> a : assignment.entrySet()) { - assertTrue("Region assigned to server that was not listed as online", - onlineServerSet.contains(a.getKey())); - for (HRegionInfo r : a.getValue()) - assignedRegions.add(r); - } - assertEquals(existing.size(), assignedRegions.size()); - - // Verify condition 2, if server had existing assignment, must have same - Set onlineHostNames = new TreeSet(); - for (ServerName s : servers) { - onlineHostNames.add(s.getHostname()); - } - - for (Map.Entry> a : assignment.entrySet()) { - ServerName assignedTo = a.getKey(); - for (HRegionInfo r : a.getValue()) { - ServerName address = existing.get(r); - if (address != null && onlineHostNames.contains(address.getHostname())) { - // this region was prevously assigned somewhere, and that - // host is still around, then it should be re-assigned on the - // same host - assertEquals(address.getHostname(), assignedTo.getHostname()); - } - } - } - } - -} -- 1.7.10.2 (Apple Git-33)