hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From li...@apache.org
Subject svn commit: r1550235 - in /hbase/branches/0.89-fb/src: main/java/org/apache/hadoop/hbase/master/RegionPlacement.java test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
Date Wed, 11 Dec 2013 19:18:19 GMT
Author: liyin
Date: Wed Dec 11 19:18:18 2013
New Revision: 1550235

URL: http://svn.apache.org/r1550235
Log:
[0.89-fb][HBASE-10109] Balance #regions/RS better when creating new table

Author: adela

Summary:
When a new table is created we place the primary locations for
the regions in a round robin fashion. Because we do this for every newly
created table it is possible that some regionservers end up with more
regions than the others and if there are more tables in the cluster the
difference between a min and max #regions per RS can be larger.
One small optimization is to sort the RS in ascending order before
assigning them regions.

Test Plan: wrote unit test: TestRegionPlacement.testPrimaryPlacement

Reviewers: liyintang, manukranthk, rshroff

Reviewed By: rshroff

CC: hbase-eng@

Differential Revision: https://phabricator.fb.com/D1090198

Task ID: 3339329

Modified:
    hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
    hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java

Modified: hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java?rev=1550235&r1=1550234&r2=1550235&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
(original)
+++ hbase/branches/0.89-fb/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
Wed Dec 11 19:18:18 2013
@@ -19,9 +19,22 @@
  */
 package org.apache.hadoop.hbase.master;
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonSyntaxException;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeMap;
+
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.GnuParser;
 import org.apache.commons.cli.HelpFormatter;
@@ -50,20 +63,9 @@ import org.apache.hadoop.hbase.util.Pair
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
 
-import java.io.File;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Random;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.TreeMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonSyntaxException;
 
 public class RegionPlacement implements RegionPlacementPolicy{
   private static final Log LOG = LogFactory.getLog(RegionPlacement.class
@@ -115,8 +117,7 @@ public class RegionPlacement implements 
 
     try {
       // Place the primary region server based on the regions and servers
-      Map<HRegionInfo, HServerAddress> primaryRSMap =
-        this.placePrimaryRSAsRoundRobin(regions, domain);
+      Map<HRegionInfo, HServerAddress> primaryRSMap = this.placePrimaryRSAsRoundRobinBalanced(regions,
domain);
 
       // Place the secondary and tertiary region server
       Map<HRegionInfo, Pair<HServerAddress, HServerAddress>>
@@ -135,18 +136,99 @@ public class RegionPlacement implements 
   }
 
   /**
-   * Place the primary region server in the round robin way.
+   * Place the primary regions in the round robin way. Algorithm works as
+   * following: we merge all regionservers in single map, and sort the map in
+   * ascending order by number of regions per regionserver, then start the
+   * assignment in round robin fashion.
+   *
+   * We usually have same number of regionservers per rack, so having #regions
+   * per regionserver balanced will imply balance per rack too. If that is not
+   * the case, we can expect imbalances per rack.
+   *
    * @param regions
    * @param domain
    * @return the map between regions and its primary region server
    * @throws IOException
    */
-  private Map<HRegionInfo, HServerAddress> placePrimaryRSAsRoundRobin(
+  private Map<HRegionInfo, HServerAddress> placePrimaryRSAsRoundRobinBalanced(
       HRegionInfo[] regions, AssignmentDomain domain) throws IOException {
+    RegionAssignmentSnapshot currentSnapshot = this
+        .getRegionAssignmentSnapshot();
+    Map<HServerAddress, List<HRegionInfo>> assignmentSnapshotMap = currentSnapshot
+        .getRegionServerToRegionMap();
+    Set<HServerAddress> allServers = domain.getAllServers();
+    // not all servers will be in the assignmentSnapshotMap - so putting the ones
+    // which does not have assignment
+    for (HServerAddress server : allServers) {
+      if (assignmentSnapshotMap.get(server) == null) {
+        assignmentSnapshotMap.put(server, new ArrayList<HRegionInfo>());
+      }
+    }
+    // sort the regionserver by #regions assigned in ascending order
+    ValueComparator vComp = new ValueComparator(assignmentSnapshotMap);
+    Map<HServerAddress, List<HRegionInfo>> sortedMap = new TreeMap<HServerAddress,
List<HRegionInfo>>(
+        vComp);
+    sortedMap.putAll(assignmentSnapshotMap);
+    List<HServerAddress> servers = new ArrayList<HServerAddress>(sortedMap.keySet());
+
+    Map<HRegionInfo, HServerAddress> primaryRSMap = new HashMap<HRegionInfo, HServerAddress>();
+    int index = 0;
+    for (HRegionInfo info : regions) {
+      primaryRSMap.put(info, servers.get(index++));
+      if (index == servers.size()) {
+        index = 0;
+      }
+    }
+    return primaryRSMap;
+  }
 
+  /**
+   * Comparator will sort HServerAddress based on number of regions assigned to
+   * it in ascending order
+   *
+   */
+  private class ValueComparator implements Comparator<HServerAddress> {
+    Map<HServerAddress, List<HRegionInfo>> map;
+
+    public ValueComparator(Map<HServerAddress, List<HRegionInfo>> map) {
+      this.map = map;
+    }
+
+    @Override
+    public int compare(HServerAddress server1, HServerAddress server2) {
+      if (map.get(server1) == null) {
+        return -1;
+      }
+      if (map.get(server1) != null && map.get(server2) == null) {
+        return 1;
+      } else {
+        if (map.get(server1).size() >= map.get(server2).size()) {
+          return 1;
+        } else {
+          return -1;
+        }
+      }
+    }
+  }
+
+  /**
+   * Place the primary region server in the round robin way. Algorithm works as
+   * following: per rack, per regionserserver -> place region which means the
+   * assignment will be balanced per rack *ONLY* and it is possible to be not
+   * balanced per regionserver
+   *
+   * @param regions
+   * @param domain
+   * @return the map between regions and its primary region server
+   * @throws IOException
+   */
+  @SuppressWarnings("unused")
+  private Map<HRegionInfo, HServerAddress> placePrimaryRSAsRoundRobin(
+      HRegionInfo[] regions, AssignmentDomain domain) throws IOException {
     // Get the rack to region server map from the assignment domain
-    Map<String, List<HServerAddress>> rackToRegionServerMap=
-      domain.getRackToRegionServerMap();
+    Map<String, List<HServerAddress>> rackToRegionServerMap = domain
+        .getRackToRegionServerMap();
+    //sort the map by existing #regions/rs
 
     List<String> rackList = new ArrayList<String>();
     rackList.addAll(rackToRegionServerMap.keySet());

Modified: hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java?rev=1550235&r1=1550234&r2=1550235&view=diff
==============================================================================
--- hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
(original)
+++ hbase/branches/0.89-fb/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
Wed Dec 11 19:18:18 2013
@@ -19,7 +19,23 @@
  */
 package org.apache.hadoop.hbase.master;
 
-import com.google.gson.Gson;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
@@ -46,20 +62,7 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import com.google.gson.Gson;
 
 public class TestRegionPlacement {
   final static Log LOG = LogFactory.getLog(TestRegionPlacement.class);
@@ -104,6 +107,61 @@ public class TestRegionPlacement {
     assertTrue(loadBalancer instanceof AssignmentLoadBalancer);
   }
 
+  /**
+   * Test whether the regionservers are balanced by the number of primary
+   * regions assigned. Create two tables and check whether the primaries are
+   * placed like we expected
+   *
+   * @throws Exception
+   */
+  @Test(timeout = 360000)
+  public void testPrimaryPlacement() throws Exception {
+    // Create a table with REGION_NUM regions.
+    createTable("testRegionAssignment", REGION_NUM);
+    AssignmentPlan plan = rp.getExistingAssignmentPlan();
+    Map<Integer, Integer> expected = new HashMap<Integer, Integer>();
+    // we expect 2 regionservers with 2 regions and 2 with 3 regions
+    expected.put(2, 2);
+    expected.put(3, 2);
+    assertTrue(verifyNumPrimaries(expected, plan));
+
+    //create additional table with 5 regions
+    createTable("testTable2", 5);
+    expected.clear();
+    // after this we expect 3 regionservers with 4 regions and one with 3
+    expected.put(4, 3);
+    expected.put(3, 1);
+    plan = rp.getExistingAssignmentPlan();
+    assertTrue(verifyNumPrimaries(expected, plan));
+  }
+
+
+  public boolean verifyNumPrimaries(Map<Integer, Integer> expected, AssignmentPlan
plan) {
+    Map<HServerAddress, List<HRegionInfo>> assignment = new HashMap<HServerAddress,
List<HRegionInfo>>();
+    for (Entry<HRegionInfo, List<HServerAddress>> entry : plan.getAssignmentMap().entrySet())
{
+      HServerAddress primary = entry.getValue().get(0);
+      List<HRegionInfo> regions = assignment.get(primary);
+      if (regions == null) {
+        regions = new ArrayList<HRegionInfo>();
+        assignment.put(primary, regions);
+      }
+      regions.add(entry.getKey());
+    }
+    // see how many servers are with a specific number of regions
+    Map<Integer, Integer> rswithNumRegions = new HashMap<Integer, Integer>();
+    for (Entry<HServerAddress, List<HRegionInfo>> entry : assignment.entrySet())
{
+      Integer numRegions = entry.getValue().size();
+      Integer numServers = rswithNumRegions.get(numRegions);
+      if (numServers == null) {
+        numServers = 1;
+      } else {
+        numServers++;
+      }
+      rswithNumRegions.put(numRegions, numServers);
+    }
+    return expected.equals(rswithNumRegions);
+  }
+
   @Test(timeout = 360000)
   public void testRegionPlacement() throws Exception {
     AssignmentPlan currentPlan;
@@ -121,7 +179,7 @@ public class TestRegionPlacement {
 
     // Get the assignment plan from scanning the META table
     currentPlan = rp.getExistingAssignmentPlan();
-    rp.printAssignmentPlan(currentPlan);
+    RegionPlacement.printAssignmentPlan(currentPlan);
     // Verify the plan from the META has covered all the user regions
     assertEquals(REGION_NUM, currentPlan.getAssignmentMap().keySet().size());
 
@@ -131,7 +189,7 @@ public class TestRegionPlacement {
 
     // Verify all the region server are update with the latest favored nodes
     verifyRegionServerUpdated(currentPlan);
-    rp.printAssignmentPlan(currentPlan);
+    RegionPlacement.printAssignmentPlan(currentPlan);
     // Test Case 2: To verify whether the region placement tools can
     // correctly update the new assignment plan to META and Region Server.
     // The new assignment plan is generated by shuffle the existing assignment



Mime
View raw message