From 96fefce9c3fe9e300fd7dfc5cba490707352660b Mon Sep 17 00:00:00 2001 From: Duo Zhang Date: Fri, 23 Apr 2021 15:20:27 +0800 Subject: [PATCH] HBASE-25802 Miscellaneous style improvements for load balancer related classes (#3192) Signed-off-by: Yulin Niu --- .../hbase/master/balancer/BaseLoadBalancer.java | 56 ++++----- .../hbase/master/balancer/CandidateGenerator.java | 9 +- .../master/balancer/FavoredStochasticBalancer.java | 8 +- .../master/balancer/RandomCandidateGenerator.java | 34 ++++++ .../balancer/RegionReplicaCandidateGenerator.java | 6 +- .../RegionReplicaRackCandidateGenerator.java | 54 +++++++++ .../hbase/master/balancer/SimpleLoadBalancer.java | 133 +++++++++++++-------- .../master/balancer/StochasticLoadBalancer.java | 59 +-------- .../master/balancer/TestBaseLoadBalancer.java | 39 ++---- ...estStochasticLoadBalancerHeterogeneousCost.java | 2 +- 10 files changed, 228 insertions(+), 172 deletions(-) create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RandomCandidateGenerator.java create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaRackCandidateGenerator.java diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java index 4374de1ed5..ad751adaa3 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/BaseLoadBalancer.java @@ -33,7 +33,6 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.function.Predicate; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.ClusterMetrics; -import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseIOException; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.ServerMetrics; @@ -64,6 +63,8 @@ import org.apache.hbase.thirdparty.com.google.common.collect.Sets; @InterfaceAudience.Private public abstract class BaseLoadBalancer implements LoadBalancer { + private static final Logger LOG = LoggerFactory.getLogger(BaseLoadBalancer.class); + public static final String BALANCER_DECISION_BUFFER_ENABLED = "hbase.master.balancer.decision.buffer.enabled"; public static final boolean DEFAULT_BALANCER_DECISION_BUFFER_ENABLED = false; @@ -71,21 +72,28 @@ public abstract class BaseLoadBalancer implements LoadBalancer { protected static final int MIN_SERVER_BALANCE = 2; private volatile boolean stopped = false; - static final List EMPTY_REGION_LIST = Collections.emptyList(); - - static final Predicate IDLE_SERVER_PREDICATOR + private static final Predicate IDLE_SERVER_PREDICATOR = load -> load.getRegionMetrics().isEmpty(); protected RegionHDFSBlockLocationFinder regionFinder; protected boolean useRegionFinder; protected boolean isByTable = false; + // slop for regions + protected float slop; + // overallSlop to control simpleLoadBalancer's cluster level threshold + protected float overallSlop; + protected Configuration config; + protected RackManager rackManager; + protected MetricsBalancer metricsBalancer = null; + protected ClusterMetrics clusterStatus = null; + protected ServerName masterServerName; + protected MasterServices services; /** * The constructor that uses the basic MetricsBalancer */ protected BaseLoadBalancer() { - metricsBalancer = new MetricsBalancer(); - createRegionFinder(); + this(null); } /** @@ -94,28 +102,8 @@ public abstract class BaseLoadBalancer implements LoadBalancer { */ protected BaseLoadBalancer(MetricsBalancer metricsBalancer) { this.metricsBalancer = (metricsBalancer != null) ? metricsBalancer : new MetricsBalancer(); - createRegionFinder(); - } - - private void createRegionFinder() { - useRegionFinder = config.getBoolean("hbase.master.balancer.uselocality", true); - if (useRegionFinder) { - regionFinder = new RegionHDFSBlockLocationFinder(); - } } - // slop for regions - protected float slop; - // overallSlop to control simpleLoadBalancer's cluster level threshold - protected float overallSlop; - protected Configuration config = HBaseConfiguration.create(); - protected RackManager rackManager; - static final Logger LOG = LoggerFactory.getLogger(BaseLoadBalancer.class); - protected MetricsBalancer metricsBalancer = null; - protected ClusterMetrics clusterStatus = null; - protected ServerName masterServerName; - protected MasterServices services; - @Override public void setConf(Configuration conf) { this.config = conf; @@ -133,7 +121,9 @@ public abstract class BaseLoadBalancer implements LoadBalancer { } this.rackManager = new RackManager(getConf()); + useRegionFinder = config.getBoolean("hbase.master.balancer.uselocality", true); if (useRegionFinder) { + regionFinder = new RegionHDFSBlockLocationFinder(); regionFinder.setConf(conf); } this.isByTable = conf.getBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable); @@ -195,7 +185,9 @@ public abstract class BaseLoadBalancer implements LoadBalancer { } return false; } - if(areSomeRegionReplicasColocated(c)) return true; + if (areSomeRegionReplicasColocated(c)) { + return true; + } if(idleRegionServerExist(c)) { return true; } @@ -248,10 +240,10 @@ public abstract class BaseLoadBalancer implements LoadBalancer { /** * Generates a bulk assignment plan to be used on cluster startup using a * simple round-robin assignment. - *

+ *

* Takes a list of all the regions and all the servers in the cluster and * returns a map of each server to the regions that it should be assigned. - *

+ *

* Currently implemented as a round-robin assignment. Same invariant as load * balancing, all servers holding floor(avg) or ceiling(avg). * @@ -288,7 +280,7 @@ public abstract class BaseLoadBalancer implements LoadBalancer { return assignments; } - protected BalancerClusterState createCluster(List servers, + private BalancerClusterState createCluster(List servers, Collection regions) throws HBaseIOException { boolean hasRegionReplica = false; try { @@ -320,7 +312,7 @@ public abstract class BaseLoadBalancer implements LoadBalancer { for (ServerName server : servers) { if (!clusterState.containsKey(server)) { - clusterState.put(server, EMPTY_REGION_LIST); + clusterState.put(server, Collections.emptyList()); } } return new BalancerClusterState(regions, clusterState, null, this.regionFinder, @@ -615,7 +607,7 @@ public abstract class BaseLoadBalancer implements LoadBalancer { } } - protected Map> getRegionAssignmentsByServer( + private Map> getRegionAssignmentsByServer( Collection regions) { if (this.services != null && this.services.getAssignmentManager() != null) { return this.services.getAssignmentManager().getSnapShotOfAssignment(regions); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/CandidateGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/CandidateGenerator.java index 06b5623d1b..cf43269eb4 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/CandidateGenerator.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/CandidateGenerator.java @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.master.balancer; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.yetus.audience.InterfaceAudience; @@ -46,11 +47,11 @@ abstract class CandidateGenerator { double chanceOfNoSwap) { // Check to see if this is just a move. if (cluster.regionsPerServer[server].length == 0 - || StochasticLoadBalancer.RANDOM.nextFloat() < chanceOfNoSwap) { + || ThreadLocalRandom.current().nextFloat() < chanceOfNoSwap) { // signal a move only. return -1; } - int rand = StochasticLoadBalancer.RANDOM.nextInt(cluster.regionsPerServer[server].length); + int rand = ThreadLocalRandom.current().nextInt(cluster.regionsPerServer[server].length); return cluster.regionsPerServer[server][rand]; } @@ -59,7 +60,7 @@ abstract class CandidateGenerator { return -1; } - return StochasticLoadBalancer.RANDOM.nextInt(cluster.numServers); + return ThreadLocalRandom.current().nextInt(cluster.numServers); } int pickRandomRack(BalancerClusterState cluster) { @@ -67,7 +68,7 @@ abstract class CandidateGenerator { return -1; } - return StochasticLoadBalancer.RANDOM.nextInt(cluster.numRacks); + return ThreadLocalRandom.current().nextInt(cluster.numRacks); } int pickOtherRandomServer(BalancerClusterState cluster, int serverIndex) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredStochasticBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredStochasticBalancer.java index feed66c171..a51b8725d0 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredStochasticBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredStochasticBalancer.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; import org.apache.hadoop.hbase.HBaseIOException; import org.apache.hadoop.hbase.ServerMetrics; import org.apache.hadoop.hbase.ServerName; @@ -275,7 +276,7 @@ public class FavoredStochasticBalancer extends StochasticLoadBalancer implements } } else { // We don't have one/more load, lets just choose a random node - s = RANDOM.nextBoolean() ? secondaryHost : tertiaryHost; + s = ThreadLocalRandom.current().nextBoolean() ? secondaryHost : tertiaryHost; } addRegionToMap(assignmentMapForFavoredNodes, region, s); } else if (secondaryHost != null) { @@ -320,7 +321,7 @@ public class FavoredStochasticBalancer extends StochasticLoadBalancer implements List onlineServers = getOnlineFavoredNodes(servers, favoredNodes); if (onlineServers.size() > 0) { - destination = onlineServers.get(RANDOM.nextInt(onlineServers.size())); + destination = onlineServers.get(ThreadLocalRandom.current().nextInt(onlineServers.size())); } boolean alwaysAssign = getConf().getBoolean(FAVORED_ALWAYS_ASSIGN_REGIONS, true); @@ -398,7 +399,8 @@ public class FavoredStochasticBalancer extends StochasticLoadBalancer implements if (FavoredNodesPlan.getFavoredServerPosition(favoredNodes, sn) != null) { addRegionToMap(assignmentMap, hri, sn); } else { - ServerName destination = onlineFN.get(RANDOM.nextInt(onlineFN.size())); + ServerName destination = + onlineFN.get(ThreadLocalRandom.current().nextInt(onlineFN.size())); LOG.warn("Region: " + hri + " not hosted on favored nodes: " + favoredNodes + " current: " + sn + " moving to: " + destination); addRegionToMap(assignmentMap, hri, destination); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RandomCandidateGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RandomCandidateGenerator.java new file mode 100644 index 0000000000..c2cd998524 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RandomCandidateGenerator.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.balancer; + +import org.apache.yetus.audience.InterfaceAudience; + +@InterfaceAudience.Private +class RandomCandidateGenerator extends CandidateGenerator { + + @Override + BalanceAction generate(BalancerClusterState cluster) { + int thisServer = pickRandomServer(cluster); + + // Pick the other server + int otherServer = pickOtherRandomServer(cluster, thisServer); + + return pickRandomRegions(cluster, thisServer, otherServer); + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaCandidateGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaCandidateGenerator.java index 4badd4d290..f3b69554ac 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaCandidateGenerator.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaCandidateGenerator.java @@ -18,6 +18,7 @@ package org.apache.hadoop.hbase.master.balancer; +import java.util.concurrent.ThreadLocalRandom; import org.apache.yetus.audience.InterfaceAudience; /** @@ -27,8 +28,7 @@ import org.apache.yetus.audience.InterfaceAudience; @InterfaceAudience.Private class RegionReplicaCandidateGenerator extends CandidateGenerator { - StochasticLoadBalancer.RandomCandidateGenerator randomGenerator = - new StochasticLoadBalancer.RandomCandidateGenerator(); + protected final RandomCandidateGenerator randomGenerator = new RandomCandidateGenerator(); /** * Randomly select one regionIndex out of all region replicas co-hosted in the same group @@ -56,7 +56,7 @@ class RegionReplicaCandidateGenerator extends CandidateGenerator { int numReplicas = j - currentPrimaryIndex; if (numReplicas > 1) { // means consecutive primaries, indicating co-location // decide to select this primary region id or not - double currentRandom = StochasticLoadBalancer.RANDOM.nextDouble(); + double currentRandom = ThreadLocalRandom.current().nextDouble(); // 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) if (currentRandom > currentLargestRandom) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaRackCandidateGenerator.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaRackCandidateGenerator.java new file mode 100644 index 0000000000..a67dc1dbef --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionReplicaRackCandidateGenerator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.master.balancer; + +import java.util.concurrent.ThreadLocalRandom; +import org.apache.yetus.audience.InterfaceAudience; + +/** + * Generates candidates which moves the replicas out of the rack for co-hosted region replicas in + * the same rack + */ +@InterfaceAudience.Private +class RegionReplicaRackCandidateGenerator extends RegionReplicaCandidateGenerator { + + @Override + BalanceAction generate(BalancerClusterState cluster) { + int rackIndex = pickRandomRack(cluster); + if (cluster.numRacks <= 1 || rackIndex == -1) { + return super.generate(cluster); + } + + int regionIndex = selectCoHostedRegionPerGroup(cluster.primariesOfRegionsPerRack[rackIndex], + cluster.regionsPerRack[rackIndex], cluster.regionIndexToPrimaryIndex); + + // if there are no pairs of region replicas co-hosted, default to random generator + if (regionIndex == -1) { + // default to randompicker + return randomGenerator.generate(cluster); + } + + int serverIndex = cluster.regionIndexToServerIndex[regionIndex]; + int toRackIndex = pickOtherRandomRack(cluster, rackIndex); + + int rand = ThreadLocalRandom.current().nextInt(cluster.serversPerRack[toRackIndex].length); + int toServerIndex = cluster.serversPerRack[toRackIndex][rand]; + int toRegionIndex = pickRandomRegion(cluster, toServerIndex, 0.9f); + return getAction(serverIndex, regionIndex, toServerIndex, toRegionIndex); + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/SimpleLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/SimpleLoadBalancer.java index 8a9f019a01..2c5947b464 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/SimpleLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/SimpleLoadBalancer.java @@ -25,7 +25,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; -import java.util.Random; import java.util.TreeMap; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseInterfaceAudience; @@ -41,22 +40,20 @@ import org.slf4j.LoggerFactory; import org.apache.hbase.thirdparty.com.google.common.collect.MinMaxPriorityQueue; /** - * Makes decisions about the placement and movement of Regions across - * RegionServers. - * - *

Cluster-wide load balancing will occur only when there are no regions in - * transition and according to a fixed period of a time using {@link #balanceCluster(Map)}. - * - *

On cluster startup, bulk assignment can be used to determine - * locations for all Regions in a cluster. - * - *

This classes produces plans for the + * Makes decisions about the placement and movement of Regions across RegionServers. + *

+ * Cluster-wide load balancing will occur only when there are no regions in transition and according + * to a fixed period of a time using {@link #balanceCluster(Map)}. + *

+ * On cluster startup, bulk assignment can be used to determine locations for all Regions in a + * cluster. + *

+ * This classes produces plans for the * {@link org.apache.hadoop.hbase.master.assignment.AssignmentManager} to execute. */ @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) public class SimpleLoadBalancer extends BaseLoadBalancer { private static final Logger LOG = LoggerFactory.getLogger(SimpleLoadBalancer.class); - private static final Random RANDOM = new Random(System.currentTimeMillis()); private RegionInfoComparator riComparator = new RegionInfoComparator(); private RegionPlan.RegionPlanComparator rpComparator = new RegionPlan.RegionPlanComparator(); @@ -66,12 +63,12 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { /** * Stores additional per-server information about the regions added/removed * during the run of the balancing algorithm. - * + *

* For servers that shed regions, we need to track which regions we have already * shed. nextRegionForUnload contains the index in the list of regions on * the server that is the next to be shed. */ - static class BalanceInfo { + private static final class BalanceInfo { private int nextRegionForUnload; private int numRegionsAdded; @@ -136,8 +133,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { originSlop, slop, originOverallSlop, overallSlop); } - private void setLoad(List slList, int i, int loadChange){ - ServerAndLoad newsl = new ServerAndLoad(slList.get(i).getServerName(),slList.get(i).getLoad() + loadChange); + private void setLoad(List slList, int i, int loadChange) { + ServerAndLoad newsl = + new ServerAndLoad(slList.get(i).getServerName(), slList.get(i).getLoad() + loadChange); slList.set(i, newsl); } @@ -310,7 +308,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { i++; regionsToMove.add(new RegionPlan(hri, sal.getServerName(), null)); numTaken++; - if (numTaken >= numToOffload) break; + if (numTaken >= numToOffload) { + break; + } } serverBalanceInfo.put(sal.getServerName(), new BalanceInfo(numToOffload, (-1)*numTaken, server.getValue())); @@ -325,7 +325,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { int maxToTake = numRegions - min; for (Map.Entry> server: serversByLoad.entrySet()) { - if (maxToTake == 0) break; // no more to take + if (maxToTake == 0) { + break; // no more to take + } int load = server.getKey().getLoad(); if (load >= min) { continue; // look for other servers which haven't reached min @@ -339,15 +341,19 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { int incr = 1; List sns = Arrays.asList(underloadedServers.keySet().toArray(new ServerName[serversUnderloaded])); - Collections.shuffle(sns, RANDOM); + Collections.shuffle(sns); while (regionsToMove.size() > 0) { int cnt = 0; int i = incr > 0 ? 0 : underloadedServers.size()-1; for (; i >= 0 && i < underloadedServers.size(); i += incr) { - if (regionsToMove.isEmpty()) break; + if (regionsToMove.isEmpty()) { + break; + } ServerName si = sns.get(i); int numToTake = underloadedServers.get(si); - if (numToTake == 0) continue; + if (numToTake == 0) { + continue; + } addRegionPlan(regionsToMove, fetchFromTail, si, regionsToReturn); @@ -356,7 +362,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { BalanceInfo bi = serverBalanceInfo.get(si); bi.setNumRegionsAdded(bi.getNumRegionsAdded()+1); } - if (cnt == 0) break; + if (cnt == 0) { + break; + } // iterates underloadedServers in the other direction incr = -incr; } @@ -377,9 +385,13 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { serverBalanceInfo.get(server.getKey().getServerName()); int idx = balanceInfo == null ? 0 : balanceInfo.getNextRegionForUnload(); - if (idx >= server.getValue().size()) break; + if (idx >= server.getValue().size()) { + break; + } RegionInfo region = server.getValue().get(idx); - if (region.isMetaRegion()) continue; // Don't move meta regions. + if (region.isMetaRegion()) { + continue; // Don't move meta regions. + } regionsToMove.add(new RegionPlan(region, server.getKey().getServerName(), null)); balanceInfo.setNumRegionsAdded(balanceInfo.getNumRegionsAdded() - 1); balanceInfo.setNextRegionForUnload(balanceInfo.getNextRegionForUnload() + 1); @@ -398,7 +410,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { for (Map.Entry> server : serversByLoad.entrySet()) { int regionCount = server.getKey().getLoad(); - if (regionCount >= min) break; + if (regionCount >= min) { + break; + } BalanceInfo balanceInfo = serverBalanceInfo.get(server.getKey().getServerName()); if(balanceInfo != null) { regionCount += balanceInfo.getNumRegionsAdded(); @@ -429,7 +443,9 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { ", serversUnderloaded=" + serversUnderloaded); StringBuilder sb = new StringBuilder(); for (Map.Entry> e: loadOfOneTable.entrySet()) { - if (sb.length() > 0) sb.append(", "); + if (sb.length() > 0) { + sb.append(", "); + } sb.append(e.getKey().toString()); sb.append(" "); sb.append(e.getValue().size()); @@ -447,15 +463,16 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { } /** - * If we need to balanceoverall, we need to add one more round to peel off one region from each max. - * Together with other regions left to be assigned, we distribute all regionToMove, to the RS + * If we need to balanceoverall, we need to add one more round to peel off one region from each + * max. Together with other regions left to be assigned, we distribute all regionToMove, to the RS * that have less regions in whole cluster scope. */ public void balanceOverall(List regionsToReturn, - Map serverBalanceInfo, boolean fetchFromTail, - MinMaxPriorityQueue regionsToMove, int max, int min ){ + Map serverBalanceInfo, boolean fetchFromTail, + MinMaxPriorityQueue regionsToMove, int max, int min) { // Step 1. - // A map to record the plan we have already got as status quo, in order to resolve a cyclic assignment pair, + // A map to record the plan we have already got as status quo, in order to resolve a cyclic + // assignment pair, // e.g. plan 1: A -> B, plan 2: B ->C => resolve plan1 to A -> C, remove plan2 Map> returnMap = new HashMap<>(); for (int i = 0; i < regionsToReturn.size(); i++) { @@ -491,12 +508,14 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { RegionPlan maxPlan = new RegionPlan(hriToPlan, serverload.getServerName(), null); regionsToMove.add(maxPlan); setLoad(serverLoadList, i, -1); - }else if(balanceInfo.getHriList().size() + balanceInfo.getNumRegionsAdded() > max - || balanceInfo.getHriList().size() + balanceInfo.getNumRegionsAdded() < min){ - LOG.warn("Encounter incorrect region numbers after calculating move plan during balanceOverall, " + - "for this table, " + serverload.getServerName() + " originally has " + balanceInfo.getHriList().size() + - " regions and " + balanceInfo.getNumRegionsAdded() + " regions have been added. Yet, max =" + - max + ", min =" + min + ". Thus stop balance for this table"); // should not happen + } else if (balanceInfo.getHriList().size() + balanceInfo.getNumRegionsAdded() > max || + balanceInfo.getHriList().size() + balanceInfo.getNumRegionsAdded() < min) { + LOG.warn( + "Encounter incorrect region numbers after calculating move plan during balanceOverall, " + + "for this table, " + serverload.getServerName() + " originally has " + + balanceInfo.getHriList().size() + " regions and " + balanceInfo.getNumRegionsAdded() + + " regions have been added. Yet, max =" + max + ", min =" + min + + ". Thus stop balance for this table"); // should not happen return; } } @@ -504,12 +523,16 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { // Step 3. sort the ServerLoadList, the ArrayList hold overall load for each server. // We only need to assign the regionsToMove to // the first n = regionsToMove.size() RS that has least load. - Collections.sort(serverLoadList,new Comparator(){ + Collections.sort(serverLoadList, new Comparator() { @Override public int compare(ServerAndLoad s1, ServerAndLoad s2) { - if(s1.getLoad() == s2.getLoad()) return 0; - else return (s1.getLoad() > s2.getLoad())? 1 : -1; - }}); + if (s1.getLoad() == s2.getLoad()) { + return 0; + } else { + return (s1.getLoad() > s2.getLoad()) ? 1 : -1; + } + } + }); // Step 4. // Preparation before assign out all regionsToMove. @@ -524,16 +547,19 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { Pair shredLoad; // A List to help mark the plan in regionsToMove that should be removed List planToRemoveList = new ArrayList<>(); - // A structure to record how many times a server becomes the source of a plan, from regionsToMove. + // A structure to record how many times a server becomes the source of a plan, from + // regionsToMove. Map sourceMap = new HashMap<>(); // We remove one of the plan which would cause source RS equals destination RS. // But we should keep in mind that the second plan from such RS should be kept. for(RegionPlan plan: regionsToMove){ // the source RS's load and index in ServerLoadList shredLoad = SnLoadMap.get(plan.getSource()); - if(!sourceMap.containsKey(plan.getSource())) sourceMap.put(plan.getSource(), 0); + if (!sourceMap.containsKey(plan.getSource())) { + sourceMap.put(plan.getSource(), 0); + } sourceMap.put(plan.getSource(), sourceMap.get(plan.getSource()) + 1); - if(shredLoad.getSecond() < assignLength && sourceMap.get(plan.getSource()) == 1) { + if (shredLoad.getSecond() < assignLength && sourceMap.get(plan.getSource()) == 1) { planToRemoveList.add(plan); // While marked as to be removed, the count should be add back to the source RS setLoad(serverLoadList, shredLoad.getSecond(), 1); @@ -552,14 +578,18 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { // With this strategy adopted, we can gradually achieve the overall balance, // while keeping table level balanced. for(int i = 0; i < assignLength; i++){ - // skip the RS that is also the source, we have removed them from regionsToMove in previous step - if(sourceMap.containsKey(serverLoadList.get(i).getServerName())) continue; + // skip the RS that is also the source, we have removed them from regionsToMove in previous + // step + if (sourceMap.containsKey(serverLoadList.get(i).getServerName())) { + continue; + } addRegionPlan(regionsToMove, fetchFromTail, serverLoadList.get(i).getServerName(), regionsToReturn); setLoad(serverLoadList, i, 1); // resolve a possible cyclic assignment pair if we just produced one: // e.g. plan1: A -> B, plan2: B -> C => resolve plan1 to A -> C and remove plan2 - List pos = returnMap.get(regionsToReturn.get(regionsToReturn.size() - 1).getSource()); + List pos = + returnMap.get(regionsToReturn.get(regionsToReturn.size() - 1).getSource()); if (pos != null && pos.size() != 0) { regionsToReturn.get(pos.get(pos.size() - 1)).setDestination( regionsToReturn.get(regionsToReturn.size() - 1).getDestination()); @@ -574,10 +604,13 @@ public class SimpleLoadBalancer extends BaseLoadBalancer { * Add a region from the head or tail to the List of regions to return. */ private void addRegionPlan(final MinMaxPriorityQueue regionsToMove, - final boolean fetchFromTail, final ServerName sn, List regionsToReturn) { + final boolean fetchFromTail, final ServerName sn, List regionsToReturn) { RegionPlan rp = null; - if (!fetchFromTail) rp = regionsToMove.remove(); - else rp = regionsToMove.removeLast(); + if (!fetchFromTail) { + rp = regionsToMove.remove(); + } else { + rp = regionsToMove.removeLast(); + } rp.setDestination(sn); regionsToReturn.add(rp); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java index c0ab4d9e9a..85cb1d8ece 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/StochasticLoadBalancer.java @@ -27,7 +27,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.ClusterMetrics; @@ -106,6 +106,8 @@ import org.apache.hbase.thirdparty.com.google.common.collect.Lists; justification="Complaint is about costFunctions not being synchronized; not end of the world") public class StochasticLoadBalancer extends BaseLoadBalancer { + private static final Logger LOG = LoggerFactory.getLogger(StochasticLoadBalancer.class); + protected static final String STEPS_PER_REGION_KEY = "hbase.master.balancer.stochastic.stepsPerRegion"; protected static final String MAX_STEPS_KEY = @@ -122,9 +124,6 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { protected static final String COST_FUNCTIONS_COST_FUNCTIONS_KEY = "hbase.master.balancer.stochastic.additionalCostFunctions"; - protected static final Random RANDOM = new Random(System.currentTimeMillis()); - private static final Logger LOG = LoggerFactory.getLogger(StochasticLoadBalancer.class); - Map> loads = new HashMap<>(); // values are defaults @@ -365,8 +364,8 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { } BalanceAction nextAction(BalancerClusterState cluster) { - return candidateGenerators.get(RANDOM.nextInt(candidateGenerators.size())) - .generate(cluster); + return candidateGenerators.get(ThreadLocalRandom.current().nextInt(candidateGenerators.size())) + .generate(cluster); } /** @@ -683,53 +682,6 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { return total; } - static class RandomCandidateGenerator extends CandidateGenerator { - - @Override - BalanceAction generate(BalancerClusterState cluster) { - - int thisServer = pickRandomServer(cluster); - - // Pick the other server - int otherServer = pickOtherRandomServer(cluster, thisServer); - - return pickRandomRegions(cluster, thisServer, otherServer); - } - } - - /** - * Generates candidates which moves the replicas out of the rack for - * co-hosted region replicas in the same rack - */ - static class RegionReplicaRackCandidateGenerator extends RegionReplicaCandidateGenerator { - @Override - BalanceAction generate(BalancerClusterState cluster) { - int rackIndex = pickRandomRack(cluster); - if (cluster.numRacks <= 1 || rackIndex == -1) { - return super.generate(cluster); - } - - int regionIndex = selectCoHostedRegionPerGroup( - cluster.primariesOfRegionsPerRack[rackIndex], - cluster.regionsPerRack[rackIndex], - cluster.regionIndexToPrimaryIndex); - - // if there are no pairs of region replicas co-hosted, default to random generator - if (regionIndex == -1) { - // default to randompicker - return randomGenerator.generate(cluster); - } - - int serverIndex = cluster.regionIndexToServerIndex[regionIndex]; - int toRackIndex = pickOtherRandomRack(cluster, rackIndex); - - int rand = RANDOM.nextInt(cluster.serversPerRack[toRackIndex].length); - int toServerIndex = cluster.serversPerRack[toRackIndex][rand]; - int toRegionIndex = pickRandomRegion(cluster, toServerIndex, 0.9f); - return getAction(serverIndex, regionIndex, toServerIndex, toRegionIndex); - } - } - /** * Base class of StochasticLoadBalancer's Cost Functions. */ @@ -745,6 +697,7 @@ public class StochasticLoadBalancer extends BaseLoadBalancer { boolean isNeeded() { return true; } + float getMultiplier() { return multiplier; } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java index 98c0bddb1c..959300abfd 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestBaseLoadBalancer.java @@ -20,6 +20,8 @@ package org.apache.hadoop.hbase.master.balancer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -57,7 +59,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TestName; -import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,22 +94,22 @@ public class TestBaseLoadBalancer extends BalancerTestBase { conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); loadBalancer = new MockBalancer(); loadBalancer.setConf(conf); - MasterServices st = Mockito.mock(MasterServices.class); - Mockito.when(st.getServerName()).thenReturn(master); + MasterServices st = mock(MasterServices.class); + when(st.getServerName()).thenReturn(master); loadBalancer.setMasterServices(st); // Set up the rack topologies (5 machines per rack) - rackManager = Mockito.mock(RackManager.class); + rackManager = 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"); + when(rackManager.getRack(servers[i])).thenReturn("rack1"); } if (i >= 5 && i < 10) { - Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack2"); + when(rackManager.getRack(servers[i])).thenReturn("rack2"); } if (i >= 10) { - Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack3"); + when(rackManager.getRack(servers[i])).thenReturn("rack3"); } } } @@ -128,19 +129,6 @@ public class TestBaseLoadBalancer extends BalancerTestBase { } /** - * All regions have an assignment. - * @param regions - * @param servers - * @param assignments - */ - private void assertImmediateAssignment(List regions, List servers, - Map assignments) { - for (RegionInfo region : regions) { - assertTrue(assignments.containsKey(region)); - } - } - - /** * Tests the bulk assignment used during cluster startup. * * Round-robin. Should yield a balanced cluster so same invariant as the load @@ -242,11 +230,10 @@ public class TestBaseLoadBalancer extends BalancerTestBase { Configuration conf = HBaseConfiguration.create(); conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class); balancer.setConf(conf); - ServerManager sm = Mockito.mock(ServerManager.class); - Mockito.when(sm.getOnlineServersListWithPredicator(allServers, BaseLoadBalancer.IDLE_SERVER_PREDICATOR)) - .thenReturn(idleServers); - MasterServices services = Mockito.mock(MasterServices.class); - Mockito.when(services.getServerManager()).thenReturn(sm); + ServerManager sm = mock(ServerManager.class); + when(sm.getOnlineServersListWithPredicator(anyList(), any())).thenReturn(idleServers); + MasterServices services = mock(MasterServices.class); + when(services.getServerManager()).thenReturn(sm); balancer.setMasterServices(services); RegionInfo hri1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) .setStartKey(Bytes.toBytes("key1")) @@ -254,7 +241,7 @@ public class TestBaseLoadBalancer extends BalancerTestBase { .setSplit(false) .setRegionId(100) .build(); - assertNull(balancer.randomAssignment(hri1, Collections.EMPTY_LIST)); + assertNull(balancer.randomAssignment(hri1, Collections.emptyList())); assertNull(balancer.randomAssignment(hri1, null)); for (int i = 0; i != 3; ++i) { ServerName sn = balancer.randomAssignment(hri1, allServers); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestStochasticLoadBalancerHeterogeneousCost.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestStochasticLoadBalancerHeterogeneousCost.java index 8a2ec848a7..8ea71183b8 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestStochasticLoadBalancerHeterogeneousCost.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/balancer/TestStochasticLoadBalancerHeterogeneousCost.java @@ -282,7 +282,7 @@ public class TestStochasticLoadBalancerHeterogeneousCost extends BalancerTestBas } static class FairRandomCandidateGenerator extends - StochasticLoadBalancer.RandomCandidateGenerator { + RandomCandidateGenerator { @Override public BalanceAction pickRandomRegions(BalancerClusterState cluster, -- 2.11.4.GIT