diff --git a/CHANGES.txt b/CHANGES.txt index 728ee48ae16..ac1b374fbb9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -56,6 +56,8 @@ Release 0.91.0 - Unreleased server.join() method (Jeff Hammerbacher via Stack) HBASE-3437 Support Explict Split Points from the Shell HBASE-3433 KeyValue API to explicitly distinguish between deep & shallow copies + HBASE-3305 Allow round-robin distribution for table created with + multiple regions (ted yu via jgray) NEW FEATURES diff --git a/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index e44b8e0b7c3..6755f31fd5f 100644 --- a/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -1182,6 +1182,28 @@ public class AssignmentManager extends ZooKeeperListener { assign(HRegionInfo.FIRST_META_REGIONINFO, true); } + /** + * Assigns list of user regions in round-robin fashion, if any exist. + *

+ * 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 BulkStartupAssigner(this.master, bulkPlan, this); + ba.bulkAssign(); + LOG.info("Bulk assigning done"); + } + /** * Assigns all user regions, if any exist. Used during cluster startup. *

@@ -1209,9 +1231,9 @@ public class AssignmentManager extends ZooKeeperListener { // Reuse existing assignment info bulkPlan = LoadBalancer.retainAssignment(allRegions, servers); } else { - // Generate a round-robin bulk assignment plan - bulkPlan = LoadBalancer.roundRobinAssignment( - new ArrayList(allRegions.keySet()), servers); + // assign regions in round-robin fashion + assignUserRegions(new ArrayList(allRegions.keySet()), servers); + return; } LOG.info("Bulk assigning " + allRegions.size() + " region(s) across " + servers.size() + " server(s), retainAssignment=" + retainAssignment); diff --git a/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index df1482ad598..e936f35ba7b 100644 --- a/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @@ -848,9 +849,15 @@ implements HMasterInterface, HMasterRegionInterface, MasterServices, Server { // 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 + List servers = serverManager.getOnlineServersList(); + try { + this.assignmentManager.assignUserRegions(Arrays.asList(newRegions), servers); + } catch (InterruptedException ie) { + LOG.error("Caught " + ie + " during round-robin assignment"); + throw new IOException(ie); } // 5. If sync, wait for assignment of regions diff --git a/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java b/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java index 15f65c60b65..3d102c1971c 100644 --- a/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java +++ b/src/main/java/org/apache/hadoop/hbase/master/LoadBalancer.java @@ -378,13 +378,18 @@ public class LoadBalancer { 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 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 { @@ -358,6 +382,8 @@ public class TestAdmin { 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 @@ -415,6 +441,8 @@ public class TestAdmin { 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 }; @@ -436,6 +464,8 @@ public class TestAdmin { 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 },