Index: src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (revision 1042242) +++ src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (working copy) @@ -25,7 +25,10 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -296,6 +299,27 @@ } } + protected void verifyRoundRobinDistribution(HTable ht, int expectedRegions) throws IOException { + int numRS = ht.getCurrentNrHRS(); + Map regions = ht.getRegionsInfo(); + Map> server2Regions = new HashMap>(); + for (Map.Entry entry : regions.entrySet()) { + HServerAddress server = entry.getValue(); + List regs = server2Regions.get(server); + if (regs == null) { + regs = new ArrayList(); + server2Regions.put(server, regs); + } + regs.add(entry.getKey()); + } + float average = (float) expectedRegions/numRS; + int min = (int)Math.floor(average); + int max = (int)Math.ceil(average); + for (List regionList : server2Regions.values()) { + assertTrue(regionList.size() == min || regionList.size() == max); + } + } + @Test public void testCreateTableWithRegions() throws IOException, InterruptedException { @@ -356,7 +380,9 @@ hri = hris.next(); assertTrue(Bytes.equals(hri.getStartKey(), splitKeys[8])); assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0); - + + verifyRoundRobinDistribution(ht, expectedRegions); + // Now test using start/end with a number of regions // Use 80 bit numbers to make sure we aren't limited @@ -414,6 +440,8 @@ assertTrue(Bytes.equals(hri.getStartKey(), new byte [] {9,9,9,9,9,9,9,9,9,9})); assertTrue(hri.getEndKey() == null || hri.getEndKey().length == 0); + verifyRoundRobinDistribution(ht, expectedRegions); + // Try once more with something that divides into something infinite startKey = new byte [] { 0, 0, 0, 0, 0, 0 }; @@ -435,6 +463,8 @@ expectedRegions, regions.size()); System.err.println("Found " + regions.size() + " regions"); + verifyRoundRobinDistribution(ht, expectedRegions); + // Try an invalid case where there are duplicate split keys splitKeys = new byte [][] { new byte [] { 1, 1, 1 }, Index: src/main/java/org/apache/hadoop/hbase/master/HMaster.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/HMaster.java (revision 1042242) +++ src/main/java/org/apache/hadoop/hbase/master/HMaster.java (working copy) @@ -25,6 +25,8 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -64,6 +66,7 @@ import org.apache.hadoop.hbase.ipc.HMasterInterface; import org.apache.hadoop.hbase.ipc.HMasterRegionInterface; import org.apache.hadoop.hbase.ipc.RpcServer; +import org.apache.hadoop.hbase.master.AssignmentManager.BulkStartupAssigner; import org.apache.hadoop.hbase.master.LoadBalancer.RegionPlan; import org.apache.hadoop.hbase.master.handler.DeleteTableHandler; import org.apache.hadoop.hbase.master.handler.DisableTableHandler; @@ -721,14 +724,14 @@ this.assignmentManager.balance(rp); } } - + public void createTable(HTableDescriptor desc, byte [][] splitKeys) throws IOException { createTable(desc, splitKeys, false); } public void createTable(HTableDescriptor desc, byte [][] splitKeys, - boolean sync) + boolean sync) throws IOException { if (!isMasterRunning()) { throw new MasterNotRunningException(); @@ -761,7 +764,7 @@ } private synchronized void createTable(final HRegionInfo [] newRegions, - boolean sync) + boolean sync) throws IOException { String tableName = newRegions[0].getTableDesc().getNameAsString(); if(MetaReader.tableExists(catalogTracker, tableName)) { @@ -787,13 +790,27 @@ // 4. Close the new region to flush to disk. Close log file too. region.close(); region.getLog().closeAndDelete(); + } - // 5. Trigger immediate assignment of this region - assignmentManager.assign(region.getRegionInfo(), true); + // 5. Trigger immediate assignment of the regions in round-robin fashion + Map> bulkPlan = null; + List servers = serverManager.getOnlineServersList(); + // Generate a round-robin bulk assignment plan + bulkPlan = LoadBalancer.roundRobinAssignment( + Arrays.asList(newRegions), servers); + LOG.info("Bulk assigning " + newRegions.length + " region(s) across " + + servers.size() + " server(s)"); + // Use fixed count thread pool assigning. + BulkAssigner ba = new BulkStartupAssigner(this, bulkPlan, this.assignmentManager); + try { + ba.bulkAssign(); + } catch (InterruptedException ie) { + LOG.warn("Caught " + ie + " during round-robin assignment"); } - + LOG.info("Bulk assigning done"); + // 5. If sync, wait for assignment of regions - if(sync) { + if (sync) { LOG.debug("Waiting for " + newRegions.length + " region(s) to be " + "assigned before returning"); for(HRegionInfo regionInfo : newRegions) { Index: src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java (revision 1042242) +++ src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java (working copy) @@ -368,13 +368,18 @@ int numServers = servers.size(); int max = (int)Math.ceil((float)numRegions/numServers); int serverIdx = 0; - for(HServerInfo server : servers) { + if (numServers > 1) { + serverIdx = rand.nextInt(numServers); + } + int regionIdx = 0; + for (int j = 0; j < numServers; j++) { + HServerInfo server = servers.get((j+serverIdx) % numServers); List serverRegions = new ArrayList(max); - for(int i=serverIdx;i