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 java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.Deque;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.NavigableMap;
33 import java.util.Random;
34 import java.util.Set;
35 import java.util.TreeMap;
36
37 import org.apache.commons.lang.NotImplementedException;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.apache.hadoop.conf.Configuration;
41 import org.apache.hadoop.hbase.ClusterStatus;
42 import org.apache.hadoop.hbase.HBaseIOException;
43 import org.apache.hadoop.hbase.HDFSBlocksDistribution;
44 import org.apache.hadoop.hbase.HRegionInfo;
45 import org.apache.hadoop.hbase.RegionLoad;
46 import org.apache.hadoop.hbase.ServerName;
47 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
48 import org.apache.hadoop.hbase.master.LoadBalancer;
49 import org.apache.hadoop.hbase.master.MasterServices;
50 import org.apache.hadoop.hbase.master.RackManager;
51 import org.apache.hadoop.hbase.master.RegionPlan;
52 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action.Type;
53 import org.apache.hadoop.util.StringUtils;
54
55 import com.google.common.annotations.VisibleForTesting;
56 import com.google.common.base.Joiner;
57 import com.google.common.collect.ArrayListMultimap;
58 import com.google.common.collect.Lists;
59 import com.google.common.collect.Sets;
60
61
62
63
64
65
66
67
68 public abstract class BaseLoadBalancer implements LoadBalancer {
69 private static final int MIN_SERVER_BALANCE = 2;
70 private volatile boolean stopped = false;
71
72 private static final List<HRegionInfo> EMPTY_REGION_LIST = new ArrayList<HRegionInfo>(0);
73
74 protected final RegionLocationFinder regionFinder = new RegionLocationFinder();
75
76 private static class DefaultRackManager extends RackManager {
77 @Override
78 public String getRack(ServerName server) {
79 return UNKNOWN_RACK;
80 }
81 }
82
83
84
85
86 protected BaseLoadBalancer() {
87 metricsBalancer = new MetricsBalancer();
88 }
89
90
91
92
93
94 protected BaseLoadBalancer(MetricsBalancer metricsBalancer) {
95 this.metricsBalancer = (metricsBalancer != null) ? metricsBalancer : new MetricsBalancer();
96 }
97
98
99
100
101
102
103
104
105
106
107
108 protected static class Cluster {
109 ServerName[] servers;
110 String[] hosts;
111 String[] racks;
112 boolean multiServersPerHost = false;
113
114 ArrayList<String> tables;
115 HRegionInfo[] regions;
116 Deque<RegionLoad>[] regionLoads;
117 private RegionLocationFinder regionFinder;
118
119 int[][] regionLocations;
120
121 int[] serverIndexToHostIndex;
122 int[] serverIndexToRackIndex;
123
124 int[][] regionsPerServer;
125 int[][] regionsPerHost;
126 int[][] regionsPerRack;
127 int[][] primariesOfRegionsPerServer;
128 int[][] primariesOfRegionsPerHost;
129 int[][] primariesOfRegionsPerRack;
130
131 int[][] serversPerHost;
132 int[][] serversPerRack;
133 int[] regionIndexToServerIndex;
134 int[] initialRegionIndexToServerIndex;
135 int[] regionIndexToTableIndex;
136 int[][] numRegionsPerServerPerTable;
137 int[] numMaxRegionsPerTable;
138 int[] regionIndexToPrimaryIndex;
139 boolean hasRegionReplicas = false;
140
141 Integer[] serverIndicesSortedByRegionCount;
142 Integer[] serverIndicesSortedByLocality;
143
144 Map<String, Integer> serversToIndex;
145 Map<String, Integer> hostsToIndex;
146 Map<String, Integer> racksToIndex;
147 Map<String, Integer> tablesToIndex;
148 Map<HRegionInfo, Integer> regionsToIndex;
149 float[] localityPerServer;
150
151 int numServers;
152 int numHosts;
153 int numRacks;
154 int numTables;
155 int numRegions;
156
157 int numMovedRegions = 0;
158 Map<ServerName, List<HRegionInfo>> clusterState;
159
160 protected final RackManager rackManager;
161
162 protected Cluster(
163 Map<ServerName, List<HRegionInfo>> clusterState,
164 Map<String, Deque<RegionLoad>> loads,
165 RegionLocationFinder regionFinder,
166 RackManager rackManager) {
167 this(null, clusterState, loads, regionFinder,
168 rackManager);
169 }
170
171 @SuppressWarnings("unchecked")
172 protected Cluster(
173 Collection<HRegionInfo> unassignedRegions,
174 Map<ServerName, List<HRegionInfo>> clusterState,
175 Map<String, Deque<RegionLoad>> loads,
176 RegionLocationFinder regionFinder,
177 RackManager rackManager) {
178
179 if (unassignedRegions == null) {
180 unassignedRegions = EMPTY_REGION_LIST;
181 }
182
183 serversToIndex = new HashMap<String, Integer>();
184 hostsToIndex = new HashMap<String, Integer>();
185 racksToIndex = new HashMap<String, Integer>();
186 tablesToIndex = new HashMap<String, Integer>();
187
188
189 tables = new ArrayList<String>();
190 this.rackManager = rackManager != null ? rackManager : new DefaultRackManager();
191
192 numRegions = 0;
193
194 List<List<Integer>> serversPerHostList = new ArrayList<List<Integer>>();
195 List<List<Integer>> serversPerRackList = new ArrayList<List<Integer>>();
196 this.clusterState = clusterState;
197 this.regionFinder = regionFinder;
198
199
200
201 for (ServerName sn : clusterState.keySet()) {
202 if (serversToIndex.get(sn.getHostAndPort()) == null) {
203 serversToIndex.put(sn.getHostAndPort(), numServers++);
204 }
205 if (!hostsToIndex.containsKey(sn.getHostname())) {
206 hostsToIndex.put(sn.getHostname(), numHosts++);
207 serversPerHostList.add(new ArrayList<Integer>(1));
208 }
209
210 int serverIndex = serversToIndex.get(sn.getHostAndPort());
211 int hostIndex = hostsToIndex.get(sn.getHostname());
212 serversPerHostList.get(hostIndex).add(serverIndex);
213
214 String rack = this.rackManager.getRack(sn);
215 if (!racksToIndex.containsKey(rack)) {
216 racksToIndex.put(rack, numRacks++);
217 serversPerRackList.add(new ArrayList<Integer>());
218 }
219 int rackIndex = racksToIndex.get(rack);
220 serversPerRackList.get(rackIndex).add(serverIndex);
221 }
222
223
224 for (Entry<ServerName, List<HRegionInfo>> entry : clusterState.entrySet()) {
225 numRegions += entry.getValue().size();
226 }
227 numRegions += unassignedRegions.size();
228
229 regionsToIndex = new HashMap<HRegionInfo, Integer>(numRegions);
230 servers = new ServerName[numServers];
231 serversPerHost = new int[numHosts][];
232 serversPerRack = new int[numRacks][];
233 regions = new HRegionInfo[numRegions];
234 regionIndexToServerIndex = new int[numRegions];
235 initialRegionIndexToServerIndex = new int[numRegions];
236 regionIndexToTableIndex = new int[numRegions];
237 regionIndexToPrimaryIndex = new int[numRegions];
238 regionLoads = new Deque[numRegions];
239 regionLocations = new int[numRegions][];
240 serverIndicesSortedByRegionCount = new Integer[numServers];
241 serverIndicesSortedByLocality = new Integer[numServers];
242 localityPerServer = new float[numServers];
243
244 serverIndexToHostIndex = new int[numServers];
245 serverIndexToRackIndex = new int[numServers];
246 regionsPerServer = new int[numServers][];
247 regionsPerHost = new int[numHosts][];
248 regionsPerRack = new int[numRacks][];
249 primariesOfRegionsPerServer = new int[numServers][];
250 primariesOfRegionsPerHost = new int[numHosts][];
251 primariesOfRegionsPerRack = new int[numRacks][];
252
253 int tableIndex = 0, regionIndex = 0, regionPerServerIndex = 0;
254
255 for (Entry<ServerName, List<HRegionInfo>> entry : clusterState.entrySet()) {
256 int serverIndex = serversToIndex.get(entry.getKey().getHostAndPort());
257
258
259
260 if (servers[serverIndex] == null ||
261 servers[serverIndex].getStartcode() < entry.getKey().getStartcode()) {
262 servers[serverIndex] = entry.getKey();
263 }
264
265 if (regionsPerServer[serverIndex] != null) {
266
267
268 regionsPerServer[serverIndex] = new int[entry.getValue().size() + regionsPerServer[serverIndex].length];
269 } else {
270 regionsPerServer[serverIndex] = new int[entry.getValue().size()];
271 }
272 primariesOfRegionsPerServer[serverIndex] = new int[regionsPerServer[serverIndex].length];
273 serverIndicesSortedByRegionCount[serverIndex] = serverIndex;
274 serverIndicesSortedByLocality[serverIndex] = serverIndex;
275 }
276
277 hosts = new String[numHosts];
278 for (Entry<String, Integer> entry : hostsToIndex.entrySet()) {
279 hosts[entry.getValue()] = entry.getKey();
280 }
281 racks = new String[numRacks];
282 for (Entry<String, Integer> entry : racksToIndex.entrySet()) {
283 racks[entry.getValue()] = entry.getKey();
284 }
285
286 for (Entry<ServerName, List<HRegionInfo>> entry : clusterState.entrySet()) {
287 int serverIndex = serversToIndex.get(entry.getKey().getHostAndPort());
288 regionPerServerIndex = 0;
289
290 int hostIndex = hostsToIndex.get(entry.getKey().getHostname());
291 serverIndexToHostIndex[serverIndex] = hostIndex;
292
293 int rackIndex = racksToIndex.get(this.rackManager.getRack(entry.getKey()));
294 serverIndexToRackIndex[serverIndex] = rackIndex;
295
296 for (HRegionInfo region : entry.getValue()) {
297 registerRegion(region, regionIndex, serverIndex, loads, regionFinder);
298
299 regionsPerServer[serverIndex][regionPerServerIndex++] = regionIndex;
300 regionIndex++;
301 }
302 }
303 for (HRegionInfo region : unassignedRegions) {
304 registerRegion(region, regionIndex, -1, loads, regionFinder);
305 regionIndex++;
306 }
307
308 for (int i = 0; i < serversPerHostList.size(); i++) {
309 serversPerHost[i] = new int[serversPerHostList.get(i).size()];
310 for (int j = 0; j < serversPerHost[i].length; j++) {
311 serversPerHost[i][j] = serversPerHostList.get(i).get(j);
312 }
313 if (serversPerHost[i].length > 1) {
314 multiServersPerHost = true;
315 }
316 }
317
318 for (int i = 0; i < serversPerRackList.size(); i++) {
319 serversPerRack[i] = new int[serversPerRackList.get(i).size()];
320 for (int j = 0; j < serversPerRack[i].length; j++) {
321 serversPerRack[i][j] = serversPerRackList.get(i).get(j);
322 }
323 }
324
325 numTables = tables.size();
326 numRegionsPerServerPerTable = new int[numServers][numTables];
327
328 for (int i = 0; i < numServers; i++) {
329 for (int j = 0; j < numTables; j++) {
330 numRegionsPerServerPerTable[i][j] = 0;
331 }
332 }
333
334 for (int i=0; i < regionIndexToServerIndex.length; i++) {
335 if (regionIndexToServerIndex[i] >= 0) {
336 numRegionsPerServerPerTable[regionIndexToServerIndex[i]][regionIndexToTableIndex[i]]++;
337 }
338 }
339
340 numMaxRegionsPerTable = new int[numTables];
341 for (int serverIndex = 0 ; serverIndex < numRegionsPerServerPerTable.length; serverIndex++) {
342 for (tableIndex = 0 ; tableIndex < numRegionsPerServerPerTable[serverIndex].length; tableIndex++) {
343 if (numRegionsPerServerPerTable[serverIndex][tableIndex] > numMaxRegionsPerTable[tableIndex]) {
344 numMaxRegionsPerTable[tableIndex] = numRegionsPerServerPerTable[serverIndex][tableIndex];
345 }
346 }
347 }
348
349 for (int i = 0; i < regions.length; i ++) {
350 HRegionInfo info = regions[i];
351 if (RegionReplicaUtil.isDefaultReplica(info)) {
352 regionIndexToPrimaryIndex[i] = i;
353 } else {
354 hasRegionReplicas = true;
355 HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
356 regionIndexToPrimaryIndex[i] =
357 regionsToIndex.containsKey(primaryInfo) ?
358 regionsToIndex.get(primaryInfo):
359 -1;
360 }
361 }
362
363 for (int i = 0; i < regionsPerServer.length; i++) {
364 primariesOfRegionsPerServer[i] = new int[regionsPerServer[i].length];
365 for (int j = 0; j < regionsPerServer[i].length; j++) {
366 int primaryIndex = regionIndexToPrimaryIndex[regionsPerServer[i][j]];
367 primariesOfRegionsPerServer[i][j] = primaryIndex;
368 }
369
370 Arrays.sort(primariesOfRegionsPerServer[i]);
371 }
372
373
374 if (multiServersPerHost) {
375 for (int i = 0 ; i < serversPerHost.length; i++) {
376 int numRegionsPerHost = 0;
377 for (int j = 0; j < serversPerHost[i].length; j++) {
378 numRegionsPerHost += regionsPerServer[serversPerHost[i][j]].length;
379 }
380 regionsPerHost[i] = new int[numRegionsPerHost];
381 primariesOfRegionsPerHost[i] = new int[numRegionsPerHost];
382 }
383 for (int i = 0 ; i < serversPerHost.length; i++) {
384 int numRegionPerHostIndex = 0;
385 for (int j = 0; j < serversPerHost[i].length; j++) {
386 for (int k = 0; k < regionsPerServer[serversPerHost[i][j]].length; k++) {
387 int region = regionsPerServer[serversPerHost[i][j]][k];
388 regionsPerHost[i][numRegionPerHostIndex] = region;
389 int primaryIndex = regionIndexToPrimaryIndex[region];
390 primariesOfRegionsPerHost[i][numRegionPerHostIndex] = primaryIndex;
391 numRegionPerHostIndex++;
392 }
393 }
394
395 Arrays.sort(primariesOfRegionsPerHost[i]);
396 }
397 }
398
399
400 if (numRacks > 1) {
401 for (int i = 0 ; i < serversPerRack.length; i++) {
402 int numRegionsPerRack = 0;
403 for (int j = 0; j < serversPerRack[i].length; j++) {
404 numRegionsPerRack += regionsPerServer[serversPerRack[i][j]].length;
405 }
406 regionsPerRack[i] = new int[numRegionsPerRack];
407 primariesOfRegionsPerRack[i] = new int[numRegionsPerRack];
408 }
409
410 for (int i = 0 ; i < serversPerRack.length; i++) {
411 int numRegionPerRackIndex = 0;
412 for (int j = 0; j < serversPerRack[i].length; j++) {
413 for (int k = 0; k < regionsPerServer[serversPerRack[i][j]].length; k++) {
414 int region = regionsPerServer[serversPerRack[i][j]][k];
415 regionsPerRack[i][numRegionPerRackIndex] = region;
416 int primaryIndex = regionIndexToPrimaryIndex[region];
417 primariesOfRegionsPerRack[i][numRegionPerRackIndex] = primaryIndex;
418 numRegionPerRackIndex++;
419 }
420 }
421
422 Arrays.sort(primariesOfRegionsPerRack[i]);
423 }
424 }
425 }
426
427
428 private void registerRegion(HRegionInfo region, int regionIndex, int serverIndex,
429 Map<String, Deque<RegionLoad>> loads, RegionLocationFinder regionFinder) {
430 String tableName = region.getTable().getNameAsString();
431 if (!tablesToIndex.containsKey(tableName)) {
432 tables.add(tableName);
433 tablesToIndex.put(tableName, tablesToIndex.size());
434 }
435 int tableIndex = tablesToIndex.get(tableName);
436
437 regionsToIndex.put(region, regionIndex);
438 regions[regionIndex] = region;
439 regionIndexToServerIndex[regionIndex] = serverIndex;
440 initialRegionIndexToServerIndex[regionIndex] = serverIndex;
441 regionIndexToTableIndex[regionIndex] = tableIndex;
442
443
444 if (loads != null) {
445 Deque<RegionLoad> rl = loads.get(region.getRegionNameAsString());
446
447 if (rl == null) {
448
449 rl = loads.get(region.getEncodedName());
450 }
451 regionLoads[regionIndex] = rl;
452 }
453
454 if (regionFinder != null) {
455
456 List<ServerName> loc = regionFinder.getTopBlockLocations(region);
457 regionLocations[regionIndex] = new int[loc.size()];
458 for (int i=0; i < loc.size(); i++) {
459 regionLocations[regionIndex][i] =
460 loc.get(i) == null ? -1 :
461 (serversToIndex.get(loc.get(i).getHostAndPort()) == null ? -1
462 : serversToIndex.get(loc.get(i).getHostAndPort()));
463 }
464 }
465 }
466
467
468 public static class Action {
469 public static enum Type {
470 ASSIGN_REGION,
471 MOVE_REGION,
472 SWAP_REGIONS,
473 NULL,
474 }
475
476 public Type type;
477 public Action (Type type) {this.type = type;}
478
479 public Action undoAction() { return this; }
480 @Override
481 public String toString() { return type + ":";}
482 }
483
484 public static class AssignRegionAction extends Action {
485 public int region;
486 public int server;
487 public AssignRegionAction(int region, int server) {
488 super(Type.ASSIGN_REGION);
489 this.region = region;
490 this.server = server;
491 }
492 @Override
493 public Action undoAction() {
494
495
496 throw new NotImplementedException();
497 }
498 @Override
499 public String toString() {
500 return type + ": " + region + ":" + server;
501 }
502 }
503
504 public static class MoveRegionAction extends Action {
505 public int region;
506 public int fromServer;
507 public int toServer;
508
509 public MoveRegionAction(int region, int fromServer, int toServer) {
510 super(Type.MOVE_REGION);
511 this.fromServer = fromServer;
512 this.region = region;
513 this.toServer = toServer;
514 }
515 @Override
516 public Action undoAction() {
517 return new MoveRegionAction (region, toServer, fromServer);
518 }
519 @Override
520 public String toString() {
521 return type + ": " + region + ":" + fromServer + " -> " + toServer;
522 }
523 }
524
525 public static class SwapRegionsAction extends Action {
526 public int fromServer;
527 public int fromRegion;
528 public int toServer;
529 public int toRegion;
530 public SwapRegionsAction(int fromServer, int fromRegion, int toServer, int toRegion) {
531 super(Type.SWAP_REGIONS);
532 this.fromServer = fromServer;
533 this.fromRegion = fromRegion;
534 this.toServer = toServer;
535 this.toRegion = toRegion;
536 }
537 @Override
538 public Action undoAction() {
539 return new SwapRegionsAction (fromServer, toRegion, toServer, fromRegion);
540 }
541 @Override
542 public String toString() {
543 return type + ": " + fromRegion + ":" + fromServer + " <-> " + toRegion + ":" + toServer;
544 }
545 }
546
547 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NM_FIELD_NAMING_CONVENTION",
548 justification="Mistake. Too disruptive to change now")
549 public static final Action NullAction = new Action(Type.NULL);
550
551 public void doAction(Action action) {
552 switch (action.type) {
553 case NULL: break;
554 case ASSIGN_REGION:
555
556 assert action instanceof AssignRegionAction: action.getClass();
557 AssignRegionAction ar = (AssignRegionAction) action;
558 regionsPerServer[ar.server] = addRegion(regionsPerServer[ar.server], ar.region);
559 regionMoved(ar.region, -1, ar.server);
560 break;
561 case MOVE_REGION:
562 assert action instanceof MoveRegionAction: action.getClass();
563 MoveRegionAction mra = (MoveRegionAction) action;
564 regionsPerServer[mra.fromServer] = removeRegion(regionsPerServer[mra.fromServer], mra.region);
565 regionsPerServer[mra.toServer] = addRegion(regionsPerServer[mra.toServer], mra.region);
566 regionMoved(mra.region, mra.fromServer, mra.toServer);
567 break;
568 case SWAP_REGIONS:
569 assert action instanceof SwapRegionsAction: action.getClass();
570 SwapRegionsAction a = (SwapRegionsAction) action;
571 regionsPerServer[a.fromServer] = replaceRegion(regionsPerServer[a.fromServer], a.fromRegion, a.toRegion);
572 regionsPerServer[a.toServer] = replaceRegion(regionsPerServer[a.toServer], a.toRegion, a.fromRegion);
573 regionMoved(a.fromRegion, a.fromServer, a.toServer);
574 regionMoved(a.toRegion, a.toServer, a.fromServer);
575 break;
576 default:
577 throw new RuntimeException("Uknown action:" + action.type);
578 }
579 }
580
581
582
583
584
585
586
587
588 boolean wouldLowerAvailability(HRegionInfo regionInfo, ServerName serverName) {
589 if (!serversToIndex.containsKey(serverName.getHostAndPort())) {
590 return false;
591 }
592 int server = serversToIndex.get(serverName.getHostAndPort());
593 int region = regionsToIndex.get(regionInfo);
594
595 int primary = regionIndexToPrimaryIndex[region];
596
597
598
599
600 if (contains(primariesOfRegionsPerServer[server], primary)) {
601
602 for (int i = 0; i < primariesOfRegionsPerServer.length; i++) {
603 if (i != server && !contains(primariesOfRegionsPerServer[i], primary)) {
604 return true;
605 }
606 }
607 return false;
608 }
609
610
611 if (multiServersPerHost) {
612 int host = serverIndexToHostIndex[server];
613 if (contains(primariesOfRegionsPerHost[host], primary)) {
614
615 for (int i = 0; i < primariesOfRegionsPerHost.length; i++) {
616 if (i != host && !contains(primariesOfRegionsPerHost[i], primary)) {
617 return true;
618 }
619 }
620 return false;
621 }
622 }
623
624
625 if (numRacks > 1) {
626 int rack = serverIndexToRackIndex[server];
627 if (contains(primariesOfRegionsPerRack[rack], primary)) {
628
629 for (int i = 0; i < primariesOfRegionsPerRack.length; i++) {
630 if (i != rack && !contains(primariesOfRegionsPerRack[i], primary)) {
631 return true;
632 }
633 }
634 return false;
635 }
636 }
637 return false;
638 }
639
640 void doAssignRegion(HRegionInfo regionInfo, ServerName serverName) {
641 if (!serversToIndex.containsKey(serverName.getHostAndPort())) {
642 return;
643 }
644 int server = serversToIndex.get(serverName.getHostAndPort());
645 int region = regionsToIndex.get(regionInfo);
646 doAction(new AssignRegionAction(region, server));
647 }
648
649 void regionMoved(int region, int oldServer, int newServer) {
650 regionIndexToServerIndex[region] = newServer;
651 if (initialRegionIndexToServerIndex[region] == newServer) {
652 numMovedRegions--;
653 } else if (oldServer >= 0 && initialRegionIndexToServerIndex[region] == oldServer) {
654 numMovedRegions++;
655 }
656 int tableIndex = regionIndexToTableIndex[region];
657 if (oldServer >= 0) {
658 numRegionsPerServerPerTable[oldServer][tableIndex]--;
659 }
660 numRegionsPerServerPerTable[newServer][tableIndex]++;
661
662
663 if (numRegionsPerServerPerTable[newServer][tableIndex] > numMaxRegionsPerTable[tableIndex]) {
664 numRegionsPerServerPerTable[newServer][tableIndex] = numMaxRegionsPerTable[tableIndex];
665 } else if (oldServer >= 0 && (numRegionsPerServerPerTable[oldServer][tableIndex] + 1)
666 == numMaxRegionsPerTable[tableIndex]) {
667
668 for (int serverIndex = 0 ; serverIndex < numRegionsPerServerPerTable.length; serverIndex++) {
669 if (numRegionsPerServerPerTable[serverIndex][tableIndex] > numMaxRegionsPerTable[tableIndex]) {
670 numMaxRegionsPerTable[tableIndex] = numRegionsPerServerPerTable[serverIndex][tableIndex];
671 }
672 }
673 }
674
675
676 int primary = regionIndexToPrimaryIndex[region];
677 if (oldServer >= 0) {
678 primariesOfRegionsPerServer[oldServer] = removeRegion(
679 primariesOfRegionsPerServer[oldServer], primary);
680 }
681 primariesOfRegionsPerServer[newServer] = addRegionSorted(
682 primariesOfRegionsPerServer[newServer], primary);
683
684
685 if (multiServersPerHost) {
686 int oldHost = oldServer >= 0 ? serverIndexToHostIndex[oldServer] : -1;
687 int newHost = serverIndexToHostIndex[newServer];
688 if (newHost != oldHost) {
689 regionsPerHost[newHost] = addRegion(regionsPerHost[newHost], region);
690 primariesOfRegionsPerHost[newHost] = addRegionSorted(primariesOfRegionsPerHost[newHost], primary);
691 if (oldHost >= 0) {
692 regionsPerHost[oldHost] = removeRegion(regionsPerHost[oldHost], region);
693 primariesOfRegionsPerHost[oldHost] = removeRegion(
694 primariesOfRegionsPerHost[oldHost], primary);
695 }
696 }
697 }
698
699
700 if (numRacks > 1) {
701 int oldRack = oldServer >= 0 ? serverIndexToRackIndex[oldServer] : -1;
702 int newRack = serverIndexToRackIndex[newServer];
703 if (newRack != oldRack) {
704 regionsPerRack[newRack] = addRegion(regionsPerRack[newRack], region);
705 primariesOfRegionsPerRack[newRack] = addRegionSorted(primariesOfRegionsPerRack[newRack], primary);
706 if (oldRack >= 0) {
707 regionsPerRack[oldRack] = removeRegion(regionsPerRack[oldRack], region);
708 primariesOfRegionsPerRack[oldRack] = removeRegion(
709 primariesOfRegionsPerRack[oldRack], primary);
710 }
711 }
712 }
713 }
714
715 int[] removeRegion(int[] regions, int regionIndex) {
716
717 int[] newRegions = new int[regions.length - 1];
718 int i = 0;
719 for (i = 0; i < regions.length; i++) {
720 if (regions[i] == regionIndex) {
721 break;
722 }
723 newRegions[i] = regions[i];
724 }
725 System.arraycopy(regions, i+1, newRegions, i, newRegions.length - i);
726 return newRegions;
727 }
728
729 int[] addRegion(int[] regions, int regionIndex) {
730 int[] newRegions = new int[regions.length + 1];
731 System.arraycopy(regions, 0, newRegions, 0, regions.length);
732 newRegions[newRegions.length - 1] = regionIndex;
733 return newRegions;
734 }
735
736 int[] addRegionSorted(int[] regions, int regionIndex) {
737 int[] newRegions = new int[regions.length + 1];
738 int i = 0;
739 for (i = 0; i < regions.length; i++) {
740 if (regions[i] > regionIndex) {
741 break;
742 }
743 }
744 System.arraycopy(regions, 0, newRegions, 0, i);
745 System.arraycopy(regions, i, newRegions, i+1, regions.length - i);
746 newRegions[i] = regionIndex;
747
748 return newRegions;
749 }
750
751 int[] replaceRegion(int[] regions, int regionIndex, int newRegionIndex) {
752 int i = 0;
753 for (i = 0; i < regions.length; i++) {
754 if (regions[i] == regionIndex) {
755 regions[i] = newRegionIndex;
756 break;
757 }
758 }
759 return regions;
760 }
761
762 void sortServersByRegionCount() {
763 Arrays.sort(serverIndicesSortedByRegionCount, numRegionsComparator);
764 }
765
766 int getNumRegions(int server) {
767 return regionsPerServer[server].length;
768 }
769
770 boolean contains(int[] arr, int val) {
771 return Arrays.binarySearch(arr, val) >= 0;
772 }
773
774 private Comparator<Integer> numRegionsComparator = new Comparator<Integer>() {
775 @Override
776 public int compare(Integer integer, Integer integer2) {
777 return Integer.valueOf(getNumRegions(integer)).compareTo(getNumRegions(integer2));
778 }
779 };
780
781 void sortServersByLocality() {
782 Arrays.sort(serverIndicesSortedByLocality, localityComparator);
783 }
784
785 float getLocality(int server) {
786 return localityPerServer[server];
787 }
788
789 private Comparator<Integer> localityComparator = new Comparator<Integer>() {
790 @Override
791 public int compare(Integer integer, Integer integer2) {
792 float locality1 = getLocality(integer);
793 float locality2 = getLocality(integer2);
794 if (locality1 < locality2) {
795 return -1;
796 } else if (locality1 > locality2) {
797 return 1;
798 } else {
799 return 0;
800 }
801 }
802 };
803
804 int getLowestLocalityRegionServer() {
805 if (regionFinder == null) {
806 return -1;
807 } else {
808 sortServersByLocality();
809
810 int i = 0;
811 int lowestLocalityServerIndex = serverIndicesSortedByLocality[i];
812 while (localityPerServer[lowestLocalityServerIndex] == 0
813 && (regionsPerServer[lowestLocalityServerIndex].length == 0)) {
814 i++;
815 lowestLocalityServerIndex = serverIndicesSortedByLocality[i];
816 }
817 if (LOG.isTraceEnabled()) {
818 LOG.trace("Lowest locality region server with non zero regions is "
819 + servers[lowestLocalityServerIndex].getHostname() + " with locality "
820 + localityPerServer[lowestLocalityServerIndex]);
821 }
822 return lowestLocalityServerIndex;
823 }
824 }
825
826 int getLowestLocalityRegionOnServer(int serverIndex) {
827 if (regionFinder != null) {
828 float lowestLocality = 1.0f;
829 int lowestLocalityRegionIndex = -1;
830 if (regionsPerServer[serverIndex].length == 0) {
831
832 return -1;
833 }
834 for (int j = 0; j < regionsPerServer[serverIndex].length; j++) {
835 int regionIndex = regionsPerServer[serverIndex][j];
836 HDFSBlocksDistribution distribution = regionFinder
837 .getBlockDistribution(regions[regionIndex]);
838 float locality = distribution.getBlockLocalityIndex(servers[serverIndex].getHostname());
839
840 if (distribution.getUniqueBlocksTotalWeight() == 0) {
841 continue;
842 }
843 if (locality < lowestLocality) {
844 lowestLocality = locality;
845 lowestLocalityRegionIndex = j;
846 }
847 }
848 if (lowestLocalityRegionIndex == -1) {
849 return -1;
850 }
851 if (LOG.isTraceEnabled()) {
852 LOG.trace("Lowest locality region is "
853 + regions[regionsPerServer[serverIndex][lowestLocalityRegionIndex]]
854 .getRegionNameAsString() + " with locality " + lowestLocality
855 + " and its region server contains " + regionsPerServer[serverIndex].length
856 + " regions");
857 }
858 return regionsPerServer[serverIndex][lowestLocalityRegionIndex];
859 } else {
860 return -1;
861 }
862 }
863
864 float getLocalityOfRegion(int region, int server) {
865 if (regionFinder != null) {
866 HDFSBlocksDistribution distribution = regionFinder.getBlockDistribution(regions[region]);
867 return distribution.getBlockLocalityIndex(servers[server].getHostname());
868 } else {
869 return 0f;
870 }
871 }
872
873
874
875
876
877 int getLeastLoadedTopServerForRegion(int region, int currentServer) {
878 if (regionFinder != null) {
879 List<ServerName> topLocalServers = regionFinder.getTopBlockLocations(regions[region],
880 servers[currentServer].getHostname());
881 int leastLoadedServerIndex = -1;
882 int load = Integer.MAX_VALUE;
883 for (ServerName sn : topLocalServers) {
884 if (!serversToIndex.containsKey(sn.getHostAndPort())) {
885 continue;
886 }
887 int index = serversToIndex.get(sn.getHostAndPort());
888 if (regionsPerServer[index] == null) {
889 continue;
890 }
891 int tempLoad = regionsPerServer[index].length;
892 if (tempLoad <= load) {
893 leastLoadedServerIndex = index;
894 load = tempLoad;
895 }
896 }
897 if (leastLoadedServerIndex != -1) {
898 LOG.debug("Pick the least loaded server " + servers[leastLoadedServerIndex].getHostname()
899 + " with better locality for region " + regions[region]);
900 }
901 return leastLoadedServerIndex;
902 } else {
903 return -1;
904 }
905 }
906
907 void calculateRegionServerLocalities() {
908 if (regionFinder == null) {
909 LOG.warn("Region location finder found null, skipping locality calculations.");
910 return;
911 }
912 for (int i = 0; i < regionsPerServer.length; i++) {
913 HDFSBlocksDistribution distribution = new HDFSBlocksDistribution();
914 if (regionsPerServer[i].length > 0) {
915 for (int j = 0; j < regionsPerServer[i].length; j++) {
916 int regionIndex = regionsPerServer[i][j];
917 distribution.add(regionFinder.getBlockDistribution(regions[regionIndex]));
918 }
919 } else {
920 LOG.debug("Server " + servers[i].getHostname() + " had 0 regions.");
921 }
922 localityPerServer[i] = distribution.getBlockLocalityIndex(servers[i].getHostname());
923 }
924 }
925
926 @VisibleForTesting
927 protected void setNumRegions(int numRegions) {
928 this.numRegions = numRegions;
929 }
930
931 @VisibleForTesting
932 protected void setNumMovedRegions(int numMovedRegions) {
933 this.numMovedRegions = numMovedRegions;
934 }
935
936 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="SBSC_USE_STRINGBUFFER_CONCATENATION",
937 justification="Not important but should be fixed")
938 @Override
939 public String toString() {
940 String desc = "Cluster{" +
941 "servers=[";
942 for(ServerName sn:servers) {
943 desc += sn.getHostAndPort() + ", ";
944 }
945 desc +=
946 ", serverIndicesSortedByRegionCount="+
947 Arrays.toString(serverIndicesSortedByRegionCount) +
948 ", regionsPerServer=[";
949
950 for (int[]r:regionsPerServer) {
951 desc += Arrays.toString(r);
952 }
953 desc += "]" +
954 ", numMaxRegionsPerTable=" +
955 Arrays.toString(numMaxRegionsPerTable) +
956 ", numRegions=" +
957 numRegions +
958 ", numServers=" +
959 numServers +
960 ", numTables=" +
961 numTables +
962 ", numMovedRegions=" +
963 numMovedRegions +
964 '}';
965 return desc;
966 }
967 }
968
969
970 protected float slop;
971 protected Configuration config;
972 protected RackManager rackManager;
973 private static final Random RANDOM = new Random(System.currentTimeMillis());
974 private static final Log LOG = LogFactory.getLog(BaseLoadBalancer.class);
975
976 public static final String TABLES_ON_MASTER =
977 "hbase.balancer.tablesOnMaster";
978
979 protected final Set<String> tablesOnMaster = new HashSet<String>();
980 protected MetricsBalancer metricsBalancer = null;
981 protected ClusterStatus clusterStatus = null;
982 protected ServerName masterServerName;
983 protected MasterServices services;
984
985 protected static String[] getTablesOnMaster(Configuration conf) {
986 String valueString = conf.get(TABLES_ON_MASTER);
987 if (valueString != null) {
988 valueString = valueString.trim();
989 }
990 if (valueString == null || valueString.equalsIgnoreCase("none")) {
991 return null;
992 }
993 return StringUtils.getStrings(valueString);
994 }
995
996
997
998
999 public static boolean tablesOnMaster(Configuration conf) {
1000 String[] tables = getTablesOnMaster(conf);
1001 return tables != null && tables.length > 0;
1002 }
1003
1004 public static boolean userTablesOnMaster(Configuration conf) {
1005 String[] tables = getTablesOnMaster(conf);
1006 if (tables == null || tables.length == 0) {
1007 return false;
1008 }
1009 for (String tn:tables) {
1010 if (!tn.startsWith("hbase:")) {
1011 return true;
1012 }
1013 }
1014 return false;
1015 }
1016
1017 @Override
1018 public void setConf(Configuration conf) {
1019 setSlop(conf);
1020 if (slop < 0) slop = 0;
1021 else if (slop > 1) slop = 1;
1022
1023 this.config = conf;
1024 String[] tables = getTablesOnMaster(conf);
1025 if (tables != null && tables.length > 0) {
1026 Collections.addAll(tablesOnMaster, tables);
1027 }
1028 this.rackManager = new RackManager(getConf());
1029 regionFinder.setConf(conf);
1030 }
1031
1032 protected void setSlop(Configuration conf) {
1033 this.slop = conf.getFloat("hbase.regions.slop", (float) 0.2);
1034 }
1035
1036
1037
1038
1039
1040 public boolean shouldBeOnMaster(HRegionInfo region) {
1041 return tablesOnMaster.contains(region.getTable().getNameAsString())
1042 && region.getReplicaId() == HRegionInfo.DEFAULT_REPLICA_ID;
1043 }
1044
1045
1046
1047
1048 protected List<RegionPlan> balanceMasterRegions(
1049 Map<ServerName, List<HRegionInfo>> clusterMap) {
1050 if (masterServerName == null
1051 || clusterMap == null || clusterMap.size() <= 1) return null;
1052 List<RegionPlan> plans = null;
1053 List<HRegionInfo> regions = clusterMap.get(masterServerName);
1054 if (regions != null) {
1055 Iterator<ServerName> keyIt = null;
1056 for (HRegionInfo region: regions) {
1057 if (shouldBeOnMaster(region)) continue;
1058
1059
1060 if (keyIt == null || !keyIt.hasNext()) {
1061 keyIt = clusterMap.keySet().iterator();
1062 }
1063 ServerName dest = keyIt.next();
1064 if (masterServerName.equals(dest)) {
1065 if (!keyIt.hasNext()) {
1066 keyIt = clusterMap.keySet().iterator();
1067 }
1068 dest = keyIt.next();
1069 }
1070
1071
1072 RegionPlan plan = new RegionPlan(region, masterServerName, dest);
1073 if (plans == null) {
1074 plans = new ArrayList<RegionPlan>();
1075 }
1076 plans.add(plan);
1077 }
1078 }
1079 for (Map.Entry<ServerName, List<HRegionInfo>> server: clusterMap.entrySet()) {
1080 if (masterServerName.equals(server.getKey())) continue;
1081 for (HRegionInfo region: server.getValue()) {
1082 if (!shouldBeOnMaster(region)) continue;
1083
1084
1085 RegionPlan plan = new RegionPlan(region, server.getKey(), masterServerName);
1086 if (plans == null) {
1087 plans = new ArrayList<RegionPlan>();
1088 }
1089 plans.add(plan);
1090 }
1091 }
1092 return plans;
1093 }
1094
1095
1096
1097
1098 protected Map<ServerName, List<HRegionInfo>> assignMasterRegions(
1099 Collection<HRegionInfo> regions, List<ServerName> servers) {
1100 if (servers == null || regions == null || regions.isEmpty()) {
1101 return null;
1102 }
1103 Map<ServerName, List<HRegionInfo>> assignments
1104 = new TreeMap<ServerName, List<HRegionInfo>>();
1105 if (masterServerName != null && servers.contains(masterServerName)) {
1106 assignments.put(masterServerName, new ArrayList<HRegionInfo>());
1107 for (HRegionInfo region: regions) {
1108 if (shouldBeOnMaster(region)) {
1109 assignments.get(masterServerName).add(region);
1110 }
1111 }
1112 }
1113 return assignments;
1114 }
1115
1116 @Override
1117 public Configuration getConf() {
1118 return this.config;
1119 }
1120
1121 @Override
1122 public synchronized void setClusterStatus(ClusterStatus st) {
1123 this.clusterStatus = st;
1124 regionFinder.setClusterStatus(st);
1125 }
1126
1127 @Override
1128 public void setMasterServices(MasterServices masterServices) {
1129 masterServerName = masterServices.getServerName();
1130 this.services = masterServices;
1131 this.regionFinder.setServices(masterServices);
1132 }
1133
1134 public void setRackManager(RackManager rackManager) {
1135 this.rackManager = rackManager;
1136 }
1137
1138 protected boolean needsBalance(Cluster c) {
1139 ClusterLoadState cs = new ClusterLoadState(c.clusterState);
1140 if (cs.getNumServers() < MIN_SERVER_BALANCE) {
1141 if (LOG.isDebugEnabled()) {
1142 LOG.debug("Not running balancer because only " + cs.getNumServers()
1143 + " active regionserver(s)");
1144 }
1145 return false;
1146 }
1147 if(areSomeRegionReplicasColocated(c)) return true;
1148
1149
1150 float average = cs.getLoadAverage();
1151 int floor = (int) Math.floor(average * (1 - slop));
1152 int ceiling = (int) Math.ceil(average * (1 + slop));
1153 if (!(cs.getMaxLoad() > ceiling || cs.getMinLoad() < floor)) {
1154 NavigableMap<ServerAndLoad, List<HRegionInfo>> serversByLoad = cs.getServersByLoad();
1155 if (LOG.isTraceEnabled()) {
1156
1157 LOG.trace("Skipping load balancing because balanced cluster; " +
1158 "servers=" + cs.getNumServers() +
1159 " regions=" + cs.getNumRegions() + " average=" + average +
1160 " mostloaded=" + serversByLoad.lastKey().getLoad() +
1161 " leastloaded=" + serversByLoad.firstKey().getLoad());
1162 }
1163 return false;
1164 }
1165 return true;
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175 protected boolean areSomeRegionReplicasColocated(Cluster c) {
1176 return false;
1177 }
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196 @Override
1197 public Map<ServerName, List<HRegionInfo>> roundRobinAssignment(List<HRegionInfo> regions,
1198 List<ServerName> servers) {
1199 metricsBalancer.incrMiscInvocations();
1200 Map<ServerName, List<HRegionInfo>> assignments = assignMasterRegions(regions, servers);
1201 if (assignments != null && !assignments.isEmpty()) {
1202 servers = new ArrayList<ServerName>(servers);
1203
1204 servers.remove(masterServerName);
1205 List<HRegionInfo> masterRegions = assignments.get(masterServerName);
1206 if (!masterRegions.isEmpty()) {
1207 regions = new ArrayList<HRegionInfo>(regions);
1208 for (HRegionInfo region: masterRegions) {
1209 regions.remove(region);
1210 }
1211 }
1212 }
1213 if (regions == null || regions.isEmpty()) {
1214 return assignments;
1215 }
1216
1217 int numServers = servers == null ? 0 : servers.size();
1218 if (numServers == 0) {
1219 LOG.warn("Wanted to do round robin assignment but no servers to assign to");
1220 return null;
1221 }
1222
1223
1224
1225
1226
1227
1228 if (numServers == 1) {
1229 ServerName server = servers.get(0);
1230 assignments.put(server, new ArrayList<HRegionInfo>(regions));
1231 return assignments;
1232 }
1233
1234 Cluster cluster = createCluster(servers, regions, false);
1235 List<HRegionInfo> unassignedRegions = new ArrayList<HRegionInfo>();
1236
1237 roundRobinAssignment(cluster, regions, unassignedRegions,
1238 servers, assignments);
1239
1240 List<HRegionInfo> lastFewRegions = new ArrayList<HRegionInfo>();
1241
1242 int serverIdx = RANDOM.nextInt(numServers);
1243 for (HRegionInfo region : unassignedRegions) {
1244 boolean assigned = false;
1245 for (int j = 0; j < numServers; j++) {
1246 ServerName serverName = servers.get((j + serverIdx) % numServers);
1247 if (!cluster.wouldLowerAvailability(region, serverName)) {
1248 List<HRegionInfo> serverRegions = assignments.get(serverName);
1249 if (serverRegions == null) {
1250 serverRegions = new ArrayList<HRegionInfo>();
1251 assignments.put(serverName, serverRegions);
1252 }
1253 serverRegions.add(region);
1254 cluster.doAssignRegion(region, serverName);
1255 serverIdx = (j + serverIdx + 1) % numServers;
1256 assigned = true;
1257 break;
1258 }
1259 }
1260 if (!assigned) {
1261 lastFewRegions.add(region);
1262 }
1263 }
1264
1265
1266 for (HRegionInfo region : lastFewRegions) {
1267 int i = RANDOM.nextInt(numServers);
1268 ServerName server = servers.get(i);
1269 List<HRegionInfo> serverRegions = assignments.get(server);
1270 if (serverRegions == null) {
1271 serverRegions = new ArrayList<HRegionInfo>();
1272 assignments.put(server, serverRegions);
1273 }
1274 serverRegions.add(region);
1275 cluster.doAssignRegion(region, server);
1276 }
1277 return assignments;
1278 }
1279
1280 protected Cluster createCluster(List<ServerName> servers,
1281 Collection<HRegionInfo> regions, boolean forceRefresh) {
1282 if (forceRefresh) {
1283 regionFinder.refreshAndWait(regions);
1284 }
1285
1286
1287
1288
1289 Map<ServerName, List<HRegionInfo>> clusterState = getRegionAssignmentsByServer(regions);
1290
1291 for (ServerName server : servers) {
1292 if (!clusterState.containsKey(server)) {
1293 clusterState.put(server, EMPTY_REGION_LIST);
1294 }
1295 }
1296 return new Cluster(regions, clusterState, null, this.regionFinder,
1297 rackManager);
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317 @Override
1318 public Map<HRegionInfo, ServerName> immediateAssignment(List<HRegionInfo> regions,
1319 List<ServerName> servers) {
1320 metricsBalancer.incrMiscInvocations();
1321 if (servers == null || servers.isEmpty()) {
1322 LOG.warn("Wanted to do random assignment but no servers to assign to");
1323 return null;
1324 }
1325
1326 Map<HRegionInfo, ServerName> assignments = new TreeMap<HRegionInfo, ServerName>();
1327 for (HRegionInfo region : regions) {
1328 assignments.put(region, randomAssignment(region, servers));
1329 }
1330 return assignments;
1331 }
1332
1333
1334
1335
1336 @Override
1337 public ServerName randomAssignment(HRegionInfo regionInfo, List<ServerName> servers) {
1338 metricsBalancer.incrMiscInvocations();
1339 if (servers != null && servers.contains(masterServerName)) {
1340 if (shouldBeOnMaster(regionInfo)) {
1341 return masterServerName;
1342 }
1343 servers = new ArrayList<ServerName>(servers);
1344
1345 servers.remove(masterServerName);
1346 }
1347
1348 int numServers = servers == null ? 0 : servers.size();
1349 if (numServers == 0) {
1350 LOG.warn("Wanted to do retain assignment but no servers to assign to");
1351 return null;
1352 }
1353 if (numServers == 1) {
1354 return servers.get(0);
1355 }
1356
1357 List<HRegionInfo> regions = Lists.newArrayList(regionInfo);
1358 Cluster cluster = createCluster(servers, regions, false);
1359 return randomAssignment(cluster, regionInfo, servers);
1360 }
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379 @Override
1380 public Map<ServerName, List<HRegionInfo>> retainAssignment(Map<HRegionInfo, ServerName> regions,
1381 List<ServerName> servers) {
1382
1383 metricsBalancer.incrMiscInvocations();
1384 Map<ServerName, List<HRegionInfo>> assignments
1385 = assignMasterRegions(regions.keySet(), servers);
1386 if (assignments != null && !assignments.isEmpty()) {
1387 servers = new ArrayList<ServerName>(servers);
1388
1389 servers.remove(masterServerName);
1390 List<HRegionInfo> masterRegions = assignments.get(masterServerName);
1391 if (!masterRegions.isEmpty()) {
1392 regions = new HashMap<HRegionInfo, ServerName>(regions);
1393 for (HRegionInfo region: masterRegions) {
1394 regions.remove(region);
1395 }
1396 }
1397 }
1398 if (regions == null || regions.isEmpty()) {
1399 return assignments;
1400 }
1401
1402 int numServers = servers == null ? 0 : servers.size();
1403 if (numServers == 0) {
1404 LOG.warn("Wanted to do retain assignment but no servers to assign to");
1405 return null;
1406 }
1407 if (numServers == 1) {
1408 ServerName server = servers.get(0);
1409 assignments.put(server, new ArrayList<HRegionInfo>(regions.keySet()));
1410 return assignments;
1411 }
1412
1413
1414
1415
1416
1417
1418
1419 ArrayListMultimap<String, ServerName> serversByHostname = ArrayListMultimap.create();
1420 for (ServerName server : servers) {
1421 assignments.put(server, new ArrayList<HRegionInfo>());
1422 serversByHostname.put(server.getHostname(), server);
1423 }
1424
1425
1426
1427
1428 Set<String> oldHostsNoLongerPresent = Sets.newTreeSet();
1429
1430 int numRandomAssignments = 0;
1431 int numRetainedAssigments = 0;
1432
1433 Cluster cluster = createCluster(servers, regions.keySet(), true);
1434
1435 for (Map.Entry<HRegionInfo, ServerName> entry : regions.entrySet()) {
1436 HRegionInfo region = entry.getKey();
1437 ServerName oldServerName = entry.getValue();
1438 List<ServerName> localServers = new ArrayList<ServerName>();
1439 if (oldServerName != null) {
1440 localServers = serversByHostname.get(oldServerName.getHostname());
1441 }
1442 if (localServers.isEmpty()) {
1443
1444
1445 ServerName randomServer = randomAssignment(cluster, region, servers);
1446 assignments.get(randomServer).add(region);
1447 numRandomAssignments++;
1448 if (oldServerName != null) oldHostsNoLongerPresent.add(oldServerName.getHostname());
1449 } else if (localServers.size() == 1) {
1450
1451 ServerName target = localServers.get(0);
1452 assignments.get(target).add(region);
1453 cluster.doAssignRegion(region, target);
1454 numRetainedAssigments++;
1455 } else {
1456
1457 if (localServers.contains(oldServerName)) {
1458 assignments.get(oldServerName).add(region);
1459 cluster.doAssignRegion(region, oldServerName);
1460 } else {
1461 ServerName target = null;
1462 for (ServerName tmp: localServers) {
1463 if (tmp.getPort() == oldServerName.getPort()) {
1464 target = tmp;
1465 break;
1466 }
1467 }
1468 if (target == null) {
1469 target = randomAssignment(cluster, region, localServers);
1470 }
1471 assignments.get(target).add(region);
1472 }
1473 numRetainedAssigments++;
1474 }
1475 }
1476
1477 String randomAssignMsg = "";
1478 if (numRandomAssignments > 0) {
1479 randomAssignMsg =
1480 numRandomAssignments + " regions were assigned "
1481 + "to random hosts, since the old hosts for these regions are no "
1482 + "longer present in the cluster. These hosts were:\n "
1483 + Joiner.on("\n ").join(oldHostsNoLongerPresent);
1484 }
1485
1486 LOG.info("Reassigned " + regions.size() + " regions. " + numRetainedAssigments
1487 + " retained the pre-restart assignment. " + randomAssignMsg);
1488 return assignments;
1489 }
1490
1491 @Override
1492 public void initialize() throws HBaseIOException{
1493 }
1494
1495 @Override
1496 public void regionOnline(HRegionInfo regionInfo, ServerName sn) {
1497 }
1498
1499 @Override
1500 public void regionOffline(HRegionInfo regionInfo) {
1501 }
1502
1503 @Override
1504 public boolean isStopped() {
1505 return stopped;
1506 }
1507
1508 @Override
1509 public void stop(String why) {
1510 LOG.info("Load Balancer stop requested: "+why);
1511 stopped = true;
1512 }
1513
1514
1515
1516
1517 private ServerName randomAssignment(Cluster cluster, HRegionInfo regionInfo,
1518 List<ServerName> servers) {
1519 int numServers = servers.size();
1520 ServerName sn = null;
1521 final int maxIterations = numServers * 4;
1522 int iterations = 0;
1523
1524 do {
1525 int i = RANDOM.nextInt(numServers);
1526 sn = servers.get(i);
1527 } while (cluster.wouldLowerAvailability(regionInfo, sn)
1528 && iterations++ < maxIterations);
1529 cluster.doAssignRegion(regionInfo, sn);
1530 return sn;
1531 }
1532
1533
1534
1535
1536 private void roundRobinAssignment(Cluster cluster, List<HRegionInfo> regions,
1537 List<HRegionInfo> unassignedRegions, List<ServerName> servers,
1538 Map<ServerName, List<HRegionInfo>> assignments) {
1539
1540 int numServers = servers.size();
1541 int numRegions = regions.size();
1542 int max = (int) Math.ceil((float) numRegions / numServers);
1543 int serverIdx = 0;
1544 if (numServers > 1) {
1545 serverIdx = RANDOM.nextInt(numServers);
1546 }
1547 int regionIdx = 0;
1548
1549 for (int j = 0; j < numServers; j++) {
1550 ServerName server = servers.get((j + serverIdx) % numServers);
1551 List<HRegionInfo> serverRegions = new ArrayList<HRegionInfo>(max);
1552 for (int i = regionIdx; i < numRegions; i += numServers) {
1553 HRegionInfo region = regions.get(i % numRegions);
1554 if (cluster.wouldLowerAvailability(region, server)) {
1555 unassignedRegions.add(region);
1556 } else {
1557 serverRegions.add(region);
1558 cluster.doAssignRegion(region, server);
1559 }
1560 }
1561 assignments.put(server, serverRegions);
1562 regionIdx++;
1563 }
1564 }
1565
1566 protected Map<ServerName, List<HRegionInfo>> getRegionAssignmentsByServer(
1567 Collection<HRegionInfo> regions) {
1568 if (this.services != null && this.services.getAssignmentManager() != null) {
1569 return this.services.getAssignmentManager().getSnapShotOfAssignment(regions);
1570 } else {
1571 return new HashMap<ServerName, List<HRegionInfo>>();
1572 }
1573 }
1574
1575 @Override
1576 public void onConfigurationChange(Configuration conf) {
1577 }
1578 }