You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ec...@apache.org on 2014/08/01 00:07:46 UTC
[10/50] [abbrv] git commit: [HBASE-11303] New feature: expand cluster
on specific hosts
[HBASE-11303] New feature: expand cluster on specific hosts
Summary:
we need to be able to expand a cluster onto specified hosts. Also this is a better solution then the expandRack, since the moving is done on per table basis.
- We first try to guess the new regionservers - but user can still specify them if we didn't guess correctly.
- Then we keep a priorityqueue of regionservers (sorted in descending order) by number of regions assigned.
- We take regions from these servers and move the tertiary to a new location
- RegionPlacement -u should be run in the end to complete the movement
Test Plan: will write a unit test
Reviewers: manukranthk, liyintang, aaiyer, fan, gauravm, rshroff, daviddeng
Reviewed By: daviddeng
Subscribers: elliott, hbase-eng@
Differential Revision: https://phabricator.fb.com/D1332359
Tasks: 3041849
git-svn-id: svn+ssh://tubbs/svnhive/hadoop/branches/titan/VENDOR.hbase/hbase-trunk@42718 e7acf4d4-3532-417f-9e73-7a9ae25a1f51
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/91eea3f6
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/91eea3f6
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/91eea3f6
Branch: refs/heads/0.89-fb
Commit: 91eea3f69f6ff07e493b0acaf2a256c2122c1ecd
Parents: ce2595d
Author: adela <ad...@e7acf4d4-3532-417f-9e73-7a9ae25a1f51>
Authored: Fri Jun 6 17:08:07 2014 +0000
Committer: Elliott Clark <el...@fb.com>
Committed: Thu Jul 31 14:44:22 2014 -0700
----------------------------------------------------------------------
.../hadoop/hbase/master/AssignmentDomain.java | 31 ++-
.../hbase/master/RegionAssignmentSnapshot.java | 26 ++-
.../hadoop/hbase/master/RegionPlacement.java | 226 +++++++++++++++----
.../hbase/master/RegionPlacementTestBase.java | 35 ++-
.../hbase/master/TestRegionPlacement.java | 129 ++++++++++-
5 files changed, 370 insertions(+), 77 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hbase/blob/91eea3f6/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java b/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java
index 29e97ea..5d10a53 100644
--- a/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java
+++ b/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java
@@ -30,7 +30,6 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
-import com.google.common.collect.Lists;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
@@ -38,6 +37,8 @@ import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
+import com.google.common.collect.Lists;
+
public class AssignmentDomain {
protected static final Log LOG =
LogFactory.getLog(AssignmentDomain.class.getClass());
@@ -231,4 +232,32 @@ public class AssignmentDomain {
return false;
return true;
}
+
+ /**
+ * Given list of hostnames (Set of strings) you will get list of
+ * HServerAddress. The order will be perserved i.e if you passed in the set
+ * (h1, 2, 3) you will get (HServerAddres1, 2, 3)
+ *
+ * @param hostnames
+ * - Set of String
+ * @return - List of corresponding HServerAddress
+ */
+ public List<HServerAddress> getHServerAddressFromHostname(
+ Set<String> hostnames) {
+ List<HServerAddress> serversToReturn = new ArrayList<HServerAddress>();
+ Set<HServerAddress> servers = regionServerToRackMap.keySet();
+ Map<String, HServerAddress> allServerNameToAddress = new HashMap<>();
+ for (HServerAddress address : servers) {
+ allServerNameToAddress.put(address.getHostname(), address);
+ }
+ for (String host : hostnames) {
+ HServerAddress hostAddress = allServerNameToAddress.get(host);
+ if (hostAddress == null) {
+ LOG.error("Host: " + hostAddress + " is not recognized!!!");
+ } else {
+ serversToReturn.add(hostAddress);
+ }
+ }
+ return serversToReturn;
+ }
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/91eea3f6/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java b/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java
index 9e80713..75b9df1 100644
--- a/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java
+++ b/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java
@@ -54,6 +54,7 @@ public class RegionAssignmentSnapshot {
/** the region name to region info map */
private final Map<String, HRegionInfo> regionNameToRegionInfoMap;
+ private final Map<String, Map<HServerAddress, List<HRegionInfo>>> rsToRegionsPerTable;
/** the regionServer to region map */
private final Map<HServerAddress, List<HRegionInfo>> regionServerToRegionMap;
/** the existing assignment plan in the META region */
@@ -67,6 +68,7 @@ public class RegionAssignmentSnapshot {
regionToRegionServerMap = new HashMap<HRegionInfo, HServerAddress>();
regionServerToRegionMap = new HashMap<HServerAddress, List<HRegionInfo>>();
regionNameToRegionInfoMap = new TreeMap<String, HRegionInfo>();
+ rsToRegionsPerTable = new HashMap<>();
exsitingAssignmentPlan = new AssignmentPlan();
globalAssignmentDomain = new AssignmentDomain(conf);
}
@@ -77,7 +79,7 @@ public class RegionAssignmentSnapshot {
*/
public void initialize() throws IOException {
LOG.info("Start to scan the META for the current region assignment " +
- "snappshot");
+ "snapshot");
// Add all the online region servers
HBaseAdmin admin = new HBaseAdmin(conf);
@@ -160,6 +162,20 @@ public class RegionAssignmentSnapshot {
}
regionList.add(regionInfo);
regionServerToRegionMap.put(server, regionList);
+
+ // update rsToRegionsPerTable accordingly
+ String tblName = regionInfo.getTableDesc().getNameAsString();
+ Map<HServerAddress, List<HRegionInfo>> assignment = rsToRegionsPerTable.get(tblName);
+ if (assignment== null){
+ assignment = new HashMap<>();
+ rsToRegionsPerTable.put(tblName, assignment);
+ }
+ List<HRegionInfo> regions = assignment.get(server);
+ if (regions == null) {
+ regions = new ArrayList<>();
+ assignment.put(server, regions);
+ }
+ regions.add(regionInfo);
}
}
@@ -190,4 +206,12 @@ public class RegionAssignmentSnapshot {
public Set<String> getTableSet() {
return this.tableToRegionMap.keySet();
}
+
+ /**
+ * Returns assignment regionserver->regions per table
+ *
+ */
+ public Map<String, Map<HServerAddress, List<HRegionInfo>>> getRegionServerToRegionsPerTable() {
+ return this.rsToRegionsPerTable;
+ }
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/91eea3f6/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java b/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
index b91c29e..6c7e7aa 100644
--- a/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
+++ b/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java
@@ -31,6 +31,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.PriorityQueue;
import java.util.Random;
import java.util.Scanner;
import java.util.Set;
@@ -640,6 +641,107 @@ public class RegionPlacement implements RegionPlacementPolicy{
}
/**
+ * Used to expand cluster by adding more hosts. You don't need to use this
+ * when you are adding just a few hosts, but when you add a bunch of them
+ * (let's say >5 at once)
+ *
+ * We are iterating through each table and we are moving one region at a time
+ * starting from the hosts which contain largest number of regions. Note that
+ * only the tertiary will be updated, you will still need to update the
+ * assignment plan after a few days after locality builds up.
+ *
+ * @param newHosts
+ * - new hosts where we expand the cluster
+ * @param assignmentSnapshot
+ * - current assignment snapshot
+ * @throws IOException
+ */
+ public AssignmentPlan expandRegionsToNewHosts(List<HServerAddress> newHosts,
+ RegionAssignmentSnapshot assignmentSnapshot) throws IOException {
+
+ AssignmentPlan plan = assignmentSnapshot.getExistingAssignmentPlan();
+
+ Set<String> allTables = assignmentSnapshot.getTableSet();
+ for (String t : allTables) {
+ int totalRegionsPerTable = 0;
+ PriorityQueue<Pair<HServerAddress, List<HRegionInfo>>> pqueue = new PriorityQueue<>(
+ 200, new Comparator<Pair<HServerAddress, List<HRegionInfo>>>() {
+
+ @Override
+ public int compare(Pair<HServerAddress, List<HRegionInfo>> p1,
+ Pair<HServerAddress, List<HRegionInfo>> p2) {
+ return -Integer.compare(p1.getSecond().size(), p2.getSecond()
+ .size());
+ }
+ });
+ Map<HServerAddress, List<HRegionInfo>> rsToRegionsPerTable = assignmentSnapshot
+ .getRegionServerToRegionsPerTable().get(t);
+ for (Entry<HServerAddress, List<HRegionInfo>> entry : rsToRegionsPerTable.entrySet()) {
+ System.out.println("server: " + entry.getKey() + " regions" + entry.getValue().size());
+ totalRegionsPerTable +=entry.getValue().size();
+ }
+ for (Entry<HServerAddress, List<HRegionInfo>> entry : rsToRegionsPerTable
+ .entrySet()) {
+ pqueue.add(new Pair<HServerAddress, List<HRegionInfo>>(entry.getKey(),
+ entry.getValue()));
+ }
+
+ // for each of the new machines - calculate how many regions of this table
+ // should be placed
+
+ // calculate overall avg of regions per host (including new hosts as well)
+ AssignmentDomain domain = assignmentSnapshot.getGlobalAssignmentDomain();
+ System.out.println("rs: " + domain.getAllServers().size());
+ double avgPerHosts = (double) totalRegionsPerTable
+ / domain.getAllServers().size();
+ int avgPerHostFloor = (int) Math.floor(avgPerHosts);
+ Random rand = new Random();
+ for (HServerAddress server : newHosts) {
+ int neededRegionsOnNewHost = avgPerHostFloor
+ + (rand.nextDouble() < (avgPerHosts - avgPerHostFloor) ? 1 : 0);
+ // now start taking regions from the existing hosts (they are sorted by
+ // number of regions in descending order)
+ int placedRegionOnNew = 0;
+ while (placedRegionOnNew < neededRegionsOnNewHost) {
+ Pair<HServerAddress, List<HRegionInfo>> onOld = pqueue.poll();
+ HRegionInfo regionToMove = onOld.getSecond().remove(0);
+ // now return back the modified pair in the priority queue
+ pqueue.add(onOld);
+ placedRegionOnNew++;
+ // move the tertiary of the region to the new server
+ List<HServerAddress> favoredNodes = plan.getAssignment(regionToMove);
+ AssignmentPlan.replaceFavoredNodesServerWithNew(favoredNodes,
+ AssignmentPlan.POSITION.TERTIARY, server);
+ plan.updateAssignmentPlan(regionToMove, favoredNodes);
+ }
+ System.out.println("Server: " + server + " will get "
+ + placedRegionOnNew + " tertiary assignments for table " + t);
+ }
+ }
+ return plan;
+ }
+
+ /**
+ * @param plan
+ * @throws IOException
+ */
+ private void userUpdatePlan(AssignmentPlan plan) throws IOException {
+ System.out
+ .println("Do you want to update the assignment plan with this changes (y/n): ");
+ Scanner s = new Scanner(System.in);
+ String input = s.nextLine().trim();
+ s.close();
+ if (input.toLowerCase().equals("y")) {
+ System.out.println("Updating assignment plan...");
+ updateAssignmentPlanToMeta(plan);
+ updateAssignmentPlanToRegionServers(plan);
+ } else {
+ System.out.println("exiting without updating the assignment plan");
+ }
+ }
+
+
+ /**
* This method will pick regions from a given rack, such that these regions
* are going to be moved to the new rack later. The logic behind is: we move
* the regions' tertiaries into a new rack
@@ -702,12 +804,14 @@ public class RegionPlacement implements RegionPlacementPolicy{
serverIndex++;
}
System.out.println("Total number of regions: " + totalMovedPerRack
- + " in rack " + currentRack + " will move its tertiary to a new rack: "
+ + " in rack " + currentRack + " will move its tertiary to the new : "
+ newRack);
System.out.println("------------------------------------------");
return regionsToMove;
}
+
+
/**
* Returns the average number of regions per regionserver
*
@@ -730,11 +834,15 @@ public class RegionPlacement implements RegionPlacementPolicy{
* Move the regions to the new rack, such that each server will get equal
* number of regions
*
- * @param plan - the current assignment plan
- * @param domain - the assignment domain
- * @param regionsToMove - the regions that would be moved to the new rack
- * regionserver per rack are picked to be moved in the new rack
- * @param newRack - the new rack
+ * @param plan
+ * - the current assignment plan
+ * @param domain
+ * - the assignment domain
+ * @param regionsToMove
+ * - the regions that would be moved to the new rack regionserver per
+ * rack are picked to be moved in the new rack
+ * @param newRack
+ * - the new rack
* @throws IOException
*/
private void moveRegionsToNewRack(AssignmentPlan plan,
@@ -742,7 +850,8 @@ public class RegionPlacement implements RegionPlacementPolicy{
throws IOException {
System.out.println("------------------------------------------");
System.out
- .println("Printing how many regions are planned to be assigned per region server in the new rack (" + newRack + ")");
+ .println("Printing how many regions are planned to be assigned per region server in the new rack ("
+ + newRack + ")");
List<HServerAddress> serversFromNewRack = domain
.getServersFromRack(newRack);
int totalNumRSNewRack = serversFromNewRack.size();
@@ -761,19 +870,9 @@ public class RegionPlacement implements RegionPlacementPolicy{
System.out.println("RS: " + serversFromNewRack.get(j).getHostname()
+ " got " + regionsPerRs + "tertiary regions");
}
- System.out
- .println("Do you want to update the assignment plan with this changes (y/n): ");
- Scanner s = new Scanner(System.in);
- String input = s.nextLine().trim();
- s.close();
- if (input.toLowerCase().equals("y")) {
- System.out.println("Updating assignment plan...");
- updateAssignmentPlanToMeta(plan);
- updateAssignmentPlanToRegionServers(plan);
- } else {
- System.out.println("exiting without updating the assignment plan");
- }
+ userUpdatePlan(plan);
}
+
/**
* Generate the assignment plan for the existing table
*
@@ -1657,6 +1756,7 @@ public class RegionPlacement implements RegionPlacementPolicy{
"use munkres to place secondaries and tertiaries");
opt.addOption("ld", "locality-dispersion", false, "print locality and dispersion information for current plan");
opt.addOption("exprack", "expand-with-rack", false, "expand the regions to a new rack");
+ opt.addOption("expHosts", false, "expand the regions to a new rack");
opt.addOption("rnum", false, "print number of primaries per RS");
opt.addOption("bp", "balance-primary", false, "balance the primaries across all regionservers in the cluster");
try {
@@ -1733,14 +1833,7 @@ public class RegionPlacement implements RegionPlacementPolicy{
.getRegionDegreeLocalityMappingFromFS(conf);
Map<String, Integer> movesPerTable = rp.getRegionsMovement(newPlan);
rp.checkDifferencesWithOldPlan(movesPerTable, locality, newPlan);
- System.out.println("Do you want to update the assignment plan? [y/n]");
- Scanner s = new Scanner(System.in);
- String input = s.nextLine().trim();
- if (input.equals("y")) {
- System.out.println("Updating assignment plan...");
- rp.updateAssignmentPlan(newPlan);
- }
- s.close();
+ rp.userUpdatePlan(newPlan);
}
// Read all the modes
else if (cmd.hasOption("v") || cmd.hasOption("verify")) {
@@ -1773,14 +1866,7 @@ public class RegionPlacement implements RegionPlacementPolicy{
System.out.println("Printing how will distribution of primaries look like");
rp.printDistributionOfPrimariesPerTable(rp.getRegionAssignmentSnapshot(), newPlan);
rp.printDistributionOfPrimariesPerCell(rp.getExistingAssignmentPlan(), newPlan);
- System.out.println("Do you want to update the assignment plan? [y/n]");
- Scanner s = new Scanner(System.in);
- String input = s.nextLine().trim();
- if (input.equals("y")) {
- System.out.println("Updating assignment plan...");
- rp.updateAssignmentPlan(newPlan);
- }
- s.close();
+ rp.userUpdatePlan(newPlan);
} else if (cmd.hasOption("ld")) {
Map<String, Map<String, Float>> locality = FSUtils
.getRegionDegreeLocalityMappingFromFS(conf);
@@ -1831,6 +1917,8 @@ public class RegionPlacement implements RegionPlacementPolicy{
String newRack = s.nextLine().trim();
s.close();
rp.expandRegionsToNewRack(newRack, snapshot);
+ } else if (cmd.hasOption("expHosts")) {
+ expandClusterWithNewHosts(rp);
} else if (cmd.hasOption("upload")) {
String fileName = cmd.getOptionValue("upload");
try {
@@ -1840,14 +1928,7 @@ public class RegionPlacement implements RegionPlacementPolicy{
.getRegionDegreeLocalityMappingFromFS(conf);
Map<String, Integer> movesPerTable = rp.getRegionsMovement(newPlan);
rp.checkDifferencesWithOldPlan(movesPerTable, locality, newPlan);
- System.out.println("Do you want to update the assignment plan? [y/n]");
- Scanner s = new Scanner(System.in);
- String input = s.nextLine().trim();
- if (input.equals("y")) {
- System.out.println("Updating assignment plan...");
- rp.updateAssignmentPlan(newPlan);
- }
- s.close();
+ rp.userUpdatePlan(newPlan);
} catch (JsonParseException je) {
LOG.error("Unable to parse json file", je);
} catch (IOException e) {
@@ -1874,6 +1955,65 @@ public class RegionPlacement implements RegionPlacementPolicy{
}
}
+ /**
+ * Entry-point when expanding a cluster with a number of hosts
+ *
+ * @param rp
+ * - regionPlacement object
+ * @throws IOException
+ */
+ private static void expandClusterWithNewHosts(RegionPlacement rp)
+ throws IOException {
+ RegionAssignmentSnapshot snapshot = rp.getRegionAssignmentSnapshot();
+ System.out.println("List of all hosts with #regions assigned: ");
+ Map<HServerAddress, List<HRegionInfo>> serverToRegions = snapshot
+ .getRegionServerToRegionMap();
+ // these are potential hosts - to which we want to expand
+ List<HServerAddress> emptyHosts = new ArrayList<>();
+ for (Entry<HServerAddress, List<HRegionInfo>> entry : serverToRegions
+ .entrySet()) {
+ System.out.println(entry.getKey().getHostname() + "\t:\t"
+ + entry.getValue().size());
+ if (entry.getValue().size() == 0) {
+ emptyHosts.add(entry.getKey());
+ }
+ }
+ System.out
+ .println("Guessing with which hosts you want to expand the cluster");
+ StringBuilder sb = new StringBuilder();
+ for (HServerAddress server : emptyHosts) {
+ sb.append(server.getHostname());
+ sb.append(",");
+ }
+ // remove the last comma
+ sb.deleteCharAt(sb.length() - 1);
+ System.out.println("If all the hosts are correct just press Y, otherwise "
+ + "specify the list of the hosts in one line (comma separated)");
+ System.out.println("Example:");
+ System.out.println();
+ System.out.println("hbase123.xyz, hbase456.xyz, hbase789.xyz");
+ Scanner s = new Scanner(System.in);
+ String answer = s.nextLine().trim();
+ s.close();
+ List<HServerAddress> specifiedHosts = null;
+ if (!answer.equalsIgnoreCase("Y")) {
+ Set<String> specifiedStrings = new HashSet<String>();
+ String[] hosts = answer.split(",");
+ for (String host : hosts) {
+ specifiedStrings.add(host.trim());
+ }
+ specifiedHosts = snapshot.getGlobalAssignmentDomain()
+ .getHServerAddressFromHostname(specifiedStrings);
+ if (specifiedHosts == null) {
+ System.out.println("One or more of the specified hosts were not recognized, ABORTING");
+ return;
+ }
+ }
+ AssignmentPlan newPlan = rp.expandRegionsToNewHosts(
+ specifiedHosts == null ? emptyHosts : specifiedHosts, snapshot);
+ rp.userUpdatePlan(newPlan);
+ }
+
private static void printHelp(Options opt) {
new HelpFormatter().printHelp(
"RegionPlacement < -w | -u | -n | -v | -t | -h | -overwrite -r regionName -f favoredNodes " +
http://git-wip-us.apache.org/repos/asf/hbase/blob/91eea3f6/src/test/java/org/apache/hadoop/hbase/master/RegionPlacementTestBase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/hadoop/hbase/master/RegionPlacementTestBase.java b/src/test/java/org/apache/hadoop/hbase/master/RegionPlacementTestBase.java
index 873a33f..7ef600d 100644
--- a/src/test/java/org/apache/hadoop/hbase/master/RegionPlacementTestBase.java
+++ b/src/test/java/org/apache/hadoop/hbase/master/RegionPlacementTestBase.java
@@ -19,41 +19,34 @@
*/
package org.apache.hadoop.hbase.master;
+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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MiniHBaseCluster;
import org.apache.hadoop.hbase.client.HBaseAdmin;
-import org.apache.hadoop.hbase.client.HTable;
-import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
-import org.apache.hadoop.hbase.client.ResultScanner;
-import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
-import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Writables;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-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;
-
public class RegionPlacementTestBase {
protected final static RegionMovementTestHelper TEST_UTIL = new RegionMovementTestHelper();
protected final static int META_REGION_OVERHEAD = 1;
http://git-wip-us.apache.org/repos/asf/hbase/blob/91eea3f6/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java b/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
index 60cea48..83ac5bf 100644
--- a/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
+++ b/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java
@@ -19,7 +19,21 @@
*/
package org.apache.hadoop.hbase.master;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.LargeTests;
@@ -32,21 +46,11 @@ import org.apache.hadoop.hbase.util.Bytes;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
@Category(LargeTests.class)
public class TestRegionPlacement extends RegionPlacementTestBase {
@@ -339,5 +343,108 @@ public class TestRegionPlacement extends RegionPlacementTestBase {
assertEquals("Loaded plan should be the same with current plan", currentPlan, loadedPlan);
}
+
+ /**
+ * First create a table on pinned servers. Then change the table descriptor
+ * such that the table is not pinned anymore and can be assigned on all
+ * machines in the cluster. Then we call expandOnHosts passing the empty
+ * regionserver and in the end we verify that he received avg number of
+ * tertiaries
+ *
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ @Test
+ public void testExpandOnHosts() throws IOException, InterruptedException {
+ String tableName = "testExpandHosts";
+ // let's have more region for this test case
+ REGION_NUM = 20;
+
+ TEST_UTIL.resetLastOpenedRegionCount();
+ resetLastRegionOnPrimary();
+
+ try {
+ MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
+ HTableDescriptor htd = new HTableDescriptor(tableName);
+ htd.addFamily(new HColumnDescriptor("d"));
+
+ assertTrue("number of slaves is smaller then 3", SLAVES >= 3);
+ Set<HServerAddress> servers = new HashSet<>(3);
+ HRegionServer unusedServer = cluster.getRegionServer(3);
+
+ for (int i = 0; i < 3; i++) {
+ servers.add(cluster.getRegionServer(i).getServerInfo()
+ .getServerAddress());
+ }
+
+ htd.setServers(servers);
+ admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+ admin.createTable(htd, Bytes.toBytes("aaaa"), Bytes.toBytes("zzzz"),
+ REGION_NUM);
+
+ // Wait for things to stabilize
+ TEST_UTIL.waitOnTable(tableName);
+ TEST_UTIL.waitOnStableRegionMovement();
+
+ // Reset all of the counters.
+ resetLastRegionOnPrimary();
+ TEST_UTIL.resetLastOpenedRegionCount();
+
+ verifyRegionAssignment(rp.getExistingAssignmentPlan(), 0, REGION_NUM);
+ assertPinned(tableName, cluster, servers, unusedServer);
+
+ // change the table descriptor now
+ htd.setServers(null);
+ admin.disableTable(tableName);
+ admin.modifyTable(Bytes.toBytes(tableName), htd);
+ admin.enableTable(tableName);
+
+ // Wait for things to stabilize
+ TEST_UTIL.waitOnTable(tableName);
+ TEST_UTIL.waitOnStableRegionMovement();
+
+ // Reset all of the counters.
+ resetLastRegionOnPrimary();
+ TEST_UTIL.resetLastOpenedRegionCount();
+
+ //verify there are no primaries on the last hosts from the existing table
+ HServerAddress newHost = cluster.getRegionServer(3).getServerInfo()
+ .getServerAddress();
+ Map<HRegionInfo, List<HServerAddress>> currentMap = rp.getExistingAssignmentPlan().getAssignmentMap();
+ for (List<HServerAddress> val : currentMap.values()) {
+ if (val.contains(newHost)) {
+ Assert.fail("new regionserver is contained in existing assignment");
+ }
+ }
+
+ List<HServerAddress> newHosts = new ArrayList<>();
+ newHosts.add(newHost);
+ AssignmentPlan newPlan = rp.expandRegionsToNewHosts(newHosts, rp.getRegionAssignmentSnapshot());
+ rp.updateAssignmentPlan(newPlan);
+
+ // verify there are tertiaries on the new host
+ Map<HRegionInfo, List<HServerAddress>> assignMap = newPlan.getAssignmentMap();
+ int regionsOnNewHost = 0;
+ for (List<HServerAddress> serversFromAssignment : assignMap.values()) {
+ //verify it is the tertiary
+ if (serversFromAssignment.get(AssignmentPlan.POSITION.TERTIARY.ordinal()).equals(newHost)) {
+ regionsOnNewHost++;
+ }
+ }
+ // we expect an average number of regions to move to the new host
+ assertEquals("avg number of regions to the new host: ", (int) REGION_NUM
+ / SLAVES, regionsOnNewHost);
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ } finally {
+ if (admin != null) {
+ admin.disableTable(tableName);
+ admin.deleteTable(tableName);
+ admin.close();
+ admin = null;
+ }
+ }
+ }
+
}