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 },