Index: src/test/java/org/apache/hadoop/hbase/TestRegionRebalancing.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/TestRegionRebalancing.java (revision 1233570) +++ src/test/java/org/apache/hadoop/hbase/TestRegionRebalancing.java (working copy) @@ -166,9 +166,16 @@ LOG.debug(server.getServerName() + " Avg: " + avg + " actual: " + serverLoad); if (!(avg > 2.0 && serverLoad <= avgLoadPlusSlop && serverLoad >= avgLoadMinusSlop)) { - LOG.debug(server.getServerName() + " Isn't balanced!!! Avg: " + avg + - " actual: " + serverLoad + " slop: " + slop); - success = false; + for (HRegionInfo hri : server.getOnlineRegions()) { + if (hri.isMetaRegion() || hri.isRootRegion()) serverLoad--; + // LOG.debug(hri.getRegionNameAsString()); + } + if (!(serverLoad <= avgLoadPlusSlop && serverLoad >= avgLoadMinusSlop)) { + LOG.debug(server.getServerName() + " Isn't balanced!!! Avg: " + avg + + " actual: " + serverLoad + " slop: " + slop); + success = false; + break; + } } } Index: src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java (revision 1233570) +++ src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java (working copy) @@ -49,6 +49,7 @@ import org.apache.hadoop.hbase.Chore; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HServerLoad; import org.apache.hadoop.hbase.NotServingRegionException; import org.apache.hadoop.hbase.Server; import org.apache.hadoop.hbase.ServerName; @@ -2774,6 +2775,55 @@ } /** + * 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 loadbalancer + * 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. + */ + Map>> getAssignmentsByTable() { + Map>> result = null; + synchronized (this.regions) { + result = new HashMap>>(); + if (!this.master.getConfiguration(). + getBoolean("hbase.master.loadbalance.bytable", true)) { + result.put("ensemble", getAssignments()); + } else { + for (Map.Entry> e: this.servers.entrySet()) { + for (HRegionInfo hri : e.getValue()) { + if (hri.isMetaRegion() || hri.isRootRegion()) continue; + String tablename = hri.getTableNameAsString(); + Map> svrToRegions = result.get(tablename); + if (svrToRegions == null) { + svrToRegions = new HashMap>(this.servers.size()); + result.put(tablename, svrToRegions); + } + List regions = null; + if (!svrToRegions.containsKey(e.getKey())) { + regions = new ArrayList(); + svrToRegions.put(e.getKey(), regions); + } else { + regions = svrToRegions.get(e.getKey()); + } + regions.add(hri); + } + } + } + } + Map onlineSvrs = this.serverManager.getOnlineServers(); + // Take care of servers w/o assignments. + for (Map> map : result.values()) { + for (Map.Entry svrEntry: onlineSvrs.entrySet()) { + if (!map.containsKey(svrEntry.getKey())) { + map.put(svrEntry.getKey(), new ArrayList()); + } + } + } + return result; + } + + /** * @return A clone of current assignments. Note, this is assignments only. * If a new server has come in and it has no regions, it will not be included * in the returned Map. Index: src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 1233570) +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java (working copy) @@ -901,16 +901,14 @@ } } - Map> assignments = - this.assignmentManager.getAssignments(); - // Returned Map from AM does not include mention of servers w/o assignments. - for (Map.Entry e: - this.serverManager.getOnlineServers().entrySet()) { - if (!assignments.containsKey(e.getKey())) { - assignments.put(e.getKey(), new ArrayList()); - } + Map>> assignmentsByTable = + this.assignmentManager.getAssignmentsByTable(); + + List plans = new ArrayList(); + for (Map> assignments : assignmentsByTable.values()) { + List partialPlans = this.balancer.balanceCluster(assignments); + if (partialPlans != null) plans.addAll(partialPlans); } - List plans = this.balancer.balanceCluster(assignments); int rpCount = 0; // number of RegionPlans balanced so far long totalRegPlanExecTime = 0; balancerRan = plans != null; Index: src/main/java/org/apache/hadoop/hbase/master/DefaultLoadBalancer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/DefaultLoadBalancer.java (revision 1234274) +++ src/main/java/org/apache/hadoop/hbase/master/DefaultLoadBalancer.java (working copy) @@ -245,7 +245,7 @@ if (serversByLoad.lastKey().getLoad() <= ceiling && serversByLoad.firstKey().getLoad() >= floor) { // Skipped because no server outside (min,max) range - LOG.info("Skipping load balancing because balanced cluster; " + + LOG.debug("Skipping load balancing because balanced cluster; " + "servers=" + numServers + " " + "regions=" + numRegions + " average=" + average + " " + "mostloaded=" + serversByLoad.lastKey().getLoad() +