1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.master.balancer;
19
20 import com.google.common.annotations.VisibleForTesting;
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Deque;
27 import java.util.HashMap;
28 import java.util.LinkedList;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.Random;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.hbase.classification.InterfaceAudience;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.ClusterStatus;
39 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
40 import org.apache.hadoop.hbase.HConstants;
41 import org.apache.hadoop.hbase.HRegionInfo;
42 import org.apache.hadoop.hbase.RegionLoad;
43 import org.apache.hadoop.hbase.ServerLoad;
44 import org.apache.hadoop.hbase.ServerName;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.master.MasterServices;
47 import org.apache.hadoop.hbase.master.RegionPlan;
48 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action;
49 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action.Type;
50 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.AssignRegionAction;
51 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.LocalityType;
52 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.MoveRegionAction;
53 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.SwapRegionsAction;
54 import org.apache.hadoop.hbase.util.Bytes;
55 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
56
57 import com.google.common.base.Optional;
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
103 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
104 justification="Complaint is about costFunctions not being synchronized; not end of the world")
105 public class StochasticLoadBalancer extends BaseLoadBalancer {
106
107 protected static final String STEPS_PER_REGION_KEY =
108 "hbase.master.balancer.stochastic.stepsPerRegion";
109 protected static final String MAX_STEPS_KEY =
110 "hbase.master.balancer.stochastic.maxSteps";
111 protected static final String MAX_RUNNING_TIME_KEY =
112 "hbase.master.balancer.stochastic.maxRunningTime";
113 protected static final String KEEP_REGION_LOADS =
114 "hbase.master.balancer.stochastic.numRegionLoadsToRemember";
115 private static final String TABLE_FUNCTION_SEP = "_";
116 protected static final String MIN_COST_NEED_BALANCE_KEY =
117 "hbase.master.balancer.stochastic.minCostNeedBalance";
118
119 private static final Random RANDOM = new Random(System.currentTimeMillis());
120 private static final Log LOG = LogFactory.getLog(StochasticLoadBalancer.class);
121
122 Map<String, Deque<RegionLoad>> loads = new HashMap<String, Deque<RegionLoad>>();
123
124
125 private int maxSteps = 1000000;
126 private int stepsPerRegion = 800;
127 private long maxRunningTime = 30 * 1000 * 1;
128 private int numRegionLoadsToRemember = 15;
129 private float minCostNeedBalance = 0.05f;
130
131 private CandidateGenerator[] candidateGenerators;
132 private CostFromRegionLoadFunction[] regionLoadFunctions;
133 private CostFunction[] costFunctions;
134
135
136 private Double curOverallCost = 0d;
137 private Double[] tempFunctionCosts;
138 private Double[] curFunctionCosts;
139
140
141
142 private LocalityBasedCandidateGenerator localityCandidateGenerator;
143 private ServerLocalityCostFunction localityCost;
144 private RackLocalityCostFunction rackLocalityCost;
145 private RegionReplicaHostCostFunction regionReplicaHostCostFunction;
146 private RegionReplicaRackCostFunction regionReplicaRackCostFunction;
147 private boolean isByTable = false;
148 private TableName tableName = null;
149
150
151
152
153
154 public StochasticLoadBalancer() {
155 super(new MetricsStochasticBalancer());
156 }
157
158 @Override
159 public void onConfigurationChange(Configuration conf) {
160 setConf(conf);
161 }
162
163 @Override
164 public synchronized void setConf(Configuration conf) {
165 super.setConf(conf);
166 LOG.info("loading config");
167
168 maxSteps = conf.getInt(MAX_STEPS_KEY, maxSteps);
169
170 stepsPerRegion = conf.getInt(STEPS_PER_REGION_KEY, stepsPerRegion);
171 maxRunningTime = conf.getLong(MAX_RUNNING_TIME_KEY, maxRunningTime);
172
173 numRegionLoadsToRemember = conf.getInt(KEEP_REGION_LOADS, numRegionLoadsToRemember);
174 isByTable = conf.getBoolean(HConstants.HBASE_MASTER_LOADBALANCE_BYTABLE, isByTable);
175
176 minCostNeedBalance = conf.getFloat(MIN_COST_NEED_BALANCE_KEY, minCostNeedBalance);
177
178 if (localityCandidateGenerator == null) {
179 localityCandidateGenerator = new LocalityBasedCandidateGenerator(services);
180 }
181 localityCost = new ServerLocalityCostFunction(conf, services);
182 rackLocalityCost = new RackLocalityCostFunction(conf, services);
183
184 if (candidateGenerators == null) {
185 candidateGenerators = new CandidateGenerator[] {
186 new RandomCandidateGenerator(),
187 new LoadCandidateGenerator(),
188 localityCandidateGenerator,
189 new RegionReplicaRackCandidateGenerator(),
190 };
191 }
192
193 regionLoadFunctions = new CostFromRegionLoadFunction[] {
194 new ReadRequestCostFunction(conf),
195 new WriteRequestCostFunction(conf),
196 new MemstoreSizeCostFunction(conf),
197 new StoreFileCostFunction(conf)
198 };
199
200 regionReplicaHostCostFunction = new RegionReplicaHostCostFunction(conf);
201 regionReplicaRackCostFunction = new RegionReplicaRackCostFunction(conf);
202
203 costFunctions = new CostFunction[]{
204 new RegionCountSkewCostFunction(conf),
205 new PrimaryRegionCountSkewCostFunction(conf),
206 new MoveCostFunction(conf),
207 localityCost,
208 rackLocalityCost,
209 new TableSkewCostFunction(conf),
210 regionReplicaHostCostFunction,
211 regionReplicaRackCostFunction,
212 regionLoadFunctions[0],
213 regionLoadFunctions[1],
214 regionLoadFunctions[2],
215 regionLoadFunctions[3],
216 };
217
218 curFunctionCosts= new Double[costFunctions.length];
219 tempFunctionCosts= new Double[costFunctions.length];
220
221 }
222
223 @Override
224 protected void setSlop(Configuration conf) {
225 this.slop = conf.getFloat("hbase.regions.slop", 0.001F);
226 }
227
228 @Override
229 public synchronized void setClusterStatus(ClusterStatus st) {
230 super.setClusterStatus(st);
231 updateRegionLoad();
232 for(CostFromRegionLoadFunction cost : regionLoadFunctions) {
233 cost.setClusterStatus(st);
234 }
235
236
237 try {
238
239 int tablesCount = isByTable ? services.getTableDescriptors().getAll().size() : 1;
240 int functionsCount = getCostFunctionNames().length;
241
242 updateMetricsSize(tablesCount * (functionsCount + 1));
243 } catch (Exception e) {
244 LOG.error("failed to get the size of all tables", e);
245 }
246 }
247
248
249
250
251 public void updateMetricsSize(int size) {
252 if (metricsBalancer instanceof MetricsStochasticBalancer) {
253 ((MetricsStochasticBalancer) metricsBalancer).updateMetricsSize(size);
254 }
255 }
256
257 @Override
258 public synchronized void setMasterServices(MasterServices masterServices) {
259 super.setMasterServices(masterServices);
260 this.localityCost.setServices(masterServices);
261 this.rackLocalityCost.setServices(masterServices);
262 this.localityCandidateGenerator.setServices(masterServices);
263 }
264
265 @Override
266 protected synchronized boolean areSomeRegionReplicasColocated(Cluster c) {
267 regionReplicaHostCostFunction.init(c);
268 if (regionReplicaHostCostFunction.cost() > 0) return true;
269 regionReplicaRackCostFunction.init(c);
270 if (regionReplicaRackCostFunction.cost() > 0) return true;
271 return false;
272 }
273
274 @Override
275 protected boolean needsBalance(Cluster cluster) {
276 ClusterLoadState cs = new ClusterLoadState(cluster.clusterState);
277 if (cs.getNumServers() < MIN_SERVER_BALANCE) {
278 if (LOG.isDebugEnabled()) {
279 LOG.debug("Not running balancer because only " + cs.getNumServers()
280 + " active regionserver(s)");
281 }
282 return false;
283 }
284 if (areSomeRegionReplicasColocated(cluster)) {
285 return true;
286 }
287
288 double total = 0.0;
289 float sumMultiplier = 0.0f;
290 for (CostFunction c : costFunctions) {
291 float multiplier = c.getMultiplier();
292 if (multiplier <= 0) {
293 continue;
294 }
295 if (!c.isNeeded()) {
296 LOG.debug(c.getClass().getName() + " indicated that its cost should not be considered");
297 continue;
298 }
299 sumMultiplier += multiplier;
300 total += c.cost() * multiplier;
301 }
302
303 if (total <= 0 || sumMultiplier <= 0
304 || (sumMultiplier > 0 && (total / sumMultiplier) < minCostNeedBalance)) {
305 LOG.info("Skipping load balancing because balanced cluster; " + "total cost is " + total
306 + ", sum multiplier is " + sumMultiplier + " min cost which need balance is "
307 + minCostNeedBalance);
308 return false;
309 }
310 return true;
311 }
312
313 @Override
314 public synchronized List<RegionPlan> balanceCluster(TableName tableName, Map<ServerName,
315 List<HRegionInfo>> clusterState) {
316 this.tableName = tableName;
317 return balanceCluster(clusterState);
318 }
319
320 @VisibleForTesting
321 Cluster.Action nextAction(Cluster cluster) {
322 return candidateGenerators[(RANDOM.nextInt(candidateGenerators.length))]
323 .generate(cluster);
324 }
325
326
327
328
329
330 @Override
331 public synchronized List<RegionPlan> balanceCluster(Map<ServerName,
332 List<HRegionInfo>> clusterState) {
333 List<RegionPlan> plans = balanceMasterRegions(clusterState);
334 if (plans != null || clusterState == null || clusterState.size() <= 1) {
335 return plans;
336 }
337
338 if (masterServerName != null && clusterState.containsKey(masterServerName)) {
339 if (clusterState.size() <= 2) {
340 return null;
341 }
342 clusterState = new HashMap<ServerName, List<HRegionInfo>>(clusterState);
343 clusterState.remove(masterServerName);
344 }
345
346
347
348
349
350 RegionLocationFinder finder = null;
351 if ((this.localityCost != null && this.localityCost.getMultiplier() > 0)
352 || (this.rackLocalityCost != null && this.rackLocalityCost.getMultiplier() > 0)) {
353 finder = this.regionFinder;
354 }
355
356
357
358
359 Cluster cluster = new Cluster(clusterState, loads, finder, rackManager);
360
361 long startTime = EnvironmentEdgeManager.currentTime();
362
363 initCosts(cluster);
364
365 if (!needsBalance(cluster)) {
366 return null;
367 }
368
369 double currentCost = computeCost(cluster, Double.MAX_VALUE);
370 curOverallCost = currentCost;
371 for (int i = 0; i < this.curFunctionCosts.length; i++) {
372 curFunctionCosts[i] = tempFunctionCosts[i];
373 }
374 LOG.info("start StochasticLoadBalancer.balancer, initCost=" + currentCost + ", functionCost="
375 + functionCost());
376
377 double initCost = currentCost;
378 double newCost = currentCost;
379
380 long computedMaxSteps = Math.min(this.maxSteps,
381 ((long)cluster.numRegions * (long)this.stepsPerRegion * (long)cluster.numServers));
382
383 long step;
384
385 for (step = 0; step < computedMaxSteps; step++) {
386 Cluster.Action action = nextAction(cluster);
387
388 if (action.type == Type.NULL) {
389 continue;
390 }
391
392 cluster.doAction(action);
393 updateCostsWithAction(cluster, action);
394
395 newCost = computeCost(cluster, currentCost);
396
397
398 if (newCost < currentCost) {
399 currentCost = newCost;
400
401
402 curOverallCost = currentCost;
403 for (int i = 0; i < this.curFunctionCosts.length; i++) {
404 curFunctionCosts[i] = tempFunctionCosts[i];
405 }
406 } else {
407
408
409 Action undoAction = action.undoAction();
410 cluster.doAction(undoAction);
411 updateCostsWithAction(cluster, undoAction);
412 }
413
414 if (EnvironmentEdgeManager.currentTime() - startTime >
415 maxRunningTime) {
416 break;
417 }
418 }
419 long endTime = EnvironmentEdgeManager.currentTime();
420
421 metricsBalancer.balanceCluster(endTime - startTime);
422
423
424 updateStochasticCosts(tableName, curOverallCost, curFunctionCosts);
425 if (initCost > currentCost) {
426 plans = createRegionPlans(cluster);
427 if (LOG.isDebugEnabled()) {
428 LOG.debug("Finished computing new load balance plan. Computation took "
429 + (endTime - startTime) + "ms to try " + step
430 + " different iterations. Found a solution that moves "
431 + plans.size() + " regions; Going from a computed cost of "
432 + initCost + " to a new cost of " + currentCost);
433 }
434
435 return plans;
436 }
437 if (LOG.isDebugEnabled()) {
438 LOG.debug("Could not find a better load balance plan. Tried "
439 + step + " different configurations in " + (endTime - startTime)
440 + "ms, and did not find anything with a computed cost less than " + initCost);
441 }
442 return null;
443 }
444
445
446
447
448 private void updateStochasticCosts(TableName tableName, Double overall, Double[] subCosts) {
449 if (tableName == null) return;
450
451
452 if (metricsBalancer instanceof MetricsStochasticBalancer) {
453 MetricsStochasticBalancer balancer = (MetricsStochasticBalancer) metricsBalancer;
454
455 balancer.updateStochasticCost(tableName.getNameAsString(),
456 "Overall", "Overall cost", overall);
457
458
459 for (int i = 0; i < costFunctions.length; i++) {
460 CostFunction costFunction = costFunctions[i];
461 String costFunctionName = costFunction.getClass().getSimpleName();
462 Double costPercent = (overall == 0) ? 0 : (subCosts[i] / overall);
463
464 balancer.updateStochasticCost(tableName.getNameAsString(), costFunctionName,
465 "The percent of " + costFunctionName, costPercent);
466 }
467 }
468 }
469
470 private String functionCost() {
471 StringBuilder builder = new StringBuilder();
472 for (CostFunction c:costFunctions) {
473 builder.append(c.getClass().getSimpleName());
474 builder.append(" : (");
475 builder.append(c.getMultiplier());
476 builder.append(", ");
477 builder.append(c.cost());
478 builder.append("); ");
479 }
480 return builder.toString();
481 }
482
483
484
485
486
487
488
489
490 private List<RegionPlan> createRegionPlans(Cluster cluster) {
491 List<RegionPlan> plans = new LinkedList<RegionPlan>();
492 for (int regionIndex = 0;
493 regionIndex < cluster.regionIndexToServerIndex.length; regionIndex++) {
494 int initialServerIndex = cluster.initialRegionIndexToServerIndex[regionIndex];
495 int newServerIndex = cluster.regionIndexToServerIndex[regionIndex];
496
497 if (initialServerIndex != newServerIndex) {
498 HRegionInfo region = cluster.regions[regionIndex];
499 ServerName initialServer = cluster.servers[initialServerIndex];
500 ServerName newServer = cluster.servers[newServerIndex];
501
502 if (LOG.isTraceEnabled()) {
503 LOG.trace("Moving Region " + region.getEncodedName() + " from server "
504 + initialServer.getHostname() + " to " + newServer.getHostname());
505 }
506 RegionPlan rp = new RegionPlan(region, initialServer, newServer);
507 plans.add(rp);
508 }
509 }
510 return plans;
511 }
512
513
514
515
516 private synchronized void updateRegionLoad() {
517
518
519 Map<String, Deque<RegionLoad>> oldLoads = loads;
520 loads = new HashMap<String, Deque<RegionLoad>>();
521
522 for (ServerName sn : clusterStatus.getServers()) {
523 ServerLoad sl = clusterStatus.getLoad(sn);
524 if (sl == null) {
525 continue;
526 }
527 for (Entry<byte[], RegionLoad> entry : sl.getRegionsLoad().entrySet()) {
528 Deque<RegionLoad> rLoads = oldLoads.get(Bytes.toString(entry.getKey()));
529 if (rLoads == null) {
530
531 rLoads = new ArrayDeque<RegionLoad>();
532 } else if (rLoads.size() >= numRegionLoadsToRemember) {
533 rLoads.remove();
534 }
535 rLoads.add(entry.getValue());
536 loads.put(Bytes.toString(entry.getKey()), rLoads);
537
538 }
539 }
540
541 for(CostFromRegionLoadFunction cost : regionLoadFunctions) {
542 cost.setLoads(loads);
543 }
544 }
545
546 protected void initCosts(Cluster cluster) {
547 for (CostFunction c:costFunctions) {
548 c.init(cluster);
549 }
550 }
551
552 protected void updateCostsWithAction(Cluster cluster, Action action) {
553 for (CostFunction c : costFunctions) {
554 c.postAction(action);
555 }
556 }
557
558
559
560
561 public String[] getCostFunctionNames() {
562 if (costFunctions == null) return null;
563 String[] ret = new String[costFunctions.length];
564 for (int i = 0; i < costFunctions.length; i++) {
565 CostFunction c = costFunctions[i];
566 ret[i] = c.getClass().getSimpleName();
567 }
568
569 return ret;
570 }
571
572
573
574
575
576
577
578
579
580
581 protected double computeCost(Cluster cluster, double previousCost) {
582 double total = 0;
583
584 for (int i = 0; i < costFunctions.length; i++) {
585 CostFunction c = costFunctions[i];
586 this.tempFunctionCosts[i] = 0.0;
587
588 if (c.getMultiplier() <= 0) {
589 continue;
590 }
591
592 Float multiplier = c.getMultiplier();
593 Double cost = c.cost();
594
595 this.tempFunctionCosts[i] = multiplier*cost;
596 total += this.tempFunctionCosts[i];
597
598 if (total > previousCost) {
599 break;
600 }
601 }
602
603 return total;
604 }
605
606
607 abstract static class CandidateGenerator {
608 abstract Cluster.Action generate(Cluster cluster);
609
610
611
612
613
614
615
616
617
618
619
620
621
622 protected int pickRandomRegion(Cluster cluster, int server, double chanceOfNoSwap) {
623
624 if (cluster.regionsPerServer[server].length == 0 || RANDOM.nextFloat() < chanceOfNoSwap) {
625
626 return -1;
627 }
628 int rand = RANDOM.nextInt(cluster.regionsPerServer[server].length);
629 return cluster.regionsPerServer[server][rand];
630
631 }
632 protected int pickRandomServer(Cluster cluster) {
633 if (cluster.numServers < 1) {
634 return -1;
635 }
636
637 return RANDOM.nextInt(cluster.numServers);
638 }
639
640 protected int pickRandomRack(Cluster cluster) {
641 if (cluster.numRacks < 1) {
642 return -1;
643 }
644
645 return RANDOM.nextInt(cluster.numRacks);
646 }
647
648 protected int pickOtherRandomServer(Cluster cluster, int serverIndex) {
649 if (cluster.numServers < 2) {
650 return -1;
651 }
652 while (true) {
653 int otherServerIndex = pickRandomServer(cluster);
654 if (otherServerIndex != serverIndex) {
655 return otherServerIndex;
656 }
657 }
658 }
659
660 protected int pickOtherRandomRack(Cluster cluster, int rackIndex) {
661 if (cluster.numRacks < 2) {
662 return -1;
663 }
664 while (true) {
665 int otherRackIndex = pickRandomRack(cluster);
666 if (otherRackIndex != rackIndex) {
667 return otherRackIndex;
668 }
669 }
670 }
671
672 protected Cluster.Action pickRandomRegions(Cluster cluster,
673 int thisServer,
674 int otherServer) {
675 if (thisServer < 0 || otherServer < 0) {
676 return Cluster.NullAction;
677 }
678
679
680 int thisRegionCount = cluster.getNumRegions(thisServer);
681 int otherRegionCount = cluster.getNumRegions(otherServer);
682
683
684 double thisChance = (thisRegionCount > otherRegionCount) ? 0 : 0.5;
685 double otherChance = (thisRegionCount <= otherRegionCount) ? 0 : 0.5;
686
687 int thisRegion = pickRandomRegion(cluster, thisServer, thisChance);
688 int otherRegion = pickRandomRegion(cluster, otherServer, otherChance);
689
690 return getAction(thisServer, thisRegion, otherServer, otherRegion);
691 }
692
693 protected Cluster.Action getAction(int fromServer, int fromRegion,
694 int toServer, int toRegion) {
695 if (fromServer < 0 || toServer < 0) {
696 return Cluster.NullAction;
697 }
698 if (fromRegion > 0 && toRegion > 0) {
699 return new Cluster.SwapRegionsAction(fromServer, fromRegion,
700 toServer, toRegion);
701 } else if (fromRegion > 0) {
702 return new Cluster.MoveRegionAction(fromRegion, fromServer, toServer);
703 } else if (toRegion > 0) {
704 return new Cluster.MoveRegionAction(toRegion, toServer, fromServer);
705 } else {
706 return Cluster.NullAction;
707 }
708 }
709
710
711
712
713 protected List<Integer> getRandomIterationOrder(int length) {
714 ArrayList<Integer> order = new ArrayList<>(length);
715 for (int i = 0; i < length; i++) {
716 order.add(i);
717 }
718 Collections.shuffle(order);
719 return order;
720 }
721 }
722
723 static class RandomCandidateGenerator extends CandidateGenerator {
724
725 @Override
726 Cluster.Action generate(Cluster cluster) {
727
728 int thisServer = pickRandomServer(cluster);
729
730
731 int otherServer = pickOtherRandomServer(cluster, thisServer);
732
733 return pickRandomRegions(cluster, thisServer, otherServer);
734 }
735 }
736
737 static class LoadCandidateGenerator extends CandidateGenerator {
738
739 @Override
740 Cluster.Action generate(Cluster cluster) {
741 cluster.sortServersByRegionCount();
742 int thisServer = pickMostLoadedServer(cluster, -1);
743 int otherServer = pickLeastLoadedServer(cluster, thisServer);
744
745 return pickRandomRegions(cluster, thisServer, otherServer);
746 }
747
748 private int pickLeastLoadedServer(final Cluster cluster, int thisServer) {
749 Integer[] servers = cluster.serverIndicesSortedByRegionCount;
750
751 int index = 0;
752 while (servers[index] == null || servers[index] == thisServer) {
753 index++;
754 if (index == servers.length) {
755 return -1;
756 }
757 }
758 return servers[index];
759 }
760
761 private int pickMostLoadedServer(final Cluster cluster, int thisServer) {
762 Integer[] servers = cluster.serverIndicesSortedByRegionCount;
763
764 int index = servers.length - 1;
765 while (servers[index] == null || servers[index] == thisServer) {
766 index--;
767 if (index < 0) {
768 return -1;
769 }
770 }
771 return servers[index];
772 }
773 }
774
775 static class LocalityBasedCandidateGenerator extends CandidateGenerator {
776
777 private MasterServices masterServices;
778
779 LocalityBasedCandidateGenerator(MasterServices masterServices) {
780 this.masterServices = masterServices;
781 }
782
783 @Override
784 Cluster.Action generate(Cluster cluster) {
785 if (this.masterServices == null) {
786 int thisServer = pickRandomServer(cluster);
787
788 int otherServer = pickOtherRandomServer(cluster, thisServer);
789 return pickRandomRegions(cluster, thisServer, otherServer);
790 }
791
792
793 for (int region : getRandomIterationOrder(cluster.numRegions)) {
794 int currentServer = cluster.regionIndexToServerIndex[region];
795 if (currentServer != cluster.getOrComputeRegionsToMostLocalEntities(LocalityType.SERVER)[region]) {
796 Optional<Action> potential = tryMoveOrSwap(
797 cluster,
798 currentServer,
799 region,
800 cluster.getOrComputeRegionsToMostLocalEntities(LocalityType.SERVER)[region]
801 );
802 if (potential.isPresent()) {
803 return potential.get();
804 }
805 }
806 }
807 return Cluster.NullAction;
808 }
809
810
811
812
813
814 private Optional<Action> tryMoveOrSwap(Cluster cluster,
815 int fromServer,
816 int fromRegion,
817 int toServer) {
818
819 if (cluster.serverHasTooFewRegions(toServer)) {
820 return Optional.of(getAction(fromServer, fromRegion, toServer, -1));
821 }
822
823
824 double fromRegionLocalityDelta =
825 getWeightedLocality(cluster, fromRegion, toServer) - getWeightedLocality(cluster, fromRegion, fromServer);
826 for (int toRegionIndex : getRandomIterationOrder(cluster.regionsPerServer[toServer].length)) {
827 int toRegion = cluster.regionsPerServer[toServer][toRegionIndex];
828 double toRegionLocalityDelta =
829 getWeightedLocality(cluster, toRegion, fromServer) - getWeightedLocality(cluster, toRegion, toServer);
830
831 if (fromRegionLocalityDelta + toRegionLocalityDelta >= 0) {
832 return Optional.of(getAction(fromServer, fromRegion, toServer, toRegion));
833 }
834 }
835
836 return Optional.absent();
837 }
838
839 private double getWeightedLocality(Cluster cluster, int region, int server) {
840 return cluster.getOrComputeWeightedLocality(region, server, LocalityType.SERVER);
841 }
842
843 void setServices(MasterServices services) {
844 this.masterServices = services;
845 }
846 }
847
848
849
850
851
852 static class RegionReplicaCandidateGenerator extends CandidateGenerator {
853
854 RandomCandidateGenerator randomGenerator = new RandomCandidateGenerator();
855
856
857
858
859
860
861
862
863
864
865 int selectCoHostedRegionPerGroup(int[] primariesOfRegionsPerGroup, int[] regionsPerGroup
866 , int[] regionIndexToPrimaryIndex) {
867 int currentPrimary = -1;
868 int currentPrimaryIndex = -1;
869 int selectedPrimaryIndex = -1;
870 double currentLargestRandom = -1;
871
872
873
874 for (int j = 0; j <= primariesOfRegionsPerGroup.length; j++) {
875 int primary = j < primariesOfRegionsPerGroup.length
876 ? primariesOfRegionsPerGroup[j] : -1;
877 if (primary != currentPrimary) {
878 int numReplicas = j - currentPrimaryIndex;
879 if (numReplicas > 1) {
880
881 double currentRandom = RANDOM.nextDouble();
882
883
884 if (currentRandom > currentLargestRandom) {
885 selectedPrimaryIndex = currentPrimary;
886 currentLargestRandom = currentRandom;
887 }
888 }
889 currentPrimary = primary;
890 currentPrimaryIndex = j;
891 }
892 }
893
894
895
896 for (int j = 0; j < regionsPerGroup.length; j++) {
897 int regionIndex = regionsPerGroup[j];
898 if (selectedPrimaryIndex == regionIndexToPrimaryIndex[regionIndex]) {
899
900 if (selectedPrimaryIndex != regionIndex) {
901 return regionIndex;
902 }
903 }
904 }
905 return -1;
906 }
907
908 @Override
909 Cluster.Action generate(Cluster cluster) {
910 int serverIndex = pickRandomServer(cluster);
911 if (cluster.numServers <= 1 || serverIndex == -1) {
912 return Cluster.NullAction;
913 }
914
915 int regionIndex = selectCoHostedRegionPerGroup(
916 cluster.primariesOfRegionsPerServer[serverIndex],
917 cluster.regionsPerServer[serverIndex],
918 cluster.regionIndexToPrimaryIndex);
919
920
921 if (regionIndex == -1) {
922
923 return randomGenerator.generate(cluster);
924 }
925
926 int toServerIndex = pickOtherRandomServer(cluster, serverIndex);
927 int toRegionIndex = pickRandomRegion(cluster, toServerIndex, 0.9f);
928 return getAction(serverIndex, regionIndex, toServerIndex, toRegionIndex);
929 }
930 }
931
932
933
934
935
936 static class RegionReplicaRackCandidateGenerator extends RegionReplicaCandidateGenerator {
937 @Override
938 Cluster.Action generate(Cluster cluster) {
939 int rackIndex = pickRandomRack(cluster);
940 if (cluster.numRacks <= 1 || rackIndex == -1) {
941 return super.generate(cluster);
942 }
943
944 int regionIndex = selectCoHostedRegionPerGroup(
945 cluster.primariesOfRegionsPerRack[rackIndex],
946 cluster.regionsPerRack[rackIndex],
947 cluster.regionIndexToPrimaryIndex);
948
949
950 if (regionIndex == -1) {
951
952 return randomGenerator.generate(cluster);
953 }
954
955 int serverIndex = cluster.regionIndexToServerIndex[regionIndex];
956 int toRackIndex = pickOtherRandomRack(cluster, rackIndex);
957
958 int rand = RANDOM.nextInt(cluster.serversPerRack[toRackIndex].length);
959 int toServerIndex = cluster.serversPerRack[toRackIndex][rand];
960 int toRegionIndex = pickRandomRegion(cluster, toServerIndex, 0.9f);
961 return getAction(serverIndex, regionIndex, toServerIndex, toRegionIndex);
962 }
963 }
964
965
966
967
968 abstract static class CostFunction {
969
970 private float multiplier = 0;
971
972 protected Cluster cluster;
973
974 CostFunction(Configuration c) {
975 }
976
977 boolean isNeeded() {
978 return true;
979 }
980 float getMultiplier() {
981 return multiplier;
982 }
983
984 void setMultiplier(float m) {
985 this.multiplier = m;
986 }
987
988
989
990
991 void init(Cluster cluster) {
992 this.cluster = cluster;
993 }
994
995
996
997
998
999 void postAction(Action action) {
1000 switch (action.type) {
1001 case NULL: break;
1002 case ASSIGN_REGION:
1003 AssignRegionAction ar = (AssignRegionAction) action;
1004 regionMoved(ar.region, -1, ar.server);
1005 break;
1006 case MOVE_REGION:
1007 MoveRegionAction mra = (MoveRegionAction) action;
1008 regionMoved(mra.region, mra.fromServer, mra.toServer);
1009 break;
1010 case SWAP_REGIONS:
1011 SwapRegionsAction a = (SwapRegionsAction) action;
1012 regionMoved(a.fromRegion, a.fromServer, a.toServer);
1013 regionMoved(a.toRegion, a.toServer, a.fromServer);
1014 break;
1015 default:
1016 throw new RuntimeException("Uknown action:" + action.type);
1017 }
1018 }
1019
1020 protected void regionMoved(int region, int oldServer, int newServer) {
1021 }
1022
1023 abstract double cost();
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033 protected double costFromArray(double[] stats) {
1034 double totalCost = 0;
1035 double total = getSum(stats);
1036
1037 double count = stats.length;
1038 double mean = total/count;
1039
1040
1041
1042 double max = ((count - 1) * mean) + (total - mean);
1043
1044
1045 double min;
1046 if (count > total) {
1047 min = ((count - total) * mean) + ((1 - mean) * total);
1048 } else {
1049
1050 int numHigh = (int) (total - (Math.floor(mean) * count));
1051 int numLow = (int) (count - numHigh);
1052
1053 min = (numHigh * (Math.ceil(mean) - mean)) + (numLow * (mean - Math.floor(mean)));
1054
1055 }
1056 min = Math.max(0, min);
1057 for (int i=0; i<stats.length; i++) {
1058 double n = stats[i];
1059 double diff = Math.abs(mean - n);
1060 totalCost += diff;
1061 }
1062
1063 double scaled = scale(min, max, totalCost);
1064 return scaled;
1065 }
1066
1067 private double getSum(double[] stats) {
1068 double total = 0;
1069 for(double s:stats) {
1070 total += s;
1071 }
1072 return total;
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083 protected double scale(double min, double max, double value) {
1084 if (max <= min || value <= min) {
1085 return 0;
1086 }
1087 if ((max - min) == 0) return 0;
1088
1089 return Math.max(0d, Math.min(1d, (value - min) / (max - min)));
1090 }
1091 }
1092
1093
1094
1095
1096
1097 static class MoveCostFunction extends CostFunction {
1098 private static final String MOVE_COST_KEY = "hbase.master.balancer.stochastic.moveCost";
1099 private static final String MAX_MOVES_PERCENT_KEY =
1100 "hbase.master.balancer.stochastic.maxMovePercent";
1101 private static final float DEFAULT_MOVE_COST = 7;
1102 private static final int DEFAULT_MAX_MOVES = 600;
1103 private static final float DEFAULT_MAX_MOVE_PERCENT = 0.25f;
1104
1105 private final float maxMovesPercent;
1106
1107 MoveCostFunction(Configuration conf) {
1108 super(conf);
1109
1110
1111
1112 this.setMultiplier(conf.getFloat(MOVE_COST_KEY, DEFAULT_MOVE_COST));
1113
1114 maxMovesPercent = conf.getFloat(MAX_MOVES_PERCENT_KEY, DEFAULT_MAX_MOVE_PERCENT);
1115 }
1116
1117 @Override
1118 double cost() {
1119
1120 int maxMoves = Math.max((int) (cluster.numRegions * maxMovesPercent),
1121 DEFAULT_MAX_MOVES);
1122
1123 double moveCost = cluster.numMovedRegions;
1124
1125
1126
1127 if (moveCost > maxMoves) {
1128 return 1000000;
1129 }
1130
1131 return scale(0, Math.min(cluster.numRegions, maxMoves), moveCost);
1132 }
1133 }
1134
1135
1136
1137
1138
1139 static class RegionCountSkewCostFunction extends CostFunction {
1140 private static final String REGION_COUNT_SKEW_COST_KEY =
1141 "hbase.master.balancer.stochastic.regionCountCost";
1142 private static final float DEFAULT_REGION_COUNT_SKEW_COST = 500;
1143
1144 private double[] stats = null;
1145
1146 RegionCountSkewCostFunction(Configuration conf) {
1147 super(conf);
1148
1149 this.setMultiplier(conf.getFloat(REGION_COUNT_SKEW_COST_KEY, DEFAULT_REGION_COUNT_SKEW_COST));
1150 }
1151
1152 @Override
1153 double cost() {
1154 if (stats == null || stats.length != cluster.numServers) {
1155 stats = new double[cluster.numServers];
1156 }
1157
1158 for (int i =0; i < cluster.numServers; i++) {
1159 stats[i] = cluster.regionsPerServer[i].length;
1160 }
1161
1162 return costFromArray(stats);
1163 }
1164 }
1165
1166
1167
1168
1169
1170 static class PrimaryRegionCountSkewCostFunction extends CostFunction {
1171 private static final String PRIMARY_REGION_COUNT_SKEW_COST_KEY =
1172 "hbase.master.balancer.stochastic.primaryRegionCountCost";
1173 private static final float DEFAULT_PRIMARY_REGION_COUNT_SKEW_COST = 500;
1174
1175 private double[] stats = null;
1176
1177 PrimaryRegionCountSkewCostFunction(Configuration conf) {
1178 super(conf);
1179
1180 this.setMultiplier(conf.getFloat(PRIMARY_REGION_COUNT_SKEW_COST_KEY,
1181 DEFAULT_PRIMARY_REGION_COUNT_SKEW_COST));
1182 }
1183
1184 @Override
1185 double cost() {
1186 if (!cluster.hasRegionReplicas) {
1187 return 0;
1188 }
1189 if (stats == null || stats.length != cluster.numServers) {
1190 stats = new double[cluster.numServers];
1191 }
1192
1193 for (int i =0; i < cluster.numServers; i++) {
1194 stats[i] = 0;
1195 for (int regionIdx : cluster.regionsPerServer[i]) {
1196 if (regionIdx == cluster.regionIndexToPrimaryIndex[regionIdx]) {
1197 stats[i] ++;
1198 }
1199 }
1200 }
1201
1202 return costFromArray(stats);
1203 }
1204 }
1205
1206
1207
1208
1209
1210 static class TableSkewCostFunction extends CostFunction {
1211
1212 private static final String TABLE_SKEW_COST_KEY =
1213 "hbase.master.balancer.stochastic.tableSkewCost";
1214 private static final float DEFAULT_TABLE_SKEW_COST = 35;
1215
1216 TableSkewCostFunction(Configuration conf) {
1217 super(conf);
1218 this.setMultiplier(conf.getFloat(TABLE_SKEW_COST_KEY, DEFAULT_TABLE_SKEW_COST));
1219 }
1220
1221 @Override
1222 double cost() {
1223 double max = cluster.numRegions;
1224 double min = ((double) cluster.numRegions) / cluster.numServers;
1225 double value = 0;
1226
1227 for (int i = 0; i < cluster.numMaxRegionsPerTable.length; i++) {
1228 value += cluster.numMaxRegionsPerTable[i];
1229 }
1230
1231 return scale(min, max, value);
1232 }
1233 }
1234
1235
1236
1237
1238
1239 static abstract class LocalityBasedCostFunction extends CostFunction {
1240
1241 private final LocalityType type;
1242
1243 private double bestLocality;
1244 private double locality;
1245
1246 private MasterServices services;
1247
1248 LocalityBasedCostFunction(Configuration conf,
1249 MasterServices srv,
1250 LocalityType type,
1251 String localityCostKey,
1252 float defaultLocalityCost) {
1253 super(conf);
1254 this.type = type;
1255 this.setMultiplier(conf.getFloat(localityCostKey, defaultLocalityCost));
1256 this.services = srv;
1257 this.locality = 0.0;
1258 this.bestLocality = 0.0;
1259 }
1260
1261
1262
1263
1264 abstract int regionIndexToEntityIndex(int region);
1265
1266 public void setServices(MasterServices srvc) {
1267 this.services = srvc;
1268 }
1269
1270 @Override
1271 void init(Cluster cluster) {
1272 super.init(cluster);
1273 locality = 0.0;
1274 bestLocality = 0.0;
1275
1276
1277 if (this.services == null) {
1278 return;
1279 }
1280
1281 for (int region = 0; region < cluster.numRegions; region++) {
1282 locality += getWeightedLocality(region, regionIndexToEntityIndex(region));
1283 bestLocality += getWeightedLocality(region, getMostLocalEntityForRegion(region));
1284 }
1285
1286
1287
1288
1289 locality = bestLocality == 0 ? 1.0 : locality / bestLocality;
1290 }
1291
1292 @Override
1293 protected void regionMoved(int region, int oldServer, int newServer) {
1294 int oldEntity = type == LocalityType.SERVER ? oldServer : cluster.serverIndexToRackIndex[oldServer];
1295 int newEntity = type == LocalityType.SERVER ? newServer : cluster.serverIndexToRackIndex[newServer];
1296 if (this.services == null) {
1297 return;
1298 }
1299 double localityDelta = getWeightedLocality(region, newEntity) - getWeightedLocality(region, oldEntity);
1300 double normalizedDelta = bestLocality == 0 ? 0.0 : localityDelta / bestLocality;
1301 locality += normalizedDelta;
1302 }
1303
1304 @Override
1305 double cost() {
1306 return 1 - locality;
1307 }
1308
1309 private int getMostLocalEntityForRegion(int region) {
1310 return cluster.getOrComputeRegionsToMostLocalEntities(type)[region];
1311 }
1312
1313 private double getWeightedLocality(int region, int entity) {
1314 return cluster.getOrComputeWeightedLocality(region, entity, type);
1315 }
1316
1317 }
1318
1319 static class ServerLocalityCostFunction extends LocalityBasedCostFunction {
1320
1321 private static final String LOCALITY_COST_KEY = "hbase.master.balancer.stochastic.localityCost";
1322 private static final float DEFAULT_LOCALITY_COST = 25;
1323
1324 ServerLocalityCostFunction(Configuration conf, MasterServices srv) {
1325 super(
1326 conf,
1327 srv,
1328 LocalityType.SERVER,
1329 LOCALITY_COST_KEY,
1330 DEFAULT_LOCALITY_COST
1331 );
1332 }
1333
1334 @Override
1335 int regionIndexToEntityIndex(int region) {
1336 return cluster.regionIndexToServerIndex[region];
1337 }
1338 }
1339
1340 static class RackLocalityCostFunction extends LocalityBasedCostFunction {
1341
1342 private static final String RACK_LOCALITY_COST_KEY = "hbase.master.balancer.stochastic.rackLocalityCost";
1343 private static final float DEFAULT_RACK_LOCALITY_COST = 15;
1344
1345 public RackLocalityCostFunction(Configuration conf, MasterServices services) {
1346 super(
1347 conf,
1348 services,
1349 LocalityType.RACK,
1350 RACK_LOCALITY_COST_KEY,
1351 DEFAULT_RACK_LOCALITY_COST
1352 );
1353 }
1354
1355 @Override
1356 int regionIndexToEntityIndex(int region) {
1357 return cluster.getRackForRegion(region);
1358 }
1359 }
1360
1361
1362
1363
1364
1365 abstract static class CostFromRegionLoadFunction extends CostFunction {
1366
1367 private ClusterStatus clusterStatus = null;
1368 private Map<String, Deque<RegionLoad>> loads = null;
1369 private double[] stats = null;
1370 CostFromRegionLoadFunction(Configuration conf) {
1371 super(conf);
1372 }
1373
1374 void setClusterStatus(ClusterStatus status) {
1375 this.clusterStatus = status;
1376 }
1377
1378 void setLoads(Map<String, Deque<RegionLoad>> l) {
1379 this.loads = l;
1380 }
1381
1382 @Override
1383 double cost() {
1384 if (clusterStatus == null || loads == null) {
1385 return 0;
1386 }
1387
1388 if (stats == null || stats.length != cluster.numServers) {
1389 stats = new double[cluster.numServers];
1390 }
1391
1392 for (int i =0; i < stats.length; i++) {
1393
1394 long cost = 0;
1395
1396
1397 for(int regionIndex:cluster.regionsPerServer[i]) {
1398 Collection<RegionLoad> regionLoadList = cluster.regionLoads[regionIndex];
1399
1400
1401 if (regionLoadList != null) {
1402 cost = (long) (cost + getRegionLoadCost(regionLoadList));
1403 }
1404 }
1405
1406
1407 stats[i] = cost;
1408 }
1409
1410
1411 return costFromArray(stats);
1412 }
1413
1414 protected double getRegionLoadCost(Collection<RegionLoad> regionLoadList) {
1415 double cost = 0;
1416
1417 for (RegionLoad rl : regionLoadList) {
1418 cost += getCostFromRl(rl);
1419 }
1420 return cost / regionLoadList.size();
1421 }
1422
1423
1424 protected abstract double getCostFromRl(RegionLoad rl);
1425 }
1426
1427
1428
1429
1430
1431
1432 abstract static class CostFromRegionLoadAsRateFunction extends CostFromRegionLoadFunction {
1433
1434 CostFromRegionLoadAsRateFunction(Configuration conf) {
1435 super(conf);
1436 }
1437
1438 @Override
1439 protected double getRegionLoadCost(Collection<RegionLoad> regionLoadList) {
1440 double cost = 0;
1441 double previous = 0;
1442 boolean isFirst = true;
1443 for (RegionLoad rl : regionLoadList) {
1444 double current = getCostFromRl(rl);
1445 if (isFirst) {
1446 isFirst = false;
1447 } else {
1448 cost += current - previous;
1449 }
1450 previous = current;
1451 }
1452 return Math.max(0, cost / (regionLoadList.size() - 1));
1453 }
1454 }
1455
1456
1457
1458
1459
1460
1461 static class ReadRequestCostFunction extends CostFromRegionLoadAsRateFunction {
1462
1463 private static final String READ_REQUEST_COST_KEY =
1464 "hbase.master.balancer.stochastic.readRequestCost";
1465 private static final float DEFAULT_READ_REQUEST_COST = 5;
1466
1467 ReadRequestCostFunction(Configuration conf) {
1468 super(conf);
1469 this.setMultiplier(conf.getFloat(READ_REQUEST_COST_KEY, DEFAULT_READ_REQUEST_COST));
1470 }
1471
1472
1473 @Override
1474 protected double getCostFromRl(RegionLoad rl) {
1475 return rl.getReadRequestsCount();
1476 }
1477 }
1478
1479
1480
1481
1482
1483 static class WriteRequestCostFunction extends CostFromRegionLoadAsRateFunction {
1484
1485 private static final String WRITE_REQUEST_COST_KEY =
1486 "hbase.master.balancer.stochastic.writeRequestCost";
1487 private static final float DEFAULT_WRITE_REQUEST_COST = 5;
1488
1489 WriteRequestCostFunction(Configuration conf) {
1490 super(conf);
1491 this.setMultiplier(conf.getFloat(WRITE_REQUEST_COST_KEY, DEFAULT_WRITE_REQUEST_COST));
1492 }
1493
1494 @Override
1495 protected double getCostFromRl(RegionLoad rl) {
1496 return rl.getWriteRequestsCount();
1497 }
1498 }
1499
1500
1501
1502
1503
1504
1505
1506 static class RegionReplicaHostCostFunction extends CostFunction {
1507 private static final String REGION_REPLICA_HOST_COST_KEY =
1508 "hbase.master.balancer.stochastic.regionReplicaHostCostKey";
1509 private static final float DEFAULT_REGION_REPLICA_HOST_COST_KEY = 100000;
1510
1511 long maxCost = 0;
1512 long[] costsPerGroup;
1513 int[][] primariesOfRegionsPerGroup;
1514
1515 public RegionReplicaHostCostFunction(Configuration conf) {
1516 super(conf);
1517 this.setMultiplier(conf.getFloat(REGION_REPLICA_HOST_COST_KEY,
1518 DEFAULT_REGION_REPLICA_HOST_COST_KEY));
1519 }
1520
1521 @Override
1522 void init(Cluster cluster) {
1523 super.init(cluster);
1524
1525 maxCost = cluster.numHosts > 1 ? getMaxCost(cluster) : 0;
1526 costsPerGroup = new long[cluster.numHosts];
1527 primariesOfRegionsPerGroup = cluster.multiServersPerHost
1528 ? cluster.primariesOfRegionsPerHost
1529 : cluster.primariesOfRegionsPerServer;
1530 for (int i = 0 ; i < primariesOfRegionsPerGroup.length; i++) {
1531 costsPerGroup[i] = costPerGroup(primariesOfRegionsPerGroup[i]);
1532 }
1533 }
1534
1535 long getMaxCost(Cluster cluster) {
1536 if (!cluster.hasRegionReplicas) {
1537 return 0;
1538 }
1539
1540 int[] primariesOfRegions = new int[cluster.numRegions];
1541 System.arraycopy(cluster.regionIndexToPrimaryIndex, 0, primariesOfRegions, 0,
1542 cluster.regions.length);
1543
1544 Arrays.sort(primariesOfRegions);
1545
1546
1547 return costPerGroup(primariesOfRegions);
1548 }
1549
1550 @Override
1551 boolean isNeeded() {
1552 return cluster.hasRegionReplicas;
1553 }
1554
1555 @Override
1556 double cost() {
1557 if (maxCost <= 0) {
1558 return 0;
1559 }
1560
1561 long totalCost = 0;
1562 for (int i = 0 ; i < costsPerGroup.length; i++) {
1563 totalCost += costsPerGroup[i];
1564 }
1565 return scale(0, maxCost, totalCost);
1566 }
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576 protected long costPerGroup(int[] primariesOfRegions) {
1577 long cost = 0;
1578 int currentPrimary = -1;
1579 int currentPrimaryIndex = -1;
1580
1581
1582 for (int j = 0 ; j <= primariesOfRegions.length; j++) {
1583 int primary = j < primariesOfRegions.length ? primariesOfRegions[j] : -1;
1584 if (primary != currentPrimary) {
1585 int numReplicas = j - currentPrimaryIndex;
1586
1587 if (numReplicas > 1) {
1588 cost += (numReplicas - 1) * (numReplicas - 1);
1589 }
1590 currentPrimary = primary;
1591 currentPrimaryIndex = j;
1592 }
1593 }
1594
1595 return cost;
1596 }
1597
1598 @Override
1599 protected void regionMoved(int region, int oldServer, int newServer) {
1600 if (maxCost <= 0) {
1601 return;
1602 }
1603 if (cluster.multiServersPerHost) {
1604 int oldHost = cluster.serverIndexToHostIndex[oldServer];
1605 int newHost = cluster.serverIndexToHostIndex[newServer];
1606 if (newHost != oldHost) {
1607 costsPerGroup[oldHost] = costPerGroup(cluster.primariesOfRegionsPerHost[oldHost]);
1608 costsPerGroup[newHost] = costPerGroup(cluster.primariesOfRegionsPerHost[newHost]);
1609 }
1610 } else {
1611 costsPerGroup[oldServer] = costPerGroup(cluster.primariesOfRegionsPerServer[oldServer]);
1612 costsPerGroup[newServer] = costPerGroup(cluster.primariesOfRegionsPerServer[newServer]);
1613 }
1614 }
1615 }
1616
1617
1618
1619
1620
1621
1622 static class RegionReplicaRackCostFunction extends RegionReplicaHostCostFunction {
1623 private static final String REGION_REPLICA_RACK_COST_KEY =
1624 "hbase.master.balancer.stochastic.regionReplicaRackCostKey";
1625 private static final float DEFAULT_REGION_REPLICA_RACK_COST_KEY = 10000;
1626
1627 public RegionReplicaRackCostFunction(Configuration conf) {
1628 super(conf);
1629 this.setMultiplier(conf.getFloat(REGION_REPLICA_RACK_COST_KEY,
1630 DEFAULT_REGION_REPLICA_RACK_COST_KEY));
1631 }
1632
1633 @Override
1634 void init(Cluster cluster) {
1635 this.cluster = cluster;
1636 if (cluster.numRacks <= 1) {
1637 maxCost = 0;
1638 return;
1639 }
1640
1641 maxCost = getMaxCost(cluster);
1642 costsPerGroup = new long[cluster.numRacks];
1643 for (int i = 0 ; i < cluster.primariesOfRegionsPerRack.length; i++) {
1644 costsPerGroup[i] = costPerGroup(cluster.primariesOfRegionsPerRack[i]);
1645 }
1646 }
1647
1648 @Override
1649 protected void regionMoved(int region, int oldServer, int newServer) {
1650 if (maxCost <= 0) {
1651 return;
1652 }
1653 int oldRack = cluster.serverIndexToRackIndex[oldServer];
1654 int newRack = cluster.serverIndexToRackIndex[newServer];
1655 if (newRack != oldRack) {
1656 costsPerGroup[oldRack] = costPerGroup(cluster.primariesOfRegionsPerRack[oldRack]);
1657 costsPerGroup[newRack] = costPerGroup(cluster.primariesOfRegionsPerRack[newRack]);
1658 }
1659 }
1660 }
1661
1662
1663
1664
1665
1666 static class MemstoreSizeCostFunction extends CostFromRegionLoadAsRateFunction {
1667
1668 private static final String MEMSTORE_SIZE_COST_KEY =
1669 "hbase.master.balancer.stochastic.memstoreSizeCost";
1670 private static final float DEFAULT_MEMSTORE_SIZE_COST = 5;
1671
1672 MemstoreSizeCostFunction(Configuration conf) {
1673 super(conf);
1674 this.setMultiplier(conf.getFloat(MEMSTORE_SIZE_COST_KEY, DEFAULT_MEMSTORE_SIZE_COST));
1675 }
1676
1677 @Override
1678 protected double getCostFromRl(RegionLoad rl) {
1679 return rl.getMemStoreSizeMB();
1680 }
1681 }
1682
1683
1684
1685
1686 static class StoreFileCostFunction extends CostFromRegionLoadFunction {
1687
1688 private static final String STOREFILE_SIZE_COST_KEY =
1689 "hbase.master.balancer.stochastic.storefileSizeCost";
1690 private static final float DEFAULT_STOREFILE_SIZE_COST = 5;
1691
1692 StoreFileCostFunction(Configuration conf) {
1693 super(conf);
1694 this.setMultiplier(conf.getFloat(STOREFILE_SIZE_COST_KEY, DEFAULT_STOREFILE_SIZE_COST));
1695 }
1696
1697 @Override
1698 protected double getCostFromRl(RegionLoad rl) {
1699 return rl.getStorefileSizeMB();
1700 }
1701 }
1702
1703
1704
1705
1706 public static String composeAttributeName(String tableName, String costFunctionName) {
1707 return tableName + TABLE_FUNCTION_SEP + costFunctionName;
1708 }
1709 }