HBASE-10351 LoadBalancer changes for supporting region replicas
git-svn-id: https://svn.apache.org/repos/asf/hbase/branches/hbase-10070@1572298 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
87b2b923e2
commit
a98f52953a
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.io.InterruptedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -3916,7 +3917,8 @@ public class AssignmentManager extends ZooKeeperListener {
|
|||
return this.balancer;
|
||||
}
|
||||
|
||||
public Map<ServerName, List<HRegionInfo>> getSnapShotOfAssignment(List<HRegionInfo> infos) {
|
||||
public Map<ServerName, List<HRegionInfo>>
|
||||
getSnapShotOfAssignment(Collection<HRegionInfo> infos) {
|
||||
return getRegionStates().getRegionAssignments(infos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ public class RackManager {
|
|||
|
||||
private DNSToSwitchMapping switchMapping;
|
||||
|
||||
public RackManager() {
|
||||
}
|
||||
|
||||
public RackManager(Configuration conf) {
|
||||
switchMapping = ReflectionUtils.instantiateWithCustomCtor(
|
||||
conf.getClass("hbase.util.ip.to.rack.determiner", ScriptBasedMapping.class,
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.master;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -153,7 +154,8 @@ public class RegionStates {
|
|||
* @param regions
|
||||
* @return a pair containing the groupings as a map
|
||||
*/
|
||||
synchronized Map<ServerName, List<HRegionInfo>> getRegionAssignments(List<HRegionInfo> regions) {
|
||||
synchronized Map<ServerName, List<HRegionInfo>> getRegionAssignments(
|
||||
Collection<HRegionInfo> regions) {
|
||||
Map<ServerName, List<HRegionInfo>> map = new HashMap<ServerName, List<HRegionInfo>>();
|
||||
for (HRegionInfo region : regions) {
|
||||
HRegionInfo defaultReplica = RegionReplicaUtil.getRegionInfoForDefaultReplica(region);
|
||||
|
@ -900,6 +902,19 @@ public class RegionStates {
|
|||
return getRegionState(hri.getEncodedName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of region assignments per server
|
||||
* @return a Map of ServerName to a List of HRegionInfo's
|
||||
*/
|
||||
protected synchronized Map<ServerName, List<HRegionInfo>> getRegionAssignmentsByServer() {
|
||||
Map<ServerName, List<HRegionInfo>> regionsByServer =
|
||||
new HashMap<ServerName, List<HRegionInfo>>(serverHoldings.size());
|
||||
for (Map.Entry<ServerName, Set<HRegionInfo>> e: serverHoldings.entrySet()) {
|
||||
regionsByServer.put(e.getKey(), new ArrayList<HRegionInfo>(e.getValue()));
|
||||
}
|
||||
return regionsByServer;
|
||||
}
|
||||
|
||||
protected synchronized RegionState getRegionState(final String encodedName) {
|
||||
return regionStates.get(encodedName);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -62,6 +62,7 @@ public class FavoredNodeLoadBalancer extends BaseLoadBalancer {
|
|||
|
||||
@Override
|
||||
public void setConf(Configuration conf) {
|
||||
super.setConf(conf);
|
||||
globalFavoredNodesAssignmentPlan = new FavoredNodesPlan();
|
||||
this.rackManager = new RackManager(conf);
|
||||
super.setConf(conf);
|
||||
|
|
|
@ -147,7 +147,7 @@ class RegionLocationFinder {
|
|||
protected HTableDescriptor getTableDescriptor(TableName tableName) throws IOException {
|
||||
HTableDescriptor tableDescriptor = null;
|
||||
try {
|
||||
if (this.services != null) {
|
||||
if (this.services != null && this.services.getTableDescriptors() != null) {
|
||||
tableDescriptor = this.services.getTableDescriptors().get(tableName);
|
||||
}
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.hadoop.hbase.master.balancer;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
|
@ -37,11 +38,16 @@ import org.apache.hadoop.hbase.HRegionInfo;
|
|||
import org.apache.hadoop.hbase.RegionLoad;
|
||||
import org.apache.hadoop.hbase.ServerLoad;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
|
||||
import org.apache.hadoop.hbase.master.MasterServices;
|
||||
import org.apache.hadoop.hbase.master.RegionPlan;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action.Type;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.AssignRegionAction;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.MoveRegionAction;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.SwapRegionsAction;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
|
||||
import org.apache.hadoop.hbase.util.Pair;
|
||||
|
||||
/**
|
||||
* <p>This is a best effort load balancer. Given a Cost function F(C) => x It will
|
||||
|
@ -89,19 +95,18 @@ import org.apache.hadoop.hbase.util.Pair;
|
|||
@InterfaceAudience.Private
|
||||
public class StochasticLoadBalancer extends BaseLoadBalancer {
|
||||
|
||||
private static final String STEPS_PER_REGION_KEY =
|
||||
protected static final String STEPS_PER_REGION_KEY =
|
||||
"hbase.master.balancer.stochastic.stepsPerRegion";
|
||||
private static final String MAX_STEPS_KEY =
|
||||
protected static final String MAX_STEPS_KEY =
|
||||
"hbase.master.balancer.stochastic.maxSteps";
|
||||
private static final String MAX_RUNNING_TIME_KEY =
|
||||
protected static final String MAX_RUNNING_TIME_KEY =
|
||||
"hbase.master.balancer.stochastic.maxRunningTime";
|
||||
private static final String KEEP_REGION_LOADS =
|
||||
protected static final String KEEP_REGION_LOADS =
|
||||
"hbase.master.balancer.stochastic.numRegionLoadsToRemember";
|
||||
|
||||
private static final Random RANDOM = new Random(System.currentTimeMillis());
|
||||
private static final Log LOG = LogFactory.getLog(StochasticLoadBalancer.class);
|
||||
|
||||
private final RegionLocationFinder regionFinder = new RegionLocationFinder();
|
||||
Map<String, Deque<RegionLoad>> loads = new HashMap<String, Deque<RegionLoad>>();
|
||||
|
||||
// values are defaults
|
||||
|
@ -110,20 +115,18 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
private long maxRunningTime = 30 * 1000 * 1; // 30 seconds.
|
||||
private int numRegionLoadsToRemember = 15;
|
||||
|
||||
private RegionPicker[] pickers;
|
||||
private CandidateGenerator[] candidateGenerators;
|
||||
private CostFromRegionLoadFunction[] regionLoadFunctions;
|
||||
private CostFunction[] costFunctions;
|
||||
// Keep locality based picker and cost function to alert them
|
||||
// when new services are offered
|
||||
private LocalityBasedPicker localityPicker;
|
||||
private LocalityBasedCandidateGenerator localityCandidateGenerator;
|
||||
private LocalityCostFunction localityCost;
|
||||
|
||||
@Override
|
||||
public void setConf(Configuration conf) {
|
||||
super.setConf(conf);
|
||||
|
||||
regionFinder.setConf(conf);
|
||||
|
||||
maxSteps = conf.getInt(MAX_STEPS_KEY, maxSteps);
|
||||
|
||||
stepsPerRegion = conf.getInt(STEPS_PER_REGION_KEY, stepsPerRegion);
|
||||
|
@ -131,13 +134,14 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
|
||||
numRegionLoadsToRemember = conf.getInt(KEEP_REGION_LOADS, numRegionLoadsToRemember);
|
||||
|
||||
localityPicker = new LocalityBasedPicker(services);
|
||||
localityCandidateGenerator = new LocalityBasedCandidateGenerator(services);
|
||||
localityCost = new LocalityCostFunction(conf, services);
|
||||
|
||||
pickers = new RegionPicker[] {
|
||||
new RandomRegionPicker(),
|
||||
new LoadPicker(),
|
||||
localityPicker
|
||||
candidateGenerators = new CandidateGenerator[] {
|
||||
new RandomCandidateGenerator(),
|
||||
new LoadCandidateGenerator(),
|
||||
localityCandidateGenerator,
|
||||
new RegionReplicaCandidateGenerator(),
|
||||
};
|
||||
|
||||
regionLoadFunctions = new CostFromRegionLoadFunction[] {
|
||||
|
@ -152,6 +156,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
new MoveCostFunction(conf),
|
||||
localityCost,
|
||||
new TableSkewCostFunction(conf),
|
||||
new RegionReplicaHostCostFunction(conf),
|
||||
new RegionReplicaRackCostFunction(conf),
|
||||
regionLoadFunctions[0],
|
||||
regionLoadFunctions[1],
|
||||
regionLoadFunctions[2],
|
||||
|
@ -167,7 +173,6 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
@Override
|
||||
public void setClusterStatus(ClusterStatus st) {
|
||||
super.setClusterStatus(st);
|
||||
regionFinder.setClusterStatus(st);
|
||||
updateRegionLoad();
|
||||
for(CostFromRegionLoadFunction cost : regionLoadFunctions) {
|
||||
cost.setClusterStatus(st);
|
||||
|
@ -177,9 +182,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
@Override
|
||||
public void setMasterServices(MasterServices masterServices) {
|
||||
super.setMasterServices(masterServices);
|
||||
this.regionFinder.setServices(masterServices);
|
||||
this.localityCost.setServices(masterServices);
|
||||
this.localityPicker.setServices(masterServices);
|
||||
this.localityCandidateGenerator.setServices(masterServices);
|
||||
|
||||
}
|
||||
|
||||
|
@ -202,8 +206,10 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
long startTime = EnvironmentEdgeManager.currentTimeMillis();
|
||||
|
||||
// Keep track of servers to iterate through them.
|
||||
Cluster cluster = new Cluster(masterServerName, clusterState,
|
||||
loads, regionFinder, getBackupMasters(), tablesOnMaster);
|
||||
Cluster cluster = new Cluster(masterServerName,
|
||||
clusterState, loads, regionFinder, getBackupMasters(), tablesOnMaster, rackManager);
|
||||
initCosts(cluster);
|
||||
|
||||
double currentCost = computeCost(cluster, Double.MAX_VALUE);
|
||||
|
||||
double initCost = currentCost;
|
||||
|
@ -213,42 +219,30 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
((long)cluster.numRegions * (long)this.stepsPerRegion * (long)cluster.numServers));
|
||||
// Perform a stochastic walk to see if we can get a good fit.
|
||||
long step;
|
||||
|
||||
for (step = 0; step < computedMaxSteps; step++) {
|
||||
int pickerIdx = RANDOM.nextInt(pickers.length);
|
||||
RegionPicker p = pickers[pickerIdx];
|
||||
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> picks = p.pick(cluster);
|
||||
int generatorIdx = RANDOM.nextInt(candidateGenerators.length);
|
||||
CandidateGenerator p = candidateGenerators[generatorIdx];
|
||||
Cluster.Action action = p.generate(cluster);
|
||||
|
||||
int leftServer = picks.getFirst().getFirst();
|
||||
int leftRegion = picks.getFirst().getSecond();
|
||||
int rightServer = picks.getSecond().getFirst();
|
||||
int rightRegion = picks.getSecond().getSecond();
|
||||
|
||||
// We couldn't find a server
|
||||
if (rightServer < 0 || leftServer < 0) {
|
||||
if (action.type == Type.NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We randomly picked to do nothing.
|
||||
if (leftRegion < 0 && rightRegion < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cluster.moveOrSwapRegion(leftServer,
|
||||
rightServer,
|
||||
leftRegion,
|
||||
rightRegion);
|
||||
cluster.doAction(action);
|
||||
updateCostsWithAction(cluster, action);
|
||||
|
||||
newCost = computeCost(cluster, currentCost);
|
||||
|
||||
// Should this be kept?
|
||||
if (newCost < currentCost) {
|
||||
currentCost = newCost;
|
||||
} else {
|
||||
// Put things back the way they were before.
|
||||
// TODO: undo by remembering old values, using an UndoAction class
|
||||
cluster.moveOrSwapRegion(leftServer,
|
||||
rightServer,
|
||||
rightRegion,
|
||||
leftRegion);
|
||||
// TODO: undo by remembering old values
|
||||
Action undoAction = action.undoAction();
|
||||
cluster.doAction(undoAction);
|
||||
updateCostsWithAction(cluster, undoAction);
|
||||
}
|
||||
|
||||
if (EnvironmentEdgeManager.currentTimeMillis() - startTime >
|
||||
|
@ -343,6 +337,17 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
}
|
||||
|
||||
protected void initCosts(Cluster cluster) {
|
||||
for (CostFunction c:costFunctions) {
|
||||
c.init(cluster);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateCostsWithAction(Cluster cluster, Action action) {
|
||||
for (CostFunction c : costFunctions) {
|
||||
c.postAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main cost function. It will compute a cost associated with a proposed cluster
|
||||
|
@ -361,7 +366,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
continue;
|
||||
}
|
||||
|
||||
total += c.getMultiplier() * c.cost(cluster);
|
||||
total += c.getMultiplier() * c.cost();
|
||||
|
||||
if (total > previousCost) {
|
||||
return total;
|
||||
|
@ -370,8 +375,9 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
return total;
|
||||
}
|
||||
|
||||
abstract static class RegionPicker {
|
||||
abstract Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster);
|
||||
/** Generates a candidate action to be applied to the cluster for cost function search */
|
||||
abstract static class CandidateGenerator {
|
||||
abstract Cluster.Action generate(Cluster cluster);
|
||||
|
||||
/**
|
||||
* From a list of regions pick a random one. Null can be returned which
|
||||
|
@ -402,6 +408,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
|
||||
return RANDOM.nextInt(cluster.numServers);
|
||||
}
|
||||
|
||||
protected int pickOtherRandomServer(Cluster cluster, int serverIndex) {
|
||||
if (cluster.numServers < 2) {
|
||||
return -1;
|
||||
|
@ -414,11 +421,11 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
}
|
||||
|
||||
protected Pair<Integer, Integer> pickRandomRegions(Cluster cluster,
|
||||
protected Cluster.Action pickRandomRegions(Cluster cluster,
|
||||
int thisServer,
|
||||
int otherServer) {
|
||||
if (thisServer < 0 || otherServer < 0) {
|
||||
return new Pair<Integer, Integer>(-1, -1);
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
|
||||
// Decide who is most likely to need another region
|
||||
|
@ -432,45 +439,50 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
int thisRegion = pickRandomRegion(cluster, thisServer, thisChance);
|
||||
int otherRegion = pickRandomRegion(cluster, otherServer, otherChance);
|
||||
|
||||
return new Pair<Integer, Integer>(thisRegion, otherRegion);
|
||||
return getAction(thisServer, thisRegion, otherServer, otherRegion);
|
||||
}
|
||||
|
||||
protected Cluster.Action getAction (int fromServer, int fromRegion,
|
||||
int toServer, int toRegion) {
|
||||
if (fromServer < 0 || toServer < 0) {
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
if (fromRegion > 0 && toRegion > 0) {
|
||||
return new Cluster.SwapRegionsAction(fromServer, fromRegion,
|
||||
toServer, toRegion);
|
||||
} else if (fromRegion > 0) {
|
||||
return new Cluster.MoveRegionAction(fromRegion, fromServer, toServer);
|
||||
} else if (toRegion > 0) {
|
||||
return new Cluster.MoveRegionAction(toRegion, toServer, fromServer);
|
||||
} else {
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class RandomRegionPicker extends RegionPicker {
|
||||
static class RandomCandidateGenerator extends CandidateGenerator {
|
||||
|
||||
@Override
|
||||
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster) {
|
||||
Cluster.Action generate(Cluster cluster) {
|
||||
|
||||
int thisServer = pickRandomServer(cluster);
|
||||
|
||||
// Pick the other server
|
||||
int otherServer = pickOtherRandomServer(cluster, thisServer);
|
||||
|
||||
Pair<Integer, Integer> regions = pickRandomRegions(cluster, thisServer, otherServer);
|
||||
|
||||
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
|
||||
new Pair<Integer, Integer>(thisServer, regions.getFirst()),
|
||||
new Pair<Integer, Integer>(otherServer, regions.getSecond())
|
||||
|
||||
);
|
||||
return pickRandomRegions(cluster, thisServer, otherServer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class LoadPicker extends RegionPicker {
|
||||
public static class LoadCandidateGenerator extends CandidateGenerator {
|
||||
|
||||
@Override
|
||||
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster) {
|
||||
Cluster.Action generate(Cluster cluster) {
|
||||
cluster.sortServersByRegionCount();
|
||||
int thisServer = pickMostLoadedServer(cluster, -1);
|
||||
int otherServer = pickLeastLoadedServer(cluster, thisServer);
|
||||
|
||||
Pair<Integer, Integer> regions = pickRandomRegions(cluster, thisServer, otherServer);
|
||||
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
|
||||
new Pair<Integer, Integer>(thisServer, regions.getFirst()),
|
||||
new Pair<Integer, Integer>(otherServer, regions.getSecond())
|
||||
|
||||
);
|
||||
return pickRandomRegions(cluster, thisServer, otherServer);
|
||||
}
|
||||
|
||||
private int pickLeastLoadedServer(final Cluster cluster, int thisServer) {
|
||||
|
@ -500,21 +512,18 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
}
|
||||
|
||||
static class LocalityBasedPicker extends RegionPicker {
|
||||
static class LocalityBasedCandidateGenerator extends CandidateGenerator {
|
||||
|
||||
private MasterServices masterServices;
|
||||
|
||||
LocalityBasedPicker(MasterServices masterServices) {
|
||||
LocalityBasedCandidateGenerator(MasterServices masterServices) {
|
||||
this.masterServices = masterServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
Pair<Pair<Integer, Integer>, Pair<Integer, Integer>> pick(Cluster cluster) {
|
||||
Cluster.Action generate(Cluster cluster) {
|
||||
if (this.masterServices == null) {
|
||||
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
|
||||
new Pair<Integer, Integer>(-1,-1),
|
||||
new Pair<Integer, Integer>(-1,-1)
|
||||
);
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
// Pick a random region server
|
||||
int thisServer = pickRandomServer(cluster);
|
||||
|
@ -523,10 +532,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
int thisRegion = pickRandomRegion(cluster, thisServer, 0.0f);
|
||||
|
||||
if (thisRegion == -1) {
|
||||
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
|
||||
new Pair<Integer, Integer>(-1,-1),
|
||||
new Pair<Integer, Integer>(-1,-1)
|
||||
);
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
|
||||
// Pick the server with the highest locality
|
||||
|
@ -535,10 +541,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
// pick an region on the other server to potentially swap
|
||||
int otherRegion = this.pickRandomRegion(cluster, otherServer, 0.5f);
|
||||
|
||||
return new Pair<Pair<Integer, Integer>, Pair<Integer, Integer>>(
|
||||
new Pair<Integer, Integer>(thisServer,thisRegion),
|
||||
new Pair<Integer, Integer>(otherServer,otherRegion)
|
||||
);
|
||||
return getAction(thisServer, thisRegion, otherServer, otherRegion);
|
||||
}
|
||||
|
||||
private int pickHighestLocalityServer(Cluster cluster, int thisServer, int thisRegion) {
|
||||
|
@ -563,6 +566,79 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates candidates which moves the replicas out of the region server for
|
||||
* co-hosted region replicas
|
||||
*/
|
||||
public static class RegionReplicaCandidateGenerator extends CandidateGenerator {
|
||||
|
||||
RandomCandidateGenerator randomGenerator = new RandomCandidateGenerator();
|
||||
|
||||
@Override
|
||||
Cluster.Action generate(Cluster cluster) {
|
||||
|
||||
int serverIndex = pickRandomServer(cluster);
|
||||
|
||||
if (cluster.numServers <= 1 || serverIndex == -1) {
|
||||
return Cluster.NullAction;
|
||||
}
|
||||
|
||||
// randomly select one primaryIndex out of all region replicas in the same server
|
||||
// we don't know how many region replicas are co-hosted, we will randomly select one
|
||||
// using reservoir sampling (http://gregable.com/2007/10/reservoir-sampling.html)
|
||||
int currentPrimary = -1;
|
||||
int currentPrimaryIndex = -1;
|
||||
int primaryIndex = -1;
|
||||
double currentLargestRandom = -1;
|
||||
// regionsByPrimaryPerServer is a sorted array. Since it contains the primary region
|
||||
// ids for the regions hosted in server, a consecutive repetition means that replicas
|
||||
// are co-hosted
|
||||
for (int j = 0; j <= cluster.primariesOfRegionsPerServer[serverIndex].length; j++) {
|
||||
int primary = j < cluster.primariesOfRegionsPerServer[serverIndex].length
|
||||
? cluster.primariesOfRegionsPerServer[serverIndex][j] : -1;
|
||||
if (primary != currentPrimary) { // check for whether we see a new primary
|
||||
int numReplicas = j - currentPrimaryIndex;
|
||||
if (numReplicas > 1) { // means consecutive primaries, indicating co-location
|
||||
// decide to select this primary region id or not
|
||||
double currentRandom = RANDOM.nextDouble();
|
||||
if (currentRandom > currentLargestRandom) {
|
||||
primaryIndex = currentPrimary; // select this primary
|
||||
currentLargestRandom = currentRandom;
|
||||
}
|
||||
}
|
||||
currentPrimary = primary;
|
||||
currentPrimaryIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no pairs of region replicas co-hosted, default to random generator
|
||||
if (primaryIndex == -1) {
|
||||
// default to randompicker
|
||||
return randomGenerator.generate(cluster);
|
||||
}
|
||||
|
||||
// we have found the primary id for the region to move. Now find the actual regionIndex
|
||||
// with the given primary, prefer to move the secondary region.
|
||||
int regionIndex = -1;
|
||||
for (int k = 0; k < cluster.regionsPerServer[serverIndex].length; k++) {
|
||||
int region = cluster.regionsPerServer[serverIndex][k];
|
||||
if (primaryIndex == cluster.regionIndexToPrimaryIndex[region]) {
|
||||
// always move the secondary, not the primary
|
||||
if (!RegionReplicaUtil.isDefaultReplica(cluster.regions[region])) {
|
||||
regionIndex = region;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int toServerIndex = pickOtherRandomServer(cluster, serverIndex);
|
||||
|
||||
int toRegionIndex = pickRandomRegion(cluster, toServerIndex, 0.9f);
|
||||
|
||||
return getAction (serverIndex, regionIndex, toServerIndex, toRegionIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class of StochasticLoadBalancer's Cost Functions.
|
||||
*/
|
||||
|
@ -570,6 +646,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
|
||||
private float multiplier = 0;
|
||||
|
||||
protected Cluster cluster;
|
||||
|
||||
CostFunction(Configuration c) {
|
||||
|
||||
}
|
||||
|
@ -582,7 +660,42 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
this.multiplier = m;
|
||||
}
|
||||
|
||||
abstract double cost(Cluster cluster);
|
||||
/** Called once per LB invocation to give the cost function
|
||||
* to initialize it's state, and perform any costly calculation.
|
||||
*/
|
||||
void init(Cluster cluster) {
|
||||
this.cluster = cluster;
|
||||
}
|
||||
|
||||
/** Called once per cluster Action to give the cost function
|
||||
* an opportunity to update it's state. postAction() is always
|
||||
* called at least once before cost() is called with the cluster
|
||||
* that this action is performed on. */
|
||||
void postAction(Action action) {
|
||||
switch (action.type) {
|
||||
case NULL: break;
|
||||
case ASSIGN_REGION:
|
||||
AssignRegionAction ar = (AssignRegionAction) action;
|
||||
regionMoved(ar.region, -1, ar.server);
|
||||
break;
|
||||
case MOVE_REGION:
|
||||
MoveRegionAction mra = (MoveRegionAction) action;
|
||||
regionMoved(mra.region, mra.fromServer, mra.toServer);
|
||||
break;
|
||||
case SWAP_REGIONS:
|
||||
SwapRegionsAction a = (SwapRegionsAction) action;
|
||||
regionMoved(a.fromRegion, a.fromServer, a.toServer);
|
||||
regionMoved(a.toRegion, a.toServer, a.fromServer);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Uknown action:" + action.type);
|
||||
}
|
||||
}
|
||||
|
||||
protected void regionMoved(int region, int oldServer, int newServer) {
|
||||
}
|
||||
|
||||
abstract double cost();
|
||||
|
||||
/**
|
||||
* Function to compute a scaled cost using {@link DescriptiveStatistics}. It
|
||||
|
@ -611,8 +724,6 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
return scaled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private double getSum(double[] stats) {
|
||||
double total = 0;
|
||||
for(double s:stats) {
|
||||
|
@ -663,7 +774,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
|
||||
@Override
|
||||
double cost(Cluster cluster) {
|
||||
double cost() {
|
||||
// Try and size the max number of Moves, but always be prepared to move some.
|
||||
int maxMoves = Math.max((int) (cluster.numRegions * maxMovesPercent),
|
||||
DEFAULT_MAX_MOVES);
|
||||
|
@ -705,7 +816,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
|
||||
@Override
|
||||
double cost(Cluster cluster) {
|
||||
double cost() {
|
||||
if (stats == null || stats.length != cluster.numServers) {
|
||||
stats = new double[cluster.numServers];
|
||||
}
|
||||
|
@ -740,7 +851,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
|
||||
@Override
|
||||
double cost(Cluster cluster) {
|
||||
double cost() {
|
||||
double max = cluster.numRegions;
|
||||
double min = ((double) cluster.numRegions) / cluster.numServers;
|
||||
double value = 0;
|
||||
|
@ -775,7 +886,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
|
||||
@Override
|
||||
double cost(Cluster cluster) {
|
||||
double cost() {
|
||||
double max = 0;
|
||||
double cost = 0;
|
||||
|
||||
|
@ -834,9 +945,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
this.loads = l;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
double cost(Cluster cluster) {
|
||||
double cost() {
|
||||
if (clusterStatus == null || loads == null) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -930,6 +1040,165 @@ public class StochasticLoadBalancer extends BaseLoadBalancer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cost function for region replicas. We give a very high cost to hosting
|
||||
* replicas of the same region in the same host. We do not prevent the case
|
||||
* though, since if numReplicas > numRegionServers, we still want to keep the
|
||||
* replica open.
|
||||
*/
|
||||
public static class RegionReplicaHostCostFunction extends CostFunction {
|
||||
private static final String REGION_REPLICA_HOST_COST_KEY =
|
||||
"hbase.master.balancer.stochastic.regionReplicaHostCostKey";
|
||||
private static final float DEFAULT_REGION_REPLICA_HOST_COST_KEY = 100000;
|
||||
|
||||
long maxCost = 0;
|
||||
long[] costsPerGroup; // group is either server, host or rack
|
||||
int[][] primariesOfRegionsPerGroup;
|
||||
|
||||
public RegionReplicaHostCostFunction(Configuration conf) {
|
||||
super(conf);
|
||||
this.setMultiplier(conf.getFloat(REGION_REPLICA_HOST_COST_KEY,
|
||||
DEFAULT_REGION_REPLICA_HOST_COST_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(Cluster cluster) {
|
||||
super.init(cluster);
|
||||
// max cost is the case where every region replica is hosted together regardless of host
|
||||
maxCost = cluster.numHosts > 1 ? getMaxCost(cluster) : 0;
|
||||
costsPerGroup = new long[cluster.numHosts];
|
||||
primariesOfRegionsPerGroup = cluster.multiServersPerHost // either server based or host based
|
||||
? cluster.primariesOfRegionsPerHost
|
||||
: cluster.primariesOfRegionsPerServer;
|
||||
for (int i = 0 ; i < primariesOfRegionsPerGroup.length; i++) {
|
||||
costsPerGroup[i] = costPerGroup(primariesOfRegionsPerGroup[i]);
|
||||
}
|
||||
}
|
||||
|
||||
long getMaxCost(Cluster cluster) {
|
||||
if (!cluster.hasRegionReplicas) {
|
||||
return 0; // short circuit
|
||||
}
|
||||
// max cost is the case where every region replica is hosted together regardless of host
|
||||
int[] primariesOfRegions = new int[cluster.numRegions];
|
||||
for (int i = 0; i < cluster.regions.length; i++) {
|
||||
// assume all regions are hosted by only one server
|
||||
int primaryIndex = cluster.regionIndexToPrimaryIndex[i];
|
||||
primariesOfRegions[i] = primaryIndex;
|
||||
}
|
||||
|
||||
Arrays.sort(primariesOfRegions);
|
||||
|
||||
// compute numReplicas from the sorted array
|
||||
return costPerGroup(primariesOfRegions);
|
||||
}
|
||||
|
||||
@Override
|
||||
double cost() {
|
||||
if (maxCost <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long totalCost = 0;
|
||||
for (int i = 0 ; i < costsPerGroup.length; i++) {
|
||||
totalCost += costsPerGroup[i];
|
||||
}
|
||||
return scale(0, maxCost, totalCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* For each primary region, it computes the total number of replicas in the array (numReplicas)
|
||||
* and returns a sum of numReplicas-1 squared. For example, if the server hosts
|
||||
* regions a, b, c, d, e, f where a and b are same replicas, and c,d,e are same replicas, it
|
||||
* returns (2-1) * (2-1) + (3-1) * (3-1) + (1-1) * (1-1).
|
||||
* @param primariesOfRegions a sorted array of primary regions ids for the regions hosted
|
||||
* @return a sum of numReplicas-1 squared for each primary region in the group.
|
||||
*/
|
||||
protected long costPerGroup(int[] primariesOfRegions) {
|
||||
long cost = 0;
|
||||
int currentPrimary = -1;
|
||||
int currentPrimaryIndex = -1;
|
||||
// primariesOfRegions is a sorted array of primary ids of regions. Replicas of regions
|
||||
// sharing the same primary will have consecutive numbers in the array.
|
||||
for (int j = 0 ; j <= primariesOfRegions.length; j++) {
|
||||
int primary = j < primariesOfRegions.length ? primariesOfRegions[j] : -1;
|
||||
if (primary != currentPrimary) { // we see a new primary
|
||||
int numReplicas = j - currentPrimaryIndex;
|
||||
// square the cost
|
||||
if (numReplicas > 1) { // means consecutive primaries, indicating co-location
|
||||
cost += (numReplicas - 1) * (numReplicas - 1);
|
||||
}
|
||||
currentPrimary = primary;
|
||||
currentPrimaryIndex = j;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regionMoved(int region, int oldServer, int newServer) {
|
||||
if (maxCost <= 0) {
|
||||
return; // no need to compute
|
||||
}
|
||||
if (cluster.multiServersPerHost) {
|
||||
int oldHost = cluster.serverIndexToHostIndex[oldServer];
|
||||
int newHost = cluster.serverIndexToHostIndex[newServer];
|
||||
if (newHost != oldHost) {
|
||||
costsPerGroup[oldHost] = costPerGroup(cluster.primariesOfRegionsPerHost[oldHost]);
|
||||
costsPerGroup[newHost] = costPerGroup(cluster.primariesOfRegionsPerHost[newHost]);
|
||||
}
|
||||
} else {
|
||||
costsPerGroup[oldServer] = costPerGroup(cluster.primariesOfRegionsPerServer[oldServer]);
|
||||
costsPerGroup[newServer] = costPerGroup(cluster.primariesOfRegionsPerServer[newServer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cost function for region replicas for the rack distribution. We give a relatively high
|
||||
* cost to hosting replicas of the same region in the same rack. We do not prevent the case
|
||||
* though.
|
||||
*/
|
||||
public static class RegionReplicaRackCostFunction extends RegionReplicaHostCostFunction {
|
||||
private static final String REGION_REPLICA_RACK_COST_KEY =
|
||||
"hbase.master.balancer.stochastic.regionReplicaRackCostKey";
|
||||
private static final float DEFAULT_REGION_REPLICA_RACK_COST_KEY = 10000;
|
||||
|
||||
public RegionReplicaRackCostFunction(Configuration conf) {
|
||||
super(conf);
|
||||
this.setMultiplier(conf.getFloat(REGION_REPLICA_RACK_COST_KEY, DEFAULT_REGION_REPLICA_RACK_COST_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(Cluster cluster) {
|
||||
this.cluster = cluster;
|
||||
if (cluster.numRacks <= 1) {
|
||||
maxCost = 0;
|
||||
return; // disabled for 1 rack
|
||||
}
|
||||
// max cost is the case where every region replica is hosted together regardless of rack
|
||||
maxCost = getMaxCost(cluster);
|
||||
costsPerGroup = new long[cluster.numRacks];
|
||||
for (int i = 0 ; i < cluster.primariesOfRegionsPerRack.length; i++) {
|
||||
costsPerGroup[i] = costPerGroup(cluster.primariesOfRegionsPerRack[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void regionMoved(int region, int oldServer, int newServer) {
|
||||
if (maxCost <= 0) {
|
||||
return; // no need to compute
|
||||
}
|
||||
int oldRack = cluster.serverIndexToRackIndex[oldServer];
|
||||
int newRack = cluster.serverIndexToRackIndex[newServer];
|
||||
if (newRack != oldRack) {
|
||||
costsPerGroup[oldRack] = costPerGroup(cluster.primariesOfRegionsPerRack[oldRack]);
|
||||
costsPerGroup[newRack] = costPerGroup(cluster.primariesOfRegionsPerRack[newRack]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the cost of total memstore size. The more unbalanced the higher the
|
||||
* computed cost will be. This uses a rolling average of regionload.
|
||||
|
|
|
@ -36,8 +36,10 @@ 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.HRegionLocation;
|
||||
import org.apache.hadoop.hbase.HTableDescriptor;
|
||||
import org.apache.hadoop.hbase.MediumTests;
|
||||
import org.apache.hadoop.hbase.RegionLocations;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.catalog.CatalogTracker;
|
||||
|
@ -123,60 +125,60 @@ public class TestMasterOperationsForRegionReplicas {
|
|||
assert (state != null);
|
||||
}
|
||||
}
|
||||
// TODO: HBASE-10351 should uncomment the following tests (since the tests assume region placements are handled)
|
||||
// List<Result> metaRows = MetaReader.fullScan(ct);
|
||||
// int numRows = 0;
|
||||
// for (Result result : metaRows) {
|
||||
// RegionLocations locations = MetaReader.getRegionLocations(result);
|
||||
// HRegionInfo hri = locations.getRegionLocation().getRegionInfo();
|
||||
// if (!hri.getTable().equals(table)) continue;
|
||||
// numRows += 1;
|
||||
// HRegionLocation[] servers = locations.getRegionLocations();
|
||||
// // have two locations for the replicas of a region, and the locations should be different
|
||||
// assert(servers.length == 2);
|
||||
// assert(!servers[0].equals(servers[1]));
|
||||
// }
|
||||
// assert(numRows == numRegions);
|
||||
//
|
||||
// // The same verification of the meta as above but with the SnapshotOfRegionAssignmentFromMeta
|
||||
// // class
|
||||
// validateFromSnapshotFromMeta(table, numRegions, numReplica, ct);
|
||||
//
|
||||
// // Now kill the master, restart it and see if the assignments are kept
|
||||
// ServerName master = TEST_UTIL.getHBaseClusterInterface().getClusterStatus().getMaster();
|
||||
// TEST_UTIL.getHBaseClusterInterface().stopMaster(master);
|
||||
// TEST_UTIL.getHBaseClusterInterface().waitForMasterToStop(master, 30000);
|
||||
// TEST_UTIL.getHBaseClusterInterface().startMaster(master.getHostname());
|
||||
// TEST_UTIL.getHBaseClusterInterface().waitForActiveAndReadyMaster();
|
||||
// for (int i = 0; i < numRegions; i++) {
|
||||
// for (int j = 0; j < numReplica; j++) {
|
||||
// HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hris.get(i), j);
|
||||
// RegionState state = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
|
||||
// .getRegionStates().getRegionState(replica);
|
||||
// assert (state != null);
|
||||
// }
|
||||
// }
|
||||
// validateFromSnapshotFromMeta(table, numRegions, numReplica, ct);
|
||||
//
|
||||
// // Now shut the whole cluster down, and verify the assignments are kept so that the
|
||||
// // availability constraints are met.
|
||||
// TEST_UTIL.getConfiguration().setBoolean("hbase.master.startup.retainassign", true);
|
||||
// TEST_UTIL.shutdownMiniHBaseCluster();
|
||||
// TEST_UTIL.startMiniHBaseCluster(1, numSlaves);
|
||||
// TEST_UTIL.waitTableEnabled(table.getName());
|
||||
// ct = new CatalogTracker(TEST_UTIL.getConfiguration());
|
||||
// validateFromSnapshotFromMeta(table, numRegions, numReplica, ct);
|
||||
//
|
||||
// // Now shut the whole cluster down, and verify regions are assigned even if there is only
|
||||
// // one server running
|
||||
// TEST_UTIL.shutdownMiniHBaseCluster();
|
||||
// TEST_UTIL.startMiniHBaseCluster(1, 1);
|
||||
// TEST_UTIL.waitTableEnabled(table.getName());
|
||||
// ct = new CatalogTracker(TEST_UTIL.getConfiguration());
|
||||
// validateSingleRegionServerAssignment(ct, numRegions, numReplica);
|
||||
// for (int i = 1; i < numSlaves; i++) { //restore the cluster
|
||||
// TEST_UTIL.getMiniHBaseCluster().startRegionServer();
|
||||
// }
|
||||
|
||||
List<Result> metaRows = MetaReader.fullScan(ct);
|
||||
int numRows = 0;
|
||||
for (Result result : metaRows) {
|
||||
RegionLocations locations = MetaReader.getRegionLocations(result);
|
||||
HRegionInfo hri = locations.getRegionLocation().getRegionInfo();
|
||||
if (!hri.getTable().equals(table)) continue;
|
||||
numRows += 1;
|
||||
HRegionLocation[] servers = locations.getRegionLocations();
|
||||
// have two locations for the replicas of a region, and the locations should be different
|
||||
assert(servers.length == 2);
|
||||
assert(!servers[0].equals(servers[1]));
|
||||
}
|
||||
assert(numRows == numRegions);
|
||||
|
||||
// The same verification of the meta as above but with the SnapshotOfRegionAssignmentFromMeta
|
||||
// class
|
||||
validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica, ct);
|
||||
|
||||
// Now kill the master, restart it and see if the assignments are kept
|
||||
ServerName master = TEST_UTIL.getHBaseClusterInterface().getClusterStatus().getMaster();
|
||||
TEST_UTIL.getHBaseClusterInterface().stopMaster(master);
|
||||
TEST_UTIL.getHBaseClusterInterface().waitForMasterToStop(master, 30000);
|
||||
TEST_UTIL.getHBaseClusterInterface().startMaster(master.getHostname());
|
||||
TEST_UTIL.getHBaseClusterInterface().waitForActiveAndReadyMaster();
|
||||
for (int i = 0; i < numRegions; i++) {
|
||||
for (int j = 0; j < numReplica; j++) {
|
||||
HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hris.get(i), j);
|
||||
RegionState state = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
|
||||
.getRegionStates().getRegionState(replica);
|
||||
assert (state != null);
|
||||
}
|
||||
}
|
||||
validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica, ct);
|
||||
|
||||
// Now shut the whole cluster down, and verify the assignments are kept so that the
|
||||
// availability constraints are met.
|
||||
TEST_UTIL.getConfiguration().setBoolean("hbase.master.startup.retainassign", true);
|
||||
TEST_UTIL.shutdownMiniHBaseCluster();
|
||||
TEST_UTIL.startMiniHBaseCluster(1, numSlaves);
|
||||
TEST_UTIL.waitTableEnabled(table.getName());
|
||||
ct = new CatalogTracker(TEST_UTIL.getConfiguration());
|
||||
validateFromSnapshotFromMeta(TEST_UTIL, table, numRegions, numReplica, ct);
|
||||
|
||||
// Now shut the whole cluster down, and verify regions are assigned even if there is only
|
||||
// one server running
|
||||
TEST_UTIL.shutdownMiniHBaseCluster();
|
||||
TEST_UTIL.startMiniHBaseCluster(1, 1);
|
||||
TEST_UTIL.waitTableEnabled(table.getName());
|
||||
ct = new CatalogTracker(TEST_UTIL.getConfiguration());
|
||||
validateSingleRegionServerAssignment(ct, numRegions, numReplica);
|
||||
for (int i = 1; i < numSlaves; i++) { //restore the cluster
|
||||
TEST_UTIL.getMiniHBaseCluster().startRegionServer();
|
||||
}
|
||||
|
||||
//check on alter table
|
||||
admin.disableTable(table);
|
||||
|
@ -288,7 +290,7 @@ public class TestMasterOperationsForRegionReplicas {
|
|||
assert(count.get() == numRegions);
|
||||
}
|
||||
|
||||
private void validateFromSnapshotFromMeta(TableName table, int numRegions,
|
||||
private void validateFromSnapshotFromMeta(HBaseTestingUtility util, TableName table, int numRegions,
|
||||
int numReplica, CatalogTracker ct) throws IOException {
|
||||
SnapshotOfRegionAssignmentFromMeta snapshot = new SnapshotOfRegionAssignmentFromMeta(ct);
|
||||
snapshot.initialize();
|
||||
|
@ -296,6 +298,9 @@ public class TestMasterOperationsForRegionReplicas {
|
|||
assert(regionToServerMap.size() == numRegions * numReplica + 1); //'1' for the namespace
|
||||
Map<ServerName, List<HRegionInfo>> serverToRegionMap = snapshot.getRegionServerToRegionMap();
|
||||
for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverToRegionMap.entrySet()) {
|
||||
if (entry.getKey().equals(util.getHBaseCluster().getMaster().getServerName())) {
|
||||
continue;
|
||||
}
|
||||
List<HRegionInfo> regions = entry.getValue();
|
||||
Set<byte[]> setOfStartKeys = new HashSet<byte[]>();
|
||||
for (HRegionInfo region : regions) {
|
||||
|
@ -307,7 +312,7 @@ public class TestMasterOperationsForRegionReplicas {
|
|||
}
|
||||
// the number of startkeys will be equal to the number of regions hosted in each server
|
||||
// (each server will be hosting one replica of a region)
|
||||
assertEquals(setOfStartKeys.size() , numRegions);
|
||||
assertEquals(numRegions, setOfStartKeys.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,9 +321,14 @@ public class TestMasterOperationsForRegionReplicas {
|
|||
SnapshotOfRegionAssignmentFromMeta snapshot = new SnapshotOfRegionAssignmentFromMeta(ct);
|
||||
snapshot.initialize();
|
||||
Map<HRegionInfo, ServerName> regionToServerMap = snapshot.getRegionToRegionServerMap();
|
||||
assert(regionToServerMap.size() == numRegions * numReplica + 1); //'1' for the namespace
|
||||
assertEquals(regionToServerMap.size(), numRegions * numReplica + 1); //'1' for the namespace
|
||||
Map<ServerName, List<HRegionInfo>> serverToRegionMap = snapshot.getRegionServerToRegionMap();
|
||||
assert(serverToRegionMap.keySet().size() == 1);
|
||||
assert(serverToRegionMap.values().iterator().next().size() == numRegions * numReplica + 1);
|
||||
assertEquals(serverToRegionMap.keySet().size(), 2); // 1 rs + 1 master
|
||||
for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverToRegionMap.entrySet()) {
|
||||
if (entry.getKey().equals(TEST_UTIL.getHBaseCluster().getMaster().getServerName())) {
|
||||
continue;
|
||||
}
|
||||
assertEquals(entry.getValue().size(), numRegions * numReplica);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,20 +21,26 @@ import static org.junit.Assert.assertTrue;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.HRegionInfo;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
|
||||
import org.apache.hadoop.hbase.master.RackManager;
|
||||
import org.apache.hadoop.hbase.master.RegionPlan;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* Class used to be the base of unit tests on load balancers. It gives helper
|
||||
|
@ -80,6 +86,50 @@ public class BalancerTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether region replicas are not hosted on the same host.
|
||||
*/
|
||||
public void assertRegionReplicaPlacement(Map<ServerName, List<HRegionInfo>> serverMap, RackManager rackManager) {
|
||||
TreeMap<String, Set<HRegionInfo>> regionsPerHost = new TreeMap<String, Set<HRegionInfo>>();
|
||||
TreeMap<String, Set<HRegionInfo>> regionsPerRack = new TreeMap<String, Set<HRegionInfo>>();
|
||||
|
||||
for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
|
||||
String hostname = entry.getKey().getHostname();
|
||||
Set<HRegionInfo> infos = regionsPerHost.get(hostname);
|
||||
if (infos == null) {
|
||||
infos = new HashSet<HRegionInfo>();
|
||||
regionsPerHost.put(hostname, infos);
|
||||
}
|
||||
|
||||
for (HRegionInfo info : entry.getValue()) {
|
||||
HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
|
||||
if (!infos.add(primaryInfo)) {
|
||||
Assert.fail("Two or more region replicas are hosted on the same host after balance");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rackManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
|
||||
String rack = rackManager.getRack(entry.getKey());
|
||||
Set<HRegionInfo> infos = regionsPerRack.get(rack);
|
||||
if (infos == null) {
|
||||
infos = new HashSet<HRegionInfo>();
|
||||
regionsPerRack.put(rack, infos);
|
||||
}
|
||||
|
||||
for (HRegionInfo info : entry.getValue()) {
|
||||
HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
|
||||
if (!infos.add(primaryInfo)) {
|
||||
Assert.fail("Two or more region replicas are hosted on the same rack after balance");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String printStats(List<ServerAndLoad> servers) {
|
||||
int numServers = servers.size();
|
||||
int totalRegions = 0;
|
||||
|
@ -159,18 +209,18 @@ public class BalancerTestBase {
|
|||
map.put(sn, sal);
|
||||
}
|
||||
|
||||
protected Map<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster) {
|
||||
protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster) {
|
||||
return mockClusterServers(mockCluster, -1);
|
||||
}
|
||||
|
||||
protected BaseLoadBalancer.Cluster mockCluster(int[] mockCluster) {
|
||||
return new BaseLoadBalancer.Cluster(null,
|
||||
mockClusterServers(mockCluster, -1), null, null, null, null);
|
||||
mockClusterServers(mockCluster, -1), null, null, null, null, null);
|
||||
}
|
||||
|
||||
protected Map<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster, int numTables) {
|
||||
protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster, int numTables) {
|
||||
int numServers = mockCluster.length;
|
||||
Map<ServerName, List<HRegionInfo>> servers = new TreeMap<ServerName, List<HRegionInfo>>();
|
||||
TreeMap<ServerName, List<HRegionInfo>> servers = new TreeMap<ServerName, List<HRegionInfo>>();
|
||||
for (int i = 0; i < numServers; i++) {
|
||||
int numRegions = mockCluster[i];
|
||||
ServerAndLoad sal = randomServer(0);
|
||||
|
@ -218,7 +268,7 @@ public class BalancerTestBase {
|
|||
ServerName sn = this.serverQueue.poll();
|
||||
return new ServerAndLoad(sn, numRegionsPerServer);
|
||||
}
|
||||
String host = "srv" + rand.nextInt(100000);
|
||||
String host = "srv" + rand.nextInt(Integer.MAX_VALUE);
|
||||
int port = rand.nextInt(60000);
|
||||
long startCode = rand.nextLong();
|
||||
ServerName sn = ServerName.valueOf(host, port, startCode);
|
||||
|
|
|
@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -46,6 +47,10 @@ import org.apache.hadoop.hbase.master.LoadBalancer;
|
|||
import org.apache.hadoop.hbase.master.MasterServices;
|
||||
import org.apache.hadoop.hbase.master.RegionPlan;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
|
||||
import org.apache.hadoop.hbase.master.RackManager;
|
||||
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.MoveRegionAction;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
@ -57,8 +62,11 @@ import com.google.common.collect.Lists;
|
|||
public class TestBaseLoadBalancer extends BalancerTestBase {
|
||||
|
||||
private static LoadBalancer loadBalancer;
|
||||
private static final Log LOG = LogFactory.getLog(TestStochasticLoadBalancer.class);
|
||||
private static final Log LOG = LogFactory.getLog(TestBaseLoadBalancer.class);
|
||||
private static final ServerName master = ServerName.valueOf("fake-master", 0, 1L);
|
||||
private static RackManager rackManager;
|
||||
private static final int NUM_SERVERS = 15;
|
||||
private static ServerName[] servers = new ServerName[NUM_SERVERS];
|
||||
|
||||
int[][] regionsAndServersMocks = new int[][] {
|
||||
// { num regions, num servers }
|
||||
|
@ -75,6 +83,21 @@ public class TestBaseLoadBalancer extends BalancerTestBase {
|
|||
MasterServices st = Mockito.mock(MasterServices.class);
|
||||
Mockito.when(st.getServerName()).thenReturn(master);
|
||||
loadBalancer.setMasterServices(st);
|
||||
|
||||
// Set up the rack topologies (5 machines per rack)
|
||||
rackManager = Mockito.mock(RackManager.class);
|
||||
for (int i = 0; i < NUM_SERVERS; i++) {
|
||||
servers[i] = ServerName.valueOf("foo"+i+":1234",-1);
|
||||
if (i < 5) {
|
||||
Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack1");
|
||||
}
|
||||
if (i >= 5 && i < 10) {
|
||||
Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack2");
|
||||
}
|
||||
if (i >= 10) {
|
||||
Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack3");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MockBalancer extends BaseLoadBalancer {
|
||||
|
@ -214,6 +237,138 @@ public class TestBaseLoadBalancer extends BalancerTestBase {
|
|||
assertRetainedAssignment(existing, listOfServerNames, assignment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegionAvailability() throws Exception {
|
||||
// Create a cluster with a few servers, assign them to specific racks
|
||||
// then assign some regions. The tests should check whether moving a
|
||||
// replica from one node to a specific other node or rack lowers the
|
||||
// availability of the region or not
|
||||
|
||||
List<HRegionInfo> list0 = new ArrayList<HRegionInfo>();
|
||||
List<HRegionInfo> list1 = new ArrayList<HRegionInfo>();
|
||||
List<HRegionInfo> list2 = new ArrayList<HRegionInfo>();
|
||||
// create a region (region1)
|
||||
HRegionInfo hri1 = new HRegionInfo(
|
||||
TableName.valueOf("table"), "key1".getBytes(), "key2".getBytes(),
|
||||
false, 100);
|
||||
// create a replica of the region (replica_of_region1)
|
||||
HRegionInfo hri2 = RegionReplicaUtil.getRegionInfoForReplica(hri1, 1);
|
||||
// create a second region (region2)
|
||||
HRegionInfo hri3 = new HRegionInfo(
|
||||
TableName.valueOf("table"), "key2".getBytes(), "key3".getBytes(),
|
||||
false, 101);
|
||||
list0.add(hri1); //only region1
|
||||
list1.add(hri2); //only replica_of_region1
|
||||
list2.add(hri3); //only region2
|
||||
Map<ServerName, List<HRegionInfo>> clusterState =
|
||||
new LinkedHashMap<ServerName, List<HRegionInfo>>();
|
||||
clusterState.put(servers[0], list0); //servers[0] hosts region1
|
||||
clusterState.put(servers[1], list1); //servers[1] hosts replica_of_region1
|
||||
clusterState.put(servers[2], list2); //servers[2] hosts region2
|
||||
// create a cluster with the above clusterState. The way in which the
|
||||
// cluster is created (constructor code) would make sure the indices of
|
||||
// the servers are in the order in which it is inserted in the clusterState
|
||||
// map (linkedhashmap is important). A similar thing applies to the region lists
|
||||
Cluster cluster = new Cluster(master, clusterState, null, null, null, null, rackManager);
|
||||
// check whether a move of region1 from servers[0] to servers[1] would lower
|
||||
// the availability of region1
|
||||
assertTrue(cluster.wouldLowerAvailability(hri1, servers[1]));
|
||||
// check whether a move of region1 from servers[0] to servers[2] would lower
|
||||
// the availability of region1
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri1, servers[2]));
|
||||
// check whether a move of replica_of_region1 from servers[0] to servers[2] would lower
|
||||
// the availability of replica_of_region1
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri2, servers[2]));
|
||||
// check whether a move of region2 from servers[0] to servers[1] would lower
|
||||
// the availability of region2
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri3, servers[1]));
|
||||
|
||||
// now lets have servers[1] host replica_of_region2
|
||||
list1.add(RegionReplicaUtil.getRegionInfoForReplica(hri3, 1));
|
||||
// create a new clusterState with the above change
|
||||
cluster = new Cluster(master, clusterState, null, null, null, null, rackManager);
|
||||
// now check whether a move of a replica from servers[0] to servers[1] would lower
|
||||
// the availability of region2
|
||||
assertTrue(cluster.wouldLowerAvailability(hri3, servers[1]));
|
||||
|
||||
// start over again
|
||||
clusterState.clear();
|
||||
clusterState.put(servers[0], list0); //servers[0], rack1 hosts region1
|
||||
clusterState.put(servers[5], list1); //servers[5], rack2 hosts replica_of_region1 and replica_of_region2
|
||||
clusterState.put(servers[6], list2); //servers[6], rack2 hosts region2
|
||||
clusterState.put(servers[10], new ArrayList<HRegionInfo>()); //servers[10], rack3 hosts no region
|
||||
// create a cluster with the above clusterState
|
||||
cluster = new Cluster(master, clusterState, null, null, null, null, rackManager);
|
||||
// check whether a move of region1 from servers[0],rack1 to servers[6],rack2 would
|
||||
// lower the availability
|
||||
|
||||
assertTrue(cluster.wouldLowerAvailability(hri1, servers[0]));
|
||||
|
||||
// now create a cluster without the rack manager
|
||||
cluster = new Cluster(master, clusterState, null, null, null, null, null);
|
||||
// now repeat check whether a move of region1 from servers[0] to servers[6] would
|
||||
// lower the availability
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri1, servers[6]));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegionAvailabilityWithRegionMoves() throws Exception {
|
||||
List<HRegionInfo> list0 = new ArrayList<HRegionInfo>();
|
||||
List<HRegionInfo> list1 = new ArrayList<HRegionInfo>();
|
||||
List<HRegionInfo> list2 = new ArrayList<HRegionInfo>();
|
||||
// create a region (region1)
|
||||
HRegionInfo hri1 = new HRegionInfo(
|
||||
TableName.valueOf("table"), "key1".getBytes(), "key2".getBytes(),
|
||||
false, 100);
|
||||
// create a replica of the region (replica_of_region1)
|
||||
HRegionInfo hri2 = RegionReplicaUtil.getRegionInfoForReplica(hri1, 1);
|
||||
// create a second region (region2)
|
||||
HRegionInfo hri3 = new HRegionInfo(
|
||||
TableName.valueOf("table"), "key2".getBytes(), "key3".getBytes(),
|
||||
false, 101);
|
||||
list0.add(hri1); //only region1
|
||||
list1.add(hri2); //only replica_of_region1
|
||||
list2.add(hri3); //only region2
|
||||
Map<ServerName, List<HRegionInfo>> clusterState =
|
||||
new LinkedHashMap<ServerName, List<HRegionInfo>>();
|
||||
clusterState.put(servers[0], list0); //servers[0] hosts region1
|
||||
clusterState.put(servers[1], list1); //servers[1] hosts replica_of_region1
|
||||
clusterState.put(servers[2], list2); //servers[2] hosts region2
|
||||
// create a cluster with the above clusterState. The way in which the
|
||||
// cluster is created (constructor code) would make sure the indices of
|
||||
// the servers are in the order in which it is inserted in the clusterState
|
||||
// map (linkedhashmap is important).
|
||||
Cluster cluster = new Cluster(master, clusterState, null, null, null, null, rackManager);
|
||||
// check whether moving region1 from servers[1] to servers[2] would lower availability
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri1, servers[2]));
|
||||
|
||||
// now move region1 from servers[0] to servers[2]
|
||||
cluster.doAction(new MoveRegionAction(0, 0, 2));
|
||||
// now repeat check whether moving region1 from servers[1] to servers[2]
|
||||
// would lower availability
|
||||
assertTrue(cluster.wouldLowerAvailability(hri1, servers[2]));
|
||||
|
||||
// start over again
|
||||
clusterState.clear();
|
||||
List<HRegionInfo> list3 = new ArrayList<HRegionInfo>();
|
||||
HRegionInfo hri4 = RegionReplicaUtil.getRegionInfoForReplica(hri3, 1);
|
||||
list3.add(hri4);
|
||||
clusterState.put(servers[0], list0); //servers[0], rack1 hosts region1
|
||||
clusterState.put(servers[5], list1); //servers[5], rack2 hosts replica_of_region1
|
||||
clusterState.put(servers[6], list2); //servers[6], rack2 hosts region2
|
||||
clusterState.put(servers[12], list3); //servers[12], rack3 hosts replica_of_region2
|
||||
// create a cluster with the above clusterState
|
||||
cluster = new Cluster(master, clusterState, null, null, null, null, rackManager);
|
||||
// check whether a move of replica_of_region2 from servers[12],rack3 to servers[0],rack1 would
|
||||
// lower the availability
|
||||
assertTrue(!cluster.wouldLowerAvailability(hri4, servers[0]));
|
||||
// now move region2 from servers[6],rack2 to servers[0],rack1
|
||||
cluster.doAction(new MoveRegionAction(2, 2, 0));
|
||||
// now repeat check if replica_of_region2 from servers[12],rack3 to servers[0],rack1 would
|
||||
// lower the availability
|
||||
assertTrue(cluster.wouldLowerAvailability(hri3, servers[0]));
|
||||
}
|
||||
|
||||
private List<ServerName> getListOfServerNames(final List<ServerAndLoad> sals) {
|
||||
List<ServerName> list = new ArrayList<ServerName>();
|
||||
for (ServerAndLoad e : sals) {
|
||||
|
@ -289,7 +444,7 @@ public class TestBaseLoadBalancer extends BalancerTestBase {
|
|||
assignRegions(regions, oldServers, clusterState);
|
||||
|
||||
// should not throw exception:
|
||||
BaseLoadBalancer.Cluster cluster = new Cluster(null, clusterState, null, null, null, null);
|
||||
BaseLoadBalancer.Cluster cluster = new Cluster(null, clusterState, null, null, null, null, null);
|
||||
assertEquals(101 + 9, cluster.numRegions);
|
||||
assertEquals(10, cluster.numServers); // only 10 servers because they share the same host + port
|
||||
}
|
||||
|
@ -331,7 +486,7 @@ public class TestBaseLoadBalancer extends BalancerTestBase {
|
|||
when(locationFinder.getTopBlockLocations(regions.get(43))).thenReturn(
|
||||
Lists.newArrayList(ServerName.valueOf("foo", 0, 0))); // this server does not exists in clusterStatus
|
||||
|
||||
BaseLoadBalancer.Cluster cluster = new Cluster(null, clusterState, null, locationFinder, null, null);
|
||||
BaseLoadBalancer.Cluster cluster = new Cluster(null, clusterState, null, locationFinder, null, null, null);
|
||||
|
||||
int r0 = ArrayUtils.indexOf(cluster.regions, regions.get(0)); // this is ok, it is just a test
|
||||
int r1 = ArrayUtils.indexOf(cluster.regions, regions.get(1));
|
||||
|
|
|
@ -17,10 +17,19 @@
|
|||
*/
|
||||
package org.apache.hadoop.hbase.master.balancer;
|
||||
|
||||
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 static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
@ -34,29 +43,30 @@ import org.apache.hadoop.hbase.MediumTests;
|
|||
import org.apache.hadoop.hbase.RegionLoad;
|
||||
import org.apache.hadoop.hbase.ServerLoad;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.client.RegionReplicaUtil;
|
||||
import org.apache.hadoop.hbase.master.RackManager;
|
||||
import org.apache.hadoop.hbase.master.RegionPlan;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.net.DNSToSwitchMapping;
|
||||
import org.apache.hadoop.net.NetworkTopology;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
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 static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Category(MediumTests.class)
|
||||
public class TestStochasticLoadBalancer extends BalancerTestBase {
|
||||
public static final String REGION_KEY = "testRegion";
|
||||
private static StochasticLoadBalancer loadBalancer;
|
||||
private static final Log LOG = LogFactory.getLog(TestStochasticLoadBalancer.class);
|
||||
private static Configuration conf;
|
||||
private static final ServerName master = ServerName.valueOf("fake-master", 0, 1L);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeAllTests() throws Exception {
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
conf = HBaseConfiguration.create();
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 0.75f);
|
||||
conf.setClass("hbase.util.ip.to.rack.determiner",
|
||||
MyRackResolver.class, DNSToSwitchMapping.class);
|
||||
loadBalancer = new StochasticLoadBalancer();
|
||||
loadBalancer.setConf(conf);
|
||||
}
|
||||
|
@ -187,22 +197,29 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
StochasticLoadBalancer.CostFunction
|
||||
costFunction = new StochasticLoadBalancer.RegionCountSkewCostFunction(conf, 1, 1);
|
||||
for (int[] mockCluster : clusterStateMocks) {
|
||||
double cost = costFunction.cost(mockCluster(mockCluster));
|
||||
costFunction.init(mockCluster(mockCluster));
|
||||
double cost = costFunction.cost();
|
||||
assertTrue(cost >= 0);
|
||||
assertTrue(cost <= 1.01);
|
||||
}
|
||||
costFunction.init(mockCluster(new int[]{0, 0, 0, 0, 1}));
|
||||
assertEquals(1,
|
||||
costFunction.cost(mockCluster(new int[]{0, 0, 0, 0, 1})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
costFunction.init(mockCluster(new int[]{0, 0, 0, 1, 1}));
|
||||
assertEquals(.75,
|
||||
costFunction.cost(mockCluster(new int[]{0, 0, 0, 1, 1})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
costFunction.init(mockCluster(new int[]{0, 0, 1, 1, 1}));
|
||||
assertEquals(.5,
|
||||
costFunction.cost(mockCluster(new int[]{0, 0, 1, 1, 1})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
costFunction.init(mockCluster(new int[]{0, 1, 1, 1, 1}));
|
||||
assertEquals(.25,
|
||||
costFunction.cost(mockCluster(new int[]{0, 1, 1, 1, 1})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
costFunction.init(mockCluster(new int[]{1, 1, 1, 1, 1}));
|
||||
assertEquals(0,
|
||||
costFunction.cost(mockCluster(new int[]{1, 1, 1, 1, 1})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
costFunction.init(mockCluster(new int[]{10, 10, 10, 10, 10}));
|
||||
assertEquals(0,
|
||||
costFunction.cost(mockCluster(new int[]{10, 10, 10, 10, 10})), 0.01);
|
||||
costFunction.cost(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -212,7 +229,8 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
costFunction = new StochasticLoadBalancer.TableSkewCostFunction(conf);
|
||||
for (int[] mockCluster : clusterStateMocks) {
|
||||
BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
|
||||
double cost = costFunction.cost(cluster);
|
||||
costFunction.init(cluster);
|
||||
double cost = costFunction.cost();
|
||||
assertTrue(cost >= 0);
|
||||
assertTrue(cost <= 1.01);
|
||||
}
|
||||
|
@ -250,10 +268,11 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 3;
|
||||
int numRegions = 20;
|
||||
int numRegionsPerServer = 3; //all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 2;
|
||||
|
||||
Map<ServerName, List<HRegionInfo>> serverMap =
|
||||
createServerMap(numNodes, numRegions, numRegionsPerServer, numTables);
|
||||
createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
|
||||
List<ServerAndLoad> list = convertToList(serverMap);
|
||||
|
||||
|
||||
|
@ -275,13 +294,103 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
assertNull(plans);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplicaCost() {
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
StochasticLoadBalancer.CostFunction
|
||||
costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
|
||||
for (int[] mockCluster : clusterStateMocks) {
|
||||
BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
|
||||
costFunction.init(cluster);
|
||||
double cost = costFunction.cost();
|
||||
assertTrue(cost >= 0);
|
||||
assertTrue(cost <= 1.01);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplicaCostForReplicas() {
|
||||
Configuration conf = HBaseConfiguration.create();
|
||||
StochasticLoadBalancer.CostFunction
|
||||
costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
|
||||
|
||||
int [] servers = new int[] {3,3,3,3,3};
|
||||
TreeMap<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(servers);
|
||||
|
||||
BaseLoadBalancer.Cluster cluster;
|
||||
|
||||
cluster = new BaseLoadBalancer.Cluster(master, clusterState, null, null, null, null, null);
|
||||
costFunction.init(cluster);
|
||||
double costWithoutReplicas = costFunction.cost();
|
||||
assertEquals(0, costWithoutReplicas, 0);
|
||||
|
||||
// replicate the region from first server to the last server
|
||||
HRegionInfo replica1 = RegionReplicaUtil.getRegionInfoForReplica(
|
||||
clusterState.firstEntry().getValue().get(0),1);
|
||||
clusterState.lastEntry().getValue().add(replica1);
|
||||
|
||||
cluster = new BaseLoadBalancer.Cluster(master, clusterState, null, null, null, null, null);
|
||||
costFunction.init(cluster);
|
||||
double costWith1ReplicaDifferentServer = costFunction.cost();
|
||||
|
||||
assertEquals(0, costWith1ReplicaDifferentServer, 0);
|
||||
|
||||
// add a third replica to the last server
|
||||
HRegionInfo replica2 = RegionReplicaUtil.getRegionInfoForReplica(replica1, 2);
|
||||
clusterState.lastEntry().getValue().add(replica2);
|
||||
|
||||
cluster = new BaseLoadBalancer.Cluster(master, clusterState, null, null, null, null, null);
|
||||
costFunction.init(cluster);
|
||||
double costWith1ReplicaSameServer = costFunction.cost();
|
||||
|
||||
assertTrue(costWith1ReplicaDifferentServer < costWith1ReplicaSameServer);
|
||||
|
||||
// test with replication = 4 for following:
|
||||
|
||||
HRegionInfo replica3;
|
||||
Iterator<Entry<ServerName, List<HRegionInfo>>> it;
|
||||
Entry<ServerName, List<HRegionInfo>> entry;
|
||||
|
||||
clusterState = mockClusterServers(servers);
|
||||
it = clusterState.entrySet().iterator();
|
||||
entry = it.next(); //first server
|
||||
HRegionInfo hri = entry.getValue().get(0);
|
||||
replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
|
||||
replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
|
||||
replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
|
||||
entry.getValue().add(replica1);
|
||||
entry.getValue().add(replica2);
|
||||
it.next().getValue().add(replica3); //2nd server
|
||||
|
||||
cluster = new BaseLoadBalancer.Cluster(master, clusterState, null, null, null, null, null);
|
||||
costFunction.init(cluster);
|
||||
double costWith3ReplicasSameServer = costFunction.cost();
|
||||
|
||||
clusterState = mockClusterServers(servers);
|
||||
hri = clusterState.firstEntry().getValue().get(0);
|
||||
replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
|
||||
replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
|
||||
replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
|
||||
|
||||
clusterState.firstEntry().getValue().add(replica1);
|
||||
clusterState.lastEntry().getValue().add(replica2);
|
||||
clusterState.lastEntry().getValue().add(replica3);
|
||||
|
||||
cluster = new BaseLoadBalancer.Cluster(master, clusterState, null, null, null, null, null);
|
||||
costFunction.init(cluster);
|
||||
double costWith2ReplicasOnTwoServers = costFunction.cost();
|
||||
|
||||
assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testSmallCluster() {
|
||||
int numNodes = 10;
|
||||
int numRegions = 1000;
|
||||
int numRegionsPerServer = 40; //all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, true);
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
|
@ -289,8 +398,9 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 20;
|
||||
int numRegions = 2000;
|
||||
int numRegionsPerServer = 40; //all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, true);
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
|
@ -298,8 +408,10 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 20;
|
||||
int numRegions = 2000;
|
||||
int numRegionsPerServer = 1; // all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, false /* max moves */);
|
||||
/* fails because of max moves */
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, false, false);
|
||||
}
|
||||
|
||||
@Test (timeout = 800000)
|
||||
|
@ -307,8 +419,9 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 100;
|
||||
int numRegions = 10000;
|
||||
int numRegionsPerServer = 60; // all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 40;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, true);
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 800000)
|
||||
|
@ -316,12 +429,15 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 200;
|
||||
int numRegions = 100000;
|
||||
int numRegionsPerServer = 40; // all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 400;
|
||||
testWithCluster(numNodes,
|
||||
numRegions,
|
||||
numRegionsPerServer,
|
||||
replication,
|
||||
numTables,
|
||||
false /* num large num regions means may not always get to best balance with one run */);
|
||||
false, /* num large num regions means may not always get to best balance with one run */
|
||||
false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -330,8 +446,9 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numNodes = 100;
|
||||
int numRegions = 2000;
|
||||
int numRegionsPerServer = 9; // all servers except one
|
||||
int replication = 1;
|
||||
int numTables = 110;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, true);
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
// TODO(eclark): Make sure that the tables are well distributed.
|
||||
}
|
||||
|
||||
|
@ -341,20 +458,145 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
int numRegions = 100000; //100 regions per RS
|
||||
int numRegionsPerServer = 80; //all servers except one
|
||||
int numTables = 100;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, numTables, true);
|
||||
int replication = 1;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicasOnSmallCluster() {
|
||||
int numNodes = 10;
|
||||
int numRegions = 1000;
|
||||
int replication = 3; // 3 replicas per region
|
||||
int numRegionsPerServer = 80; //all regions are mostly balanced
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicasOnMidCluster() {
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
loadBalancer.setConf(conf);
|
||||
int numNodes = 200;
|
||||
int numRegions = 40 * 200;
|
||||
int replication = 3; // 3 replicas per region
|
||||
int numRegionsPerServer = 30; //all regions are mostly balanced
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicasOnLargeCluster() {
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
loadBalancer.setConf(conf);
|
||||
int numNodes = 1000;
|
||||
int numRegions = 40 * numNodes; //40 regions per RS
|
||||
int numRegionsPerServer = 30; //all servers except one
|
||||
int numTables = 100;
|
||||
int replication = 3;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicasOnMidClusterHighReplication() {
|
||||
conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 2000000L);
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
loadBalancer.setConf(conf);
|
||||
int numNodes = 100;
|
||||
int numRegions = 6 * 100;
|
||||
int replication = 100; // 100 replicas per region, one for each server
|
||||
int numRegionsPerServer = 5;
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicationOnMidClusterSameHosts() {
|
||||
conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 2000000L);
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
loadBalancer.setConf(conf);
|
||||
int numHosts = 100;
|
||||
int numRegions = 100 * 100;
|
||||
int replication = 3; // 3 replicas per region
|
||||
int numRegionsPerServer = 5;
|
||||
int numTables = 10;
|
||||
Map<ServerName, List<HRegionInfo>> serverMap =
|
||||
createServerMap(numHosts, numRegions, numRegionsPerServer, replication, numTables);
|
||||
int numNodesPerHost = 4;
|
||||
|
||||
// create a new map with 4 RS per host.
|
||||
Map<ServerName, List<HRegionInfo>> newServerMap = new TreeMap<ServerName, List<HRegionInfo>>(serverMap);
|
||||
for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
|
||||
for (int i=1; i < numNodesPerHost; i++) {
|
||||
ServerName s1 = entry.getKey();
|
||||
ServerName s2 = ServerName.valueOf(s1.getHostname(), s1.getPort() + i, 1); // create an RS for the same host
|
||||
newServerMap.put(s2, new ArrayList<HRegionInfo>());
|
||||
}
|
||||
}
|
||||
|
||||
testWithCluster(newServerMap, null, true, true);
|
||||
}
|
||||
|
||||
private static class ForTestRackManager extends RackManager {
|
||||
int numRacks;
|
||||
public ForTestRackManager(int numRacks) {
|
||||
this.numRacks = numRacks;
|
||||
}
|
||||
@Override
|
||||
public String getRack(ServerName server) {
|
||||
return "rack_" + (server.hashCode() % numRacks);
|
||||
}
|
||||
}
|
||||
|
||||
@Test (timeout = 120000)
|
||||
public void testRegionReplicationOnMidClusterWithRacks() {
|
||||
conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 4000000L);
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 60 * 1000); // 60 sec
|
||||
loadBalancer.setConf(conf);
|
||||
int numNodes = 50;
|
||||
int numRegions = numNodes * 30;
|
||||
int replication = 3; // 3 replicas per region
|
||||
int numRegionsPerServer = 25;
|
||||
int numTables = 10;
|
||||
int numRacks = 4; // all replicas should be on a different rack
|
||||
Map<ServerName, List<HRegionInfo>> serverMap =
|
||||
createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
|
||||
RackManager rm = new ForTestRackManager(numRacks);
|
||||
|
||||
testWithCluster(serverMap, rm, true, true);
|
||||
}
|
||||
|
||||
@Test (timeout = 60000)
|
||||
public void testRegionReplicationOnMidClusterReplicationGreaterThanNumNodes() {
|
||||
conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 2000000L);
|
||||
conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
|
||||
loadBalancer.setConf(conf);
|
||||
int numNodes = 80;
|
||||
int numRegions = 6 * 100;
|
||||
int replication = 100; // 100 replicas per region, more than numNodes
|
||||
int numRegionsPerServer = 5;
|
||||
int numTables = 10;
|
||||
testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, false);
|
||||
}
|
||||
|
||||
protected void testWithCluster(int numNodes,
|
||||
int numRegions,
|
||||
int numRegionsPerServer,
|
||||
int numTables,
|
||||
boolean assertFullyBalanced) {
|
||||
int numRegions,
|
||||
int numRegionsPerServer,
|
||||
int replication,
|
||||
int numTables,
|
||||
boolean assertFullyBalanced, boolean assertFullyBalancedForReplicas) {
|
||||
Map<ServerName, List<HRegionInfo>> serverMap =
|
||||
createServerMap(numNodes, numRegions, numRegionsPerServer, numTables);
|
||||
createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
|
||||
testWithCluster(serverMap, null, assertFullyBalanced, assertFullyBalancedForReplicas);
|
||||
}
|
||||
|
||||
|
||||
protected void testWithCluster(Map<ServerName, List<HRegionInfo>> serverMap,
|
||||
RackManager rackManager, boolean assertFullyBalanced, boolean assertFullyBalancedForReplicas) {
|
||||
List<ServerAndLoad> list = convertToList(serverMap);
|
||||
LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list));
|
||||
|
||||
loadBalancer.setRackManager(rackManager);
|
||||
// Run the balancer.
|
||||
List<RegionPlan> plans = loadBalancer.balanceCluster(serverMap);
|
||||
assertNotNull(plans);
|
||||
|
@ -369,12 +611,16 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
assertClusterAsBalanced(balancedCluster);
|
||||
List<RegionPlan> secondPlans = loadBalancer.balanceCluster(serverMap);
|
||||
assertNull(secondPlans);
|
||||
if (assertFullyBalancedForReplicas) {
|
||||
assertRegionReplicaPlacement(serverMap, rackManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<ServerName, List<HRegionInfo>> createServerMap(int numNodes,
|
||||
int numRegions,
|
||||
int numRegionsPerServer,
|
||||
int replication,
|
||||
int numTables) {
|
||||
//construct a cluster of numNodes, having a total of numRegions. Each RS will hold
|
||||
//numRegionsPerServer many regions except for the last one, which will host all the
|
||||
|
@ -384,6 +630,40 @@ public class TestStochasticLoadBalancer extends BalancerTestBase {
|
|||
cluster[i] = numRegionsPerServer;
|
||||
}
|
||||
cluster[cluster.length - 1] = numRegions - ((cluster.length - 1) * numRegionsPerServer);
|
||||
return mockClusterServers(cluster, numTables);
|
||||
Map<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(cluster, numTables);
|
||||
if (replication > 0) {
|
||||
// replicate the regions to the same servers
|
||||
for (List<HRegionInfo> regions : clusterState.values()) {
|
||||
int length = regions.size();
|
||||
for (int i = 0; i < length; i++) {
|
||||
for (int r = 1; r < replication ; r++) {
|
||||
regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(i), r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clusterState;
|
||||
}
|
||||
|
||||
public static class MyRackResolver implements DNSToSwitchMapping {
|
||||
|
||||
public MyRackResolver(Configuration conf) {}
|
||||
|
||||
@Override
|
||||
public List<String> resolve(List<String> names) {
|
||||
List<String> racks = new ArrayList<String>(names.size());
|
||||
for (int i = 0; i < names.size(); i++) {
|
||||
racks.add(i, NetworkTopology.DEFAULT_RACK);
|
||||
}
|
||||
return racks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadCachedMappings() {}
|
||||
|
||||
@Override
|
||||
public void reloadCachedMappings(List<String> names) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ public class TestRegionReplicas {
|
|||
private void openRegion(HRegionInfo hri) throws Exception {
|
||||
ZKAssign.createNodeOffline(HTU.getZooKeeperWatcher(), hri, getRS().getServerName());
|
||||
// first version is '0'
|
||||
AdminProtos.OpenRegionRequest orr = RequestConverter.buildOpenRegionRequest(getRS().getServerName(), hri, 0, null);
|
||||
AdminProtos.OpenRegionRequest orr = RequestConverter.buildOpenRegionRequest(getRS().getServerName(), hri, 0, null, null);
|
||||
AdminProtos.OpenRegionResponse responseOpen = getRS().getRSRpcServices().openRegion(null, orr);
|
||||
Assert.assertTrue(responseOpen.getOpeningStateCount() == 1);
|
||||
Assert.assertTrue(responseOpen.getOpeningState(0).
|
||||
|
|
Loading…
Reference in New Issue