Index: src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java (revision 1189019) +++ src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java (working copy) @@ -1387,6 +1387,44 @@ } /** + * Assigns all user regions to online servers. Use round-robin assignment. + * + * @param regions + * @throws IOException + * @throws InterruptedException + */ + public void assignUserRegionsToOnlineServers(List regions) + throws IOException, InterruptedException { + List servers = this.serverManager.getOnlineServersList(); + assignUserRegions(regions, servers); + } + + /** + * Assigns all user regions, if any. Used during cluster startup. + *

+ * This is a synchronous call and will return once every region has been + * assigned. If anything fails, an exception is thrown + * + * @throws InterruptedException + * @throws IOException + */ + public void assignUserRegions(List regions, + List servers) throws IOException, InterruptedException { + if (regions == null) { + return; + } + Map> bulkPlan = null; + // Generate a round-robin bulk assignment plan + bulkPlan = LoadBalancer.roundRobinAssignment(regions, servers); + LOG.info("Bulk assigning " + regions.size() + + " region(s) round-robin across " + servers.size() + " server(s)"); + // Use fixed count thread pool assigning. + BulkAssigner ba = new StartupBulkAssigner(this.master, bulkPlan, this); + ba.bulkAssign(); + LOG.info("Bulk assigning done"); + } + + /** * Run bulk assign on startup. Does one RCP per regionserver passing a * batch of reginons using {@link SingleServerBulkAssigner}. * Uses default {@link #getUncaughtExceptionHandler()} @@ -1405,7 +1443,8 @@ } @Override - public boolean bulkAssign(boolean sync) throws InterruptedException { + public boolean bulkAssign(boolean sync) throws InterruptedException, + IOException { // Disable timing out regions in transition up in zk while bulk assigning. this.assignmentManager.timeoutMonitor.bulkAssign(true); try { Index: src/main/java/org/apache/hadoop/hbase/master/BulkAssigner.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/BulkAssigner.java (revision 1189019) +++ src/main/java/org/apache/hadoop/hbase/master/BulkAssigner.java (working copy) @@ -19,6 +19,7 @@ */ package org.apache.hadoop.hbase.master; +import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.Executors; @@ -35,7 +36,7 @@ * Server. */ public abstract class BulkAssigner { - final Server server; + protected final Server server; /** * @param server An instance of Server @@ -71,19 +72,24 @@ getLong("hbase.bulk.assignment.waiton.empty.rit", 5 * 60 * 1000); } - protected abstract void populatePool(final java.util.concurrent.ExecutorService pool); + protected abstract void populatePool( + final java.util.concurrent.ExecutorService pool) throws IOException; - public boolean bulkAssign() throws InterruptedException { + public boolean bulkAssign() throws InterruptedException, IOException { return bulkAssign(true); } /** * Run the bulk assign. - * @param sync Whether to assign synchronously. + * + * @param sync + * Whether to assign synchronously. * @throws InterruptedException * @return True if done. + * @throws IOException */ - public boolean bulkAssign(boolean sync) throws InterruptedException { + public boolean bulkAssign(boolean sync) throws InterruptedException, + IOException { boolean result = false; ThreadFactoryBuilder builder = new ThreadFactoryBuilder(); builder.setDaemon(true); Index: src/main/java/org/apache/hadoop/hbase/master/handler/EnableTableHandler.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/master/handler/EnableTableHandler.java (revision 1189019) +++ src/main/java/org/apache/hadoop/hbase/master/handler/EnableTableHandler.java (working copy) @@ -141,15 +141,29 @@ } @Override - protected void populatePool(ExecutorService pool) { - for (HRegionInfo region: regions) { - if (assignmentManager.isRegionInTransition(region) != null) continue; - final HRegionInfo hri = region; - pool.execute(new Runnable() { - public void run() { - assignmentManager.assign(hri, true); + protected void populatePool(ExecutorService pool) throws IOException { + boolean roundRobinAssignment = this.server.getConfiguration().getBoolean( + "hbase.master.enabletable.roundrobin", false); + + if (!roundRobinAssignment) { + for (HRegionInfo region : regions) { + if (assignmentManager.isRegionInTransition(region) != null) { + continue; } - }); + final HRegionInfo hri = region; + pool.execute(new Runnable() { + public void run() { + assignmentManager.assign(hri, true); + } + }); + } + } else { + try { + assignmentManager.assignUserRegionsToOnlineServers(regions); + } catch (InterruptedException e) { + LOG.warn("Assignment was interrupted"); + Thread.currentThread().interrupt(); + } } } Index: src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (revision 1189019) +++ src/test/java/org/apache/hadoop/hbase/client/TestAdmin.java (working copy) @@ -26,6 +26,8 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -47,8 +49,8 @@ import org.apache.hadoop.hbase.TableNotDisabledException; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.executor.EventHandler; +import org.apache.hadoop.hbase.executor.EventHandler.EventType; import org.apache.hadoop.hbase.executor.ExecutorService; -import org.apache.hadoop.hbase.executor.EventHandler.EventType; import org.apache.hadoop.hbase.master.AssignmentManager; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.util.Bytes; @@ -73,6 +75,8 @@ TEST_UTIL.getConfiguration().setInt("hbase.regionserver.msginterval", 100); TEST_UTIL.getConfiguration().setInt("hbase.client.pause", 250); TEST_UTIL.getConfiguration().setInt("hbase.client.retries.number", 6); + TEST_UTIL.getConfiguration().setBoolean( + "hbase.master.enabletable.roundrobin", true); TEST_UTIL.startMiniCluster(3); } @@ -125,7 +129,56 @@ assertTrue(ok); } + /** + * Test round-robin assignment on enableTable. + * + * @throws IOException + */ @Test + public void testEnableTableRoundRobinAssignment() throws IOException { + byte[] tableName = Bytes.toBytes("testEnableTableAssignment"); + + byte[][] splitKeys = { new byte[] { 1, 1, 1 }, new byte[] { 2, 2, 2 }, + new byte[] { 3, 3, 3 }, new byte[] { 4, 4, 4 }, new byte[] { 5, 5, 5 }, + new byte[] { 6, 6, 6 }, new byte[] { 7, 7, 7 }, new byte[] { 8, 8, 8 }, + new byte[] { 9, 9, 9 } }; + int expectedRegions = splitKeys.length + 1; + HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); + admin.createTable(desc, splitKeys); + HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName); + Map regions = ht.getRegionsInfo(); + assertEquals("Tried to create " + expectedRegions + " regions " + + "but only found " + regions.size(), expectedRegions, regions.size()); + // Disable table. + admin.disableTable(tableName); + // Enable table, use round-robin assignment to assign regions. + admin.enableTable(tableName); + // Check the assignment. + HTable testTable = new HTable(tableName); + Map regionMap = testTable.getRegionsInfo(); + Map serverMap = new HashMap(); + for (Map.Entry entry : regionMap.entrySet()) { + String server = entry.getValue().toString(); + Integer regioncount = serverMap.get(server); + if (regioncount == null) { + regioncount = 0; + } + regioncount++; + serverMap.put(server, regioncount); + } + List> entryList = new ArrayList>( + serverMap.entrySet()); + Collections.sort(entryList, new Comparator>() { + public int compare(Map.Entry oa, + Map.Entry ob) { + return (oa.getValue() - ob.getValue()); + } + }); + assertTrue(entryList.size() == 3); + assertTrue((entryList.get(2).getValue() - entryList.get(0).getValue()) < 2); + } + @Test public void testCreateTable() throws IOException { HTableDescriptor [] tables = admin.listTables(); int numTables = tables.length;