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.assertEquals;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.when;
26
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Queue;
35 import java.util.TreeMap;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.hbase.ClusterStatus;
41 import org.apache.hadoop.hbase.HBaseConfiguration;
42 import org.apache.hadoop.hbase.HRegionInfo;
43 import org.apache.hadoop.hbase.RegionLoad;
44 import org.apache.hadoop.hbase.ServerLoad;
45 import org.apache.hadoop.hbase.ServerName;
46 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
47 import org.apache.hadoop.hbase.master.MockNoopMasterServices;
48 import org.apache.hadoop.hbase.master.RackManager;
49 import org.apache.hadoop.hbase.master.RegionPlan;
50 import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster;
51 import org.apache.hadoop.hbase.testclassification.FlakeyTests;
52 import org.apache.hadoop.hbase.testclassification.MediumTests;
53 import org.apache.hadoop.hbase.util.Bytes;
54 import org.junit.Ignore;
55 import org.junit.Test;
56 import org.junit.experimental.categories.Category;
57
58 @Category({FlakeyTests.class, MediumTests.class})
59 public class TestStochasticLoadBalancer extends BalancerTestBase {
60 public static final String REGION_KEY = "testRegion";
61 private static final Log LOG = LogFactory.getLog(TestStochasticLoadBalancer.class);
62
63 @Test
64 public void testKeepRegionLoad() throws Exception {
65
66 ServerName sn = ServerName.valueOf("test:8080", 100);
67 int numClusterStatusToAdd = 20000;
68 for (int i = 0; i < numClusterStatusToAdd; i++) {
69 ServerLoad sl = mock(ServerLoad.class);
70
71 RegionLoad rl = mock(RegionLoad.class);
72 when(rl.getStores()).thenReturn(i);
73
74 Map<byte[], RegionLoad> regionLoadMap =
75 new TreeMap<byte[], RegionLoad>(Bytes.BYTES_COMPARATOR);
76 regionLoadMap.put(Bytes.toBytes(REGION_KEY), rl);
77 when(sl.getRegionsLoad()).thenReturn(regionLoadMap);
78
79 ClusterStatus clusterStatus = mock(ClusterStatus.class);
80 when(clusterStatus.getServers()).thenReturn(Arrays.asList(sn));
81 when(clusterStatus.getLoad(sn)).thenReturn(sl);
82
83 loadBalancer.setClusterStatus(clusterStatus);
84 }
85
86 String regionNameAsString = HRegionInfo.getRegionNameAsString(Bytes.toBytes(REGION_KEY));
87 assertTrue(loadBalancer.loads.get(regionNameAsString) != null);
88 assertTrue(loadBalancer.loads.get(regionNameAsString).size() == 15);
89
90 Queue<RegionLoad> loads = loadBalancer.loads.get(regionNameAsString);
91 int i = 0;
92 while(loads.size() > 0) {
93 RegionLoad rl = loads.remove();
94 assertEquals(i + (numClusterStatusToAdd - 15), rl.getStores());
95 i ++;
96 }
97 }
98
99
100
101
102
103
104
105
106
107 @Test
108 public void testBalanceCluster() throws Exception {
109
110 for (int[] mockCluster : clusterStateMocks) {
111 Map<ServerName, List<HRegionInfo>> servers = mockClusterServers(mockCluster);
112 List<ServerAndLoad> list = convertToList(servers);
113 LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list));
114 List<RegionPlan> plans = loadBalancer.balanceCluster(servers);
115 List<ServerAndLoad> balancedCluster = reconcile(list, plans, servers);
116 LOG.info("Mock Balance : " + printMock(balancedCluster));
117 assertClusterAsBalanced(balancedCluster);
118 List<RegionPlan> secondPlans = loadBalancer.balanceCluster(servers);
119 assertNull(secondPlans);
120 for (Map.Entry<ServerName, List<HRegionInfo>> entry : servers.entrySet()) {
121 returnRegions(entry.getValue());
122 returnServer(entry.getKey());
123 }
124 }
125 }
126
127 @Test
128 public void testLocalityCost() throws Exception {
129 Configuration conf = HBaseConfiguration.create();
130 MockNoopMasterServices master = new MockNoopMasterServices();
131 StochasticLoadBalancer.CostFunction
132 costFunction = new StochasticLoadBalancer.LocalityCostFunction(conf, master);
133
134 for (int[][] clusterRegionLocations : clusterRegionLocationMocks) {
135 MockCluster cluster = new MockCluster(clusterRegionLocations);
136 costFunction.init(cluster);
137 double cost = costFunction.cost();
138
139 assertEquals(0.5f, cost, 0.001);
140 }
141 }
142
143 @Test
144 public void testMoveCost() throws Exception {
145 Configuration conf = HBaseConfiguration.create();
146 StochasticLoadBalancer.CostFunction
147 costFunction = new StochasticLoadBalancer.MoveCostFunction(conf);
148 for (int[] mockCluster : clusterStateMocks) {
149 BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
150 costFunction.init(cluster);
151 double cost = costFunction.cost();
152 assertEquals(0.0f, cost, 0.001);
153
154
155 cluster.setNumRegions(200);
156 cluster.setNumMovedRegions(10);
157 cost = costFunction.cost();
158 assertEquals(0.05f, cost, 0.001);
159 cluster.setNumMovedRegions(100);
160 cost = costFunction.cost();
161 assertEquals(0.5f, cost, 0.001);
162 cluster.setNumMovedRegions(200);
163 cost = costFunction.cost();
164 assertEquals(1.0f, cost, 0.001);
165
166
167
168 cluster.setNumRegions(10000);
169 cluster.setNumMovedRegions(250);
170 cost = costFunction.cost();
171 assertEquals(0.1f, cost, 0.001);
172 cluster.setNumMovedRegions(1250);
173 cost = costFunction.cost();
174 assertEquals(0.5f, cost, 0.001);
175 cluster.setNumMovedRegions(2500);
176 cost = costFunction.cost();
177 assertEquals(1.0f, cost, 0.01);
178 }
179 }
180
181 @Test
182 public void testSkewCost() {
183 Configuration conf = HBaseConfiguration.create();
184 StochasticLoadBalancer.CostFunction
185 costFunction = new StochasticLoadBalancer.RegionCountSkewCostFunction(conf);
186 for (int[] mockCluster : clusterStateMocks) {
187 costFunction.init(mockCluster(mockCluster));
188 double cost = costFunction.cost();
189 assertTrue(cost >= 0);
190 assertTrue(cost <= 1.01);
191 }
192
193 costFunction.init(mockCluster(new int[]{0, 0, 0, 0, 1}));
194 assertEquals(0,costFunction.cost(), 0.01);
195 costFunction.init(mockCluster(new int[]{0, 0, 0, 1, 1}));
196 assertEquals(0, costFunction.cost(), 0.01);
197 costFunction.init(mockCluster(new int[]{0, 0, 1, 1, 1}));
198 assertEquals(0, costFunction.cost(), 0.01);
199 costFunction.init(mockCluster(new int[]{0, 1, 1, 1, 1}));
200 assertEquals(0, costFunction.cost(), 0.01);
201 costFunction.init(mockCluster(new int[]{1, 1, 1, 1, 1}));
202 assertEquals(0, costFunction.cost(), 0.01);
203 costFunction.init(mockCluster(new int[]{10000, 0, 0, 0, 0}));
204 assertEquals(1, costFunction.cost(), 0.01);
205 }
206
207 @Test
208 public void testCostAfterUndoAction() {
209 final int runs = 10;
210 loadBalancer.setConf(conf);
211 for (int[] mockCluster : clusterStateMocks) {
212 BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
213 loadBalancer.initCosts(cluster);
214 for (int i = 0; i != runs; ++i) {
215 final double expectedCost = loadBalancer.computeCost(cluster, Double.MAX_VALUE);
216 Cluster.Action action = loadBalancer.nextAction(cluster);
217 cluster.doAction(action);
218 loadBalancer.updateCostsWithAction(cluster, action);
219 Cluster.Action undoAction = action.undoAction();
220 cluster.doAction(undoAction);
221 loadBalancer.updateCostsWithAction(cluster, undoAction);
222 final double actualCost = loadBalancer.computeCost(cluster, Double.MAX_VALUE);
223 assertEquals(expectedCost, actualCost, 0);
224 }
225 }
226 }
227
228 @Test
229 public void testTableSkewCost() {
230 Configuration conf = HBaseConfiguration.create();
231 StochasticLoadBalancer.CostFunction
232 costFunction = new StochasticLoadBalancer.TableSkewCostFunction(conf);
233 for (int[] mockCluster : clusterStateMocks) {
234 BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
235 costFunction.init(cluster);
236 double cost = costFunction.cost();
237 assertTrue(cost >= 0);
238 assertTrue(cost <= 1.01);
239 }
240 }
241
242 @Test
243 public void testCostFromArray() {
244 Configuration conf = HBaseConfiguration.create();
245 StochasticLoadBalancer.CostFromRegionLoadFunction
246 costFunction = new StochasticLoadBalancer.MemstoreSizeCostFunction(conf);
247 costFunction.init(mockCluster(new int[]{0, 0, 0, 0, 1}));
248
249 double[] statOne = new double[100];
250 for (int i =0; i < 100; i++) {
251 statOne[i] = 10;
252 }
253 assertEquals(0, costFunction.costFromArray(statOne), 0.01);
254
255 double[] statTwo= new double[101];
256 for (int i =0; i < 100; i++) {
257 statTwo[i] = 0;
258 }
259 statTwo[100] = 100;
260 assertEquals(1, costFunction.costFromArray(statTwo), 0.01);
261
262 double[] statThree = new double[200];
263 for (int i =0; i < 100; i++) {
264 statThree[i] = (0);
265 statThree[i+100] = 100;
266 }
267 assertEquals(0.5, costFunction.costFromArray(statThree), 0.01);
268 }
269
270 @Test(timeout = 60000)
271 public void testLosingRs() throws Exception {
272 int numNodes = 3;
273 int numRegions = 20;
274 int numRegionsPerServer = 3;
275 int replication = 1;
276 int numTables = 2;
277
278 Map<ServerName, List<HRegionInfo>> serverMap =
279 createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
280 List<ServerAndLoad> list = convertToList(serverMap);
281
282
283 List<RegionPlan> plans = loadBalancer.balanceCluster(serverMap);
284 assertNotNull(plans);
285
286
287 List<ServerAndLoad> balancedCluster = reconcile(list, plans, serverMap);
288
289 assertClusterAsBalanced(balancedCluster);
290
291 ServerName sn = serverMap.keySet().toArray(new ServerName[serverMap.size()])[0];
292
293 ServerName deadSn = ServerName.valueOf(sn.getHostname(), sn.getPort(), sn.getStartcode() - 100);
294
295 serverMap.put(deadSn, new ArrayList<HRegionInfo>(0));
296
297 plans = loadBalancer.balanceCluster(serverMap);
298 assertNull(plans);
299 }
300
301 @Test
302 public void testReplicaCost() {
303 Configuration conf = HBaseConfiguration.create();
304 StochasticLoadBalancer.CostFunction
305 costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
306 for (int[] mockCluster : clusterStateMocks) {
307 BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
308 costFunction.init(cluster);
309 double cost = costFunction.cost();
310 assertTrue(cost >= 0);
311 assertTrue(cost <= 1.01);
312 }
313 }
314
315 @Test
316 public void testReplicaCostForReplicas() {
317 Configuration conf = HBaseConfiguration.create();
318 StochasticLoadBalancer.CostFunction
319 costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
320
321 int [] servers = new int[] {3,3,3,3,3};
322 TreeMap<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(servers);
323
324 BaseLoadBalancer.Cluster cluster;
325
326 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
327 costFunction.init(cluster);
328 double costWithoutReplicas = costFunction.cost();
329 assertEquals(0, costWithoutReplicas, 0);
330
331
332 HRegionInfo replica1 = RegionReplicaUtil.getRegionInfoForReplica(
333 clusterState.firstEntry().getValue().get(0),1);
334 clusterState.lastEntry().getValue().add(replica1);
335
336 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
337 costFunction.init(cluster);
338 double costWith1ReplicaDifferentServer = costFunction.cost();
339
340 assertEquals(0, costWith1ReplicaDifferentServer, 0);
341
342
343 HRegionInfo replica2 = RegionReplicaUtil.getRegionInfoForReplica(replica1, 2);
344 clusterState.lastEntry().getValue().add(replica2);
345
346 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
347 costFunction.init(cluster);
348 double costWith1ReplicaSameServer = costFunction.cost();
349
350 assertTrue(costWith1ReplicaDifferentServer < costWith1ReplicaSameServer);
351
352
353
354 HRegionInfo replica3;
355 Iterator<Entry<ServerName, List<HRegionInfo>>> it;
356 Entry<ServerName, List<HRegionInfo>> entry;
357
358 clusterState = mockClusterServers(servers);
359 it = clusterState.entrySet().iterator();
360 entry = it.next();
361 HRegionInfo hri = entry.getValue().get(0);
362 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
363 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
364 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
365 entry.getValue().add(replica1);
366 entry.getValue().add(replica2);
367 it.next().getValue().add(replica3);
368
369 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
370 costFunction.init(cluster);
371 double costWith3ReplicasSameServer = costFunction.cost();
372
373 clusterState = mockClusterServers(servers);
374 hri = clusterState.firstEntry().getValue().get(0);
375 replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
376 replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
377 replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
378
379 clusterState.firstEntry().getValue().add(replica1);
380 clusterState.lastEntry().getValue().add(replica2);
381 clusterState.lastEntry().getValue().add(replica3);
382
383 cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
384 costFunction.init(cluster);
385 double costWith2ReplicasOnTwoServers = costFunction.cost();
386
387 assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer);
388 }
389
390 @Test
391 public void testNeedsBalanceForColocatedReplicas() {
392
393
394 List<HRegionInfo> regions = randomRegions(1);
395 ServerName s1 = ServerName.valueOf("host1", 1000, 11111);
396 ServerName s2 = ServerName.valueOf("host11", 1000, 11111);
397 Map<ServerName, List<HRegionInfo>> map = new HashMap<ServerName, List<HRegionInfo>>();
398 map.put(s1, regions);
399 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1));
400
401 regions = randomRegions(1);
402 map.put(s2, regions);
403 assertTrue(loadBalancer.needsBalance(new Cluster(map, null, null, null)));
404
405
406 map.clear();
407 regions = randomRegions(1);
408 List<HRegionInfo> regionsOnS2 = new ArrayList<HRegionInfo>(1);
409 regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1));
410 map.put(s1, regions);
411 map.put(s2, regionsOnS2);
412
413 map.put(ServerName.valueOf("host2", 1000, 11111), randomRegions(1));
414 assertTrue(loadBalancer.needsBalance(new Cluster(map, null, null,
415 new ForTestRackManagerOne())));
416 }
417
418 @Test (timeout = 60000)
419 public void testSmallCluster() {
420 int numNodes = 10;
421 int numRegions = 1000;
422 int numRegionsPerServer = 40;
423 int replication = 1;
424 int numTables = 10;
425 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
426 }
427
428 @Test (timeout = 60000)
429 public void testSmallCluster2() {
430 int numNodes = 20;
431 int numRegions = 2000;
432 int numRegionsPerServer = 40;
433 int replication = 1;
434 int numTables = 10;
435 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
436 }
437
438 @Test (timeout = 60000)
439 public void testSmallCluster3() {
440 int numNodes = 20;
441 int numRegions = 2000;
442 int numRegionsPerServer = 1;
443 int replication = 1;
444 int numTables = 10;
445
446 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, false, false);
447 }
448
449 @Test (timeout = 800000)
450 public void testMidCluster() {
451 int numNodes = 100;
452 int numRegions = 10000;
453 int numRegionsPerServer = 60;
454 int replication = 1;
455 int numTables = 40;
456 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
457 }
458
459 @Test (timeout = 800000)
460 public void testMidCluster2() {
461 int numNodes = 200;
462 int numRegions = 100000;
463 int numRegionsPerServer = 40;
464 int replication = 1;
465 int numTables = 400;
466 testWithCluster(numNodes,
467 numRegions,
468 numRegionsPerServer,
469 replication,
470 numTables,
471 false,
472 false);
473 }
474
475
476 @Test (timeout = 800000)
477 public void testMidCluster3() {
478 int numNodes = 100;
479 int numRegions = 2000;
480 int numRegionsPerServer = 9;
481 int replication = 1;
482 int numTables = 110;
483 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
484
485 }
486
487 @Test
488 public void testLargeCluster() {
489 int numNodes = 1000;
490 int numRegions = 100000;
491 int numRegionsPerServer = 80;
492 int numTables = 100;
493 int replication = 1;
494 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
495 }
496
497 @Test (timeout = 800000)
498 public void testRegionReplicasOnSmallCluster() {
499 int numNodes = 10;
500 int numRegions = 1000;
501 int replication = 3;
502 int numRegionsPerServer = 80;
503 int numTables = 10;
504 testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
505 }
506
507 @Ignore @Test (timeout = 800000)
508 public void testRegionReplicationOnMidClusterSameHosts() {
509 conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 2000000L);
510 conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 90 * 1000);
511 conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
512 loadBalancer.setConf(conf);
513 int numHosts = 100;
514 int numRegions = 100 * 100;
515 int replication = 3;
516 int numRegionsPerServer = 5;
517 int numTables = 10;
518 Map<ServerName, List<HRegionInfo>> serverMap =
519 createServerMap(numHosts, numRegions, numRegionsPerServer, replication, numTables);
520 int numNodesPerHost = 4;
521
522
523 Map<ServerName, List<HRegionInfo>> newServerMap = new TreeMap<ServerName, List<HRegionInfo>>(serverMap);
524 for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
525 for (int i=1; i < numNodesPerHost; i++) {
526 ServerName s1 = entry.getKey();
527 ServerName s2 = ServerName.valueOf(s1.getHostname(), s1.getPort() + i, 1);
528 newServerMap.put(s2, new ArrayList<HRegionInfo>());
529 }
530 }
531
532 testWithCluster(newServerMap, null, true, true);
533 }
534
535 private static class ForTestRackManager extends RackManager {
536 int numRacks;
537 public ForTestRackManager(int numRacks) {
538 this.numRacks = numRacks;
539 }
540 @Override
541 public String getRack(ServerName server) {
542 return "rack_" + (server.hashCode() % numRacks);
543 }
544 }
545
546 private static class ForTestRackManagerOne extends RackManager {
547 @Override
548 public String getRack(ServerName server) {
549 return server.getHostname().endsWith("1") ? "rack1" : "rack2";
550 }
551 }
552
553 @Test (timeout = 800000)
554 public void testRegionReplicationOnMidClusterWithRacks() {
555 conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 10000000L);
556 conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
557 conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 120 * 1000);
558 loadBalancer.setConf(conf);
559 int numNodes = 30;
560 int numRegions = numNodes * 30;
561 int replication = 3;
562 int numRegionsPerServer = 28;
563 int numTables = 10;
564 int numRacks = 4;
565 Map<ServerName, List<HRegionInfo>> serverMap =
566 createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
567 RackManager rm = new ForTestRackManager(numRacks);
568
569 testWithCluster(serverMap, rm, false, true);
570 }
571
572 }