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 static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.HashSet;
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.Queue;
33 import java.util.Random;
34 import java.util.Set;
35 import java.util.SortedSet;
36 import java.util.TreeMap;
37 import java.util.TreeSet;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.hadoop.conf.Configuration;
42 import org.apache.hadoop.hbase.HBaseConfiguration;
43 import org.apache.hadoop.hbase.HRegionInfo;
44 import org.apache.hadoop.hbase.ServerName;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
47 import org.apache.hadoop.hbase.master.RackManager;
48 import org.apache.hadoop.hbase.master.RegionPlan;
49 import org.apache.hadoop.hbase.util.Bytes;
50 import org.apache.hadoop.net.DNSToSwitchMapping;
51 import org.junit.Assert;
52 import org.junit.BeforeClass;
53
54
55
56
57
58
59
60 public class BalancerTestBase {
61 private static final Log LOG = LogFactory.getLog(BalancerTestBase.class);
62 protected static Random rand = new Random();
63 static int regionId = 0;
64 protected static Configuration conf;
65 protected static StochasticLoadBalancer loadBalancer;
66
67 @BeforeClass
68 public static void beforeAllTests() throws Exception {
69 conf = HBaseConfiguration.create();
70 conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class);
71 conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 0.75f);
72 conf.setFloat("hbase.regions.slop", 0.0f);
73 conf.setFloat("hbase.master.balancer.stochastic.localityCost", 0);
74 loadBalancer = new StochasticLoadBalancer();
75 loadBalancer.setConf(conf);
76 }
77
78 protected int[] largeCluster = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
90 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
91 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 };
92
93
94 protected int[][] clusterStateMocks = new int[][]{
95
96 new int[]{0},
97 new int[]{1},
98 new int[]{10},
99
100 new int[]{0, 0},
101 new int[]{2, 0},
102 new int[]{2, 1},
103 new int[]{2, 2},
104 new int[]{2, 3},
105 new int[]{2, 4},
106 new int[]{1, 1},
107 new int[]{0, 1},
108 new int[]{10, 1},
109 new int[]{514, 1432},
110 new int[]{48, 53},
111
112 new int[]{0, 1, 2},
113 new int[]{1, 2, 3},
114 new int[]{0, 2, 2},
115 new int[]{0, 3, 0},
116 new int[]{0, 4, 0},
117 new int[]{20, 20, 0},
118
119 new int[]{0, 1, 2, 3},
120 new int[]{4, 0, 0, 0},
121 new int[]{5, 0, 0, 0},
122 new int[]{6, 6, 0, 0},
123 new int[]{6, 2, 0, 0},
124 new int[]{6, 1, 0, 0},
125 new int[]{6, 0, 0, 0},
126 new int[]{4, 4, 4, 7},
127 new int[]{4, 4, 4, 8},
128 new int[]{0, 0, 0, 7},
129
130 new int[]{1, 1, 1, 1, 4},
131
132 new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
133 new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
134 new int[]{6, 6, 5, 6, 6, 6, 6, 6, 6, 1},
135 new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 54},
136 new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 55},
137 new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 56},
138 new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 16},
139 new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 8},
140 new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 9},
141 new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 10},
142 new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 123},
143 new int[]{1, 1, 1, 1, 1, 1, 1, 1, 1, 155},
144 new int[]{10, 7, 12, 8, 11, 10, 9, 14},
145 new int[]{13, 14, 6, 10, 10, 10, 8, 10},
146 new int[]{130, 14, 60, 10, 100, 10, 80, 10},
147 new int[]{130, 140, 60, 100, 100, 100, 80, 100},
148 new int[]{0, 5 , 5, 5, 5},
149 largeCluster,
150
151 };
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170 protected int[][][] clusterRegionLocationMocks = new int[][][]{
171
172
173
174
175
176
177
178
179
180 new int[][]{
181 new int[]{1, 1, 1},
182 new int[]{2, 80, 1, 2, 0},
183 new int[]{0, 50, 1, 2},
184 new int[]{1, 70, 2, 0, 1},
185 },
186
187
188 new int[][]{
189 new int[]{1, 1, 1},
190 new int[]{2, 80, 1, 2, 0},
191 new int[]{0, 50, 1, 2},
192 new int[]{1, 70, -1, 2, 0, 1},
193 },
194 };
195
196
197 protected class MockCluster extends BaseLoadBalancer.Cluster {
198
199 protected int[][] localityValue = null;
200
201 protected MockCluster(int[][] regions) {
202
203
204 super(mockClusterServers(regions[0], 1), null, null, null);
205
206 localityValue = new int[regions.length-1][];
207
208
209
210
211
212 for (int i = 1; i < regions.length; i++){
213 int regionIndex = i - 1;
214 int serverIndex = regions[i][0];
215 int locality = regions[i][1];
216 int[] locations = Arrays.copyOfRange(regions[i], 2, regions[i].length);
217
218 regionIndexToServerIndex[regionIndex] = serverIndex;
219 localityValue[regionIndex] = new int[servers.length];
220 localityValue[regionIndex][serverIndex] = (locality > 100)? locality % 100 : locality;
221 regionLocations[regionIndex] = locations;
222 }
223 }
224
225 @Override
226 float getLocalityOfRegion(int region, int server) {
227
228 return localityValue[region][server] / 100.0f;
229 }
230 }
231
232
233 public static class MockMapping implements DNSToSwitchMapping {
234 public MockMapping(Configuration conf) {
235 }
236
237 public List<String> resolve(List<String> names) {
238 List<String> ret = new ArrayList<String>(names.size());
239 for (String name : names) {
240 ret.add("rack");
241 }
242 return ret;
243 }
244
245
246 public void reloadCachedMappings() {
247 }
248
249
250 public void reloadCachedMappings(List<String> arg0) {
251 }
252 }
253
254
255
256
257
258 public void assertClusterAsBalanced(List<ServerAndLoad> servers) {
259 int numServers = servers.size();
260 int numRegions = 0;
261 int maxRegions = 0;
262 int minRegions = Integer.MAX_VALUE;
263 for (ServerAndLoad server : servers) {
264 int nr = server.getLoad();
265 if (nr > maxRegions) {
266 maxRegions = nr;
267 }
268 if (nr < minRegions) {
269 minRegions = nr;
270 }
271 numRegions += nr;
272 }
273 if (maxRegions - minRegions < 2) {
274
275 return;
276 }
277 int min = numRegions / numServers;
278 int max = numRegions % numServers == 0 ? min : min + 1;
279
280 for (ServerAndLoad server : servers) {
281 assertTrue(server.getLoad() >= 0);
282 assertTrue(server.getLoad() <= max);
283 assertTrue(server.getLoad() >= min);
284 }
285 }
286
287
288
289
290 public void assertRegionReplicaPlacement(Map<ServerName, List<HRegionInfo>> serverMap, RackManager rackManager) {
291 TreeMap<String, Set<HRegionInfo>> regionsPerHost = new TreeMap<String, Set<HRegionInfo>>();
292 TreeMap<String, Set<HRegionInfo>> regionsPerRack = new TreeMap<String, Set<HRegionInfo>>();
293
294 for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
295 String hostname = entry.getKey().getHostname();
296 Set<HRegionInfo> infos = regionsPerHost.get(hostname);
297 if (infos == null) {
298 infos = new HashSet<HRegionInfo>();
299 regionsPerHost.put(hostname, infos);
300 }
301
302 for (HRegionInfo info : entry.getValue()) {
303 HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
304 if (!infos.add(primaryInfo)) {
305 Assert.fail("Two or more region replicas are hosted on the same host after balance");
306 }
307 }
308 }
309
310 if (rackManager == null) {
311 return;
312 }
313
314 for (Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
315 String rack = rackManager.getRack(entry.getKey());
316 Set<HRegionInfo> infos = regionsPerRack.get(rack);
317 if (infos == null) {
318 infos = new HashSet<HRegionInfo>();
319 regionsPerRack.put(rack, infos);
320 }
321
322 for (HRegionInfo info : entry.getValue()) {
323 HRegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info);
324 if (!infos.add(primaryInfo)) {
325 Assert.fail("Two or more region replicas are hosted on the same rack after balance");
326 }
327 }
328 }
329 }
330
331 protected String printStats(List<ServerAndLoad> servers) {
332 int numServers = servers.size();
333 int totalRegions = 0;
334 for (ServerAndLoad server : servers) {
335 totalRegions += server.getLoad();
336 }
337 float average = (float) totalRegions / numServers;
338 int max = (int) Math.ceil(average);
339 int min = (int) Math.floor(average);
340 return "[srvr=" + numServers + " rgns=" + totalRegions + " avg=" + average + " max=" + max
341 + " min=" + min + "]";
342 }
343
344 protected List<ServerAndLoad> convertToList(final Map<ServerName, List<HRegionInfo>> servers) {
345 List<ServerAndLoad> list = new ArrayList<ServerAndLoad>(servers.size());
346 for (Map.Entry<ServerName, List<HRegionInfo>> e : servers.entrySet()) {
347 list.add(new ServerAndLoad(e.getKey(), e.getValue().size()));
348 }
349 return list;
350 }
351
352 protected String printMock(List<ServerAndLoad> balancedCluster) {
353 SortedSet<ServerAndLoad> sorted = new TreeSet<ServerAndLoad>(balancedCluster);
354 ServerAndLoad[] arr = sorted.toArray(new ServerAndLoad[sorted.size()]);
355 StringBuilder sb = new StringBuilder(sorted.size() * 4 + 4);
356 sb.append("{ ");
357 for (int i = 0; i < arr.length; i++) {
358 if (i != 0) {
359 sb.append(" , ");
360 }
361 sb.append(arr[i].getServerName().getHostname());
362 sb.append(":");
363 sb.append(arr[i].getLoad());
364 }
365 sb.append(" }");
366 return sb.toString();
367 }
368
369
370
371
372
373
374
375
376
377 protected List<ServerAndLoad> reconcile(List<ServerAndLoad> list,
378 List<RegionPlan> plans,
379 Map<ServerName, List<HRegionInfo>> servers) {
380 List<ServerAndLoad> result = new ArrayList<ServerAndLoad>(list.size());
381
382 Map<ServerName, ServerAndLoad> map = new HashMap<ServerName, ServerAndLoad>(list.size());
383 for (ServerAndLoad sl : list) {
384 map.put(sl.getServerName(), sl);
385 }
386 if (plans != null) {
387 for (RegionPlan plan : plans) {
388 ServerName source = plan.getSource();
389
390 updateLoad(map, source, -1);
391 ServerName destination = plan.getDestination();
392 updateLoad(map, destination, +1);
393
394 servers.get(source).remove(plan.getRegionInfo());
395 servers.get(destination).add(plan.getRegionInfo());
396 }
397 }
398 result.clear();
399 result.addAll(map.values());
400 return result;
401 }
402
403 protected void updateLoad(final Map<ServerName, ServerAndLoad> map,
404 final ServerName sn,
405 final int diff) {
406 ServerAndLoad sal = map.get(sn);
407 if (sal == null) sal = new ServerAndLoad(sn, 0);
408 sal = new ServerAndLoad(sn, sal.getLoad() + diff);
409 map.put(sn, sal);
410 }
411
412 protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster) {
413 return mockClusterServers(mockCluster, -1);
414 }
415
416 protected BaseLoadBalancer.Cluster mockCluster(int[] mockCluster) {
417 return new BaseLoadBalancer.Cluster(
418 mockClusterServers(mockCluster, -1), null, null, null);
419 }
420
421 protected TreeMap<ServerName, List<HRegionInfo>> mockClusterServers(int[] mockCluster, int numTables) {
422 int numServers = mockCluster.length;
423 TreeMap<ServerName, List<HRegionInfo>> servers = new TreeMap<ServerName, List<HRegionInfo>>();
424 for (int i = 0; i < numServers; i++) {
425 int numRegions = mockCluster[i];
426 ServerAndLoad sal = randomServer(0);
427 List<HRegionInfo> regions = randomRegions(numRegions, numTables);
428 servers.put(sal.getServerName(), regions);
429 }
430 return servers;
431 }
432
433 private Queue<HRegionInfo> regionQueue = new LinkedList<HRegionInfo>();
434
435 protected List<HRegionInfo> randomRegions(int numRegions) {
436 return randomRegions(numRegions, -1);
437 }
438
439 protected List<HRegionInfo> randomRegions(int numRegions, int numTables) {
440 List<HRegionInfo> regions = new ArrayList<HRegionInfo>(numRegions);
441 byte[] start = new byte[16];
442 byte[] end = new byte[16];
443 rand.nextBytes(start);
444 rand.nextBytes(end);
445 for (int i = 0; i < numRegions; i++) {
446 if (!regionQueue.isEmpty()) {
447 regions.add(regionQueue.poll());
448 continue;
449 }
450 Bytes.putInt(start, 0, numRegions << 1);
451 Bytes.putInt(end, 0, (numRegions << 1) + 1);
452 TableName tableName =
453 TableName.valueOf("table" + (numTables > 0 ? rand.nextInt(numTables) : i));
454 HRegionInfo hri = new HRegionInfo(tableName, start, end, false, regionId++);
455 regions.add(hri);
456 }
457 return regions;
458 }
459
460 protected void returnRegions(List<HRegionInfo> regions) {
461 regionQueue.addAll(regions);
462 }
463
464 private Queue<ServerName> serverQueue = new LinkedList<ServerName>();
465
466 protected ServerAndLoad randomServer(final int numRegionsPerServer) {
467 if (!this.serverQueue.isEmpty()) {
468 ServerName sn = this.serverQueue.poll();
469 return new ServerAndLoad(sn, numRegionsPerServer);
470 }
471 String host = "srv" + rand.nextInt(Integer.MAX_VALUE);
472 int port = rand.nextInt(60000);
473 long startCode = rand.nextLong();
474 ServerName sn = ServerName.valueOf(host, port, startCode);
475 return new ServerAndLoad(sn, numRegionsPerServer);
476 }
477
478 protected List<ServerAndLoad> randomServers(int numServers, int numRegionsPerServer) {
479 List<ServerAndLoad> servers = new ArrayList<ServerAndLoad>(numServers);
480 for (int i = 0; i < numServers; i++) {
481 servers.add(randomServer(numRegionsPerServer));
482 }
483 return servers;
484 }
485
486 protected void returnServer(ServerName server) {
487 serverQueue.add(server);
488 }
489
490 protected void returnServers(List<ServerName> servers) {
491 this.serverQueue.addAll(servers);
492 }
493
494 protected void testWithCluster(int numNodes,
495 int numRegions,
496 int numRegionsPerServer,
497 int replication,
498 int numTables,
499 boolean assertFullyBalanced, boolean assertFullyBalancedForReplicas) {
500 Map<ServerName, List<HRegionInfo>> serverMap =
501 createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
502 testWithCluster(serverMap, null, assertFullyBalanced, assertFullyBalancedForReplicas);
503 }
504
505 protected void testWithCluster(Map<ServerName, List<HRegionInfo>> serverMap,
506 RackManager rackManager, boolean assertFullyBalanced, boolean assertFullyBalancedForReplicas) {
507 List<ServerAndLoad> list = convertToList(serverMap);
508 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list));
509
510 loadBalancer.setRackManager(rackManager);
511
512 List<RegionPlan> plans = loadBalancer.balanceCluster(serverMap);
513 assertNotNull(plans);
514
515
516 if (assertFullyBalanced || assertFullyBalancedForReplicas) {
517
518 List<ServerAndLoad> balancedCluster = reconcile(list, plans, serverMap);
519
520
521 LOG.info("Mock Balance : " + printMock(balancedCluster));
522
523 if (assertFullyBalanced) {
524 assertClusterAsBalanced(balancedCluster);
525 List<RegionPlan> secondPlans = loadBalancer.balanceCluster(serverMap);
526 assertNull(secondPlans);
527 }
528
529 if (assertFullyBalancedForReplicas) {
530 assertRegionReplicaPlacement(serverMap, rackManager);
531 }
532 }
533 }
534
535 protected Map<ServerName, List<HRegionInfo>> createServerMap(int numNodes,
536 int numRegions,
537 int numRegionsPerServer,
538 int replication,
539 int numTables) {
540
541
542
543 int[] cluster = new int[numNodes];
544 for (int i =0; i < numNodes; i++) {
545 cluster[i] = numRegionsPerServer;
546 }
547 cluster[cluster.length - 1] = numRegions - ((cluster.length - 1) * numRegionsPerServer);
548 Map<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(cluster, numTables);
549 if (replication > 0) {
550
551 for (List<HRegionInfo> regions : clusterState.values()) {
552 int length = regions.size();
553 for (int i = 0; i < length; i++) {
554 for (int r = 1; r < replication ; r++) {
555 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(i), r));
556 }
557 }
558 }
559 }
560
561 return clusterState;
562 }
563
564 }