View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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.master.balancer.StochasticLoadBalancer.ServerLocalityCostFunction;
52  import org.apache.hadoop.hbase.testclassification.FlakeyTests;
53  import org.apache.hadoop.hbase.testclassification.MediumTests;
54  import org.apache.hadoop.hbase.util.Bytes;
55  import org.junit.Ignore;
56  import org.junit.Test;
57  import org.junit.experimental.categories.Category;
58  
59  @Category({FlakeyTests.class, MediumTests.class})
60  public class TestStochasticLoadBalancer extends BalancerTestBase {
61    public static final String REGION_KEY = "testRegion";
62    private static final Log LOG = LogFactory.getLog(TestStochasticLoadBalancer.class);
63  
64    // Mapping of locality test -> expected locality
65    private float[] expectedLocalities = {1.0f, 0.0f, 0.50f, 0.25f, 1.0f};
66  
67    /**
68     * Data set for testLocalityCost:
69     * [test][0][0] = mapping of server to number of regions it hosts
70     * [test][region + 1][0] = server that region is hosted on
71     * [test][region + 1][server + 1] = locality for region on server
72     */
73  
74    private int[][][] clusterRegionLocationMocks = new int[][][]{
75  
76        // Test 1: each region is entirely on server that hosts it
77        new int[][]{
78            new int[]{2, 1, 1},
79            new int[]{2, 0, 0, 100},   // region 0 is hosted and entirely local on server 2
80            new int[]{0, 100, 0, 0},   // region 1 is hosted and entirely on server 0
81            new int[]{0, 100, 0, 0},   // region 2 is hosted and entirely on server 0
82            new int[]{1, 0, 100, 0},   // region 1 is hosted and entirely on server 1
83        },
84  
85        // Test 2: each region is 0% local on the server that hosts it
86        new int[][]{
87            new int[]{1, 2, 1},
88            new int[]{0, 0, 0, 100},   // region 0 is hosted and entirely local on server 2
89            new int[]{1, 100, 0, 0},   // region 1 is hosted and entirely on server 0
90            new int[]{1, 100, 0, 0},   // region 2 is hosted and entirely on server 0
91            new int[]{2, 0, 100, 0},   // region 1 is hosted and entirely on server 1
92        },
93  
94        // Test 3: each region is 25% local on the server that hosts it (and 50% locality is possible)
95        new int[][]{
96            new int[]{1, 2, 1},
97            new int[]{0, 25, 0, 50},   // region 0 is hosted and entirely local on server 2
98            new int[]{1, 50, 25, 0},   // region 1 is hosted and entirely on server 0
99            new int[]{1, 50, 25, 0},   // region 2 is hosted and entirely on server 0
100           new int[]{2, 0, 50, 25},   // region 1 is hosted and entirely on server 1
101       },
102 
103       // Test 4: each region is 25% local on the server that hosts it (and 100% locality is possible)
104       new int[][]{
105           new int[]{1, 2, 1},
106           new int[]{0, 25, 0, 100},   // region 0 is hosted and entirely local on server 2
107           new int[]{1, 100, 25, 0},   // region 1 is hosted and entirely on server 0
108           new int[]{1, 100, 25, 0},   // region 2 is hosted and entirely on server 0
109           new int[]{2, 0, 100, 25},   // region 1 is hosted and entirely on server 1
110       },
111 
112       // Test 5: each region is 75% local on the server that hosts it (and 75% locality is possible everywhere)
113       new int[][]{
114           new int[]{1, 2, 1},
115           new int[]{0, 75, 75, 75},   // region 0 is hosted and entirely local on server 2
116           new int[]{1, 75, 75, 75},   // region 1 is hosted and entirely on server 0
117           new int[]{1, 75, 75, 75},   // region 2 is hosted and entirely on server 0
118           new int[]{2, 75, 75, 75},   // region 1 is hosted and entirely on server 1
119       },
120   };
121 
122 
123   @Test
124   public void testKeepRegionLoad() throws Exception {
125 
126     ServerName sn = ServerName.valueOf("test:8080", 100);
127     int numClusterStatusToAdd = 20000;
128     for (int i = 0; i < numClusterStatusToAdd; i++) {
129       ServerLoad sl = mock(ServerLoad.class);
130 
131       RegionLoad rl = mock(RegionLoad.class);
132       when(rl.getStores()).thenReturn(i);
133 
134       Map<byte[], RegionLoad> regionLoadMap =
135           new TreeMap<byte[], RegionLoad>(Bytes.BYTES_COMPARATOR);
136       regionLoadMap.put(Bytes.toBytes(REGION_KEY), rl);
137       when(sl.getRegionsLoad()).thenReturn(regionLoadMap);
138 
139       ClusterStatus clusterStatus = mock(ClusterStatus.class);
140       when(clusterStatus.getServers()).thenReturn(Arrays.asList(sn));
141       when(clusterStatus.getLoad(sn)).thenReturn(sl);
142 
143       loadBalancer.setClusterStatus(clusterStatus);
144     }
145 
146     String regionNameAsString = HRegionInfo.getRegionNameAsString(Bytes.toBytes(REGION_KEY));
147     assertTrue(loadBalancer.loads.get(regionNameAsString) != null);
148     assertTrue(loadBalancer.loads.get(regionNameAsString).size() == 15);
149 
150     Queue<RegionLoad> loads = loadBalancer.loads.get(regionNameAsString);
151     int i = 0;
152     while(loads.size() > 0) {
153       RegionLoad rl = loads.remove();
154       assertEquals(i + (numClusterStatusToAdd - 15), rl.getStores());
155       i ++;
156     }
157   }
158 
159   @Test
160   public void testNeedBalance() {
161     float minCost = conf.getFloat("hbase.master.balancer.stochastic.minCostNeedBalance", 0.05f);
162     conf.setFloat("hbase.master.balancer.stochastic.minCostNeedBalance", 1.0f);
163     loadBalancer.setConf(conf);
164     for (int[] mockCluster : clusterStateMocks) {
165       Map<ServerName, List<HRegionInfo>> servers = mockClusterServers(mockCluster);
166       List<RegionPlan> plans = loadBalancer.balanceCluster(servers);
167       assertNull(plans);
168     }
169     // reset config
170     conf.setFloat("hbase.master.balancer.stochastic.minCostNeedBalance", minCost);
171     loadBalancer.setConf(conf);
172   }
173 
174   /**
175    * Test the load balancing algorithm.
176    *
177    * Invariant is that all servers should be hosting either floor(average) or
178    * ceiling(average)
179    *
180    * @throws Exception
181    */
182   @Test
183   public void testBalanceCluster() throws Exception {
184 
185     for (int[] mockCluster : clusterStateMocks) {
186       Map<ServerName, List<HRegionInfo>> servers = mockClusterServers(mockCluster);
187       List<ServerAndLoad> list = convertToList(servers);
188       LOG.info("Mock Cluster : " + printMock(list) + " " + printStats(list));
189       List<RegionPlan> plans = loadBalancer.balanceCluster(servers);
190       List<ServerAndLoad> balancedCluster = reconcile(list, plans, servers);
191       LOG.info("Mock Balance : " + printMock(balancedCluster));
192       assertClusterAsBalanced(balancedCluster);
193       List<RegionPlan> secondPlans =  loadBalancer.balanceCluster(servers);
194       assertNull(secondPlans);
195       for (Map.Entry<ServerName, List<HRegionInfo>> entry : servers.entrySet()) {
196         returnRegions(entry.getValue());
197         returnServer(entry.getKey());
198       }
199     }
200   }
201 
202   @Test
203   public void testLocalityCost() throws Exception {
204     Configuration conf = HBaseConfiguration.create();
205     MockNoopMasterServices master = new MockNoopMasterServices();
206     StochasticLoadBalancer.CostFunction
207         costFunction = new ServerLocalityCostFunction(conf, master);
208 
209     for (int test = 0; test < clusterRegionLocationMocks.length; test++) {
210       int[][] clusterRegionLocations = clusterRegionLocationMocks[test];
211       MockCluster cluster = new MockCluster(clusterRegionLocations);
212       costFunction.init(cluster);
213       double cost = costFunction.cost();
214       double expected = 1 - expectedLocalities[test];
215       assertEquals(expected, cost, 0.001);
216     }
217   }
218 
219   @Test
220   public void testMoveCost() throws Exception {
221     Configuration conf = HBaseConfiguration.create();
222     StochasticLoadBalancer.CostFunction
223         costFunction = new StochasticLoadBalancer.MoveCostFunction(conf);
224     for (int[] mockCluster : clusterStateMocks) {
225       BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
226       costFunction.init(cluster);
227       double cost = costFunction.cost();
228       assertEquals(0.0f, cost, 0.001);
229 
230       // cluster region number is smaller than maxMoves=600
231       cluster.setNumRegions(200);
232       cluster.setNumMovedRegions(10);
233       cost = costFunction.cost();
234       assertEquals(0.05f, cost, 0.001);
235       cluster.setNumMovedRegions(100);
236       cost = costFunction.cost();
237       assertEquals(0.5f, cost, 0.001);
238       cluster.setNumMovedRegions(200);
239       cost = costFunction.cost();
240       assertEquals(1.0f, cost, 0.001);
241 
242 
243       // cluster region number is bigger than maxMoves=2500
244       cluster.setNumRegions(10000);
245       cluster.setNumMovedRegions(250);
246       cost = costFunction.cost();
247       assertEquals(0.1f, cost, 0.001);
248       cluster.setNumMovedRegions(1250);
249       cost = costFunction.cost();
250       assertEquals(0.5f, cost, 0.001);
251       cluster.setNumMovedRegions(2500);
252       cost = costFunction.cost();
253       assertEquals(1.0f, cost, 0.01);
254     }
255   }
256 
257   @Test
258   public void testSkewCost() {
259     Configuration conf = HBaseConfiguration.create();
260     StochasticLoadBalancer.CostFunction
261         costFunction = new StochasticLoadBalancer.RegionCountSkewCostFunction(conf);
262     for (int[] mockCluster : clusterStateMocks) {
263       costFunction.init(mockCluster(mockCluster));
264       double cost = costFunction.cost();
265       assertTrue(cost >= 0);
266       assertTrue(cost <= 1.01);
267     }
268 
269     costFunction.init(mockCluster(new int[]{0, 0, 0, 0, 1}));
270     assertEquals(0,costFunction.cost(), 0.01);
271     costFunction.init(mockCluster(new int[]{0, 0, 0, 1, 1}));
272     assertEquals(0, costFunction.cost(), 0.01);
273     costFunction.init(mockCluster(new int[]{0, 0, 1, 1, 1}));
274     assertEquals(0, costFunction.cost(), 0.01);
275     costFunction.init(mockCluster(new int[]{0, 1, 1, 1, 1}));
276     assertEquals(0, costFunction.cost(), 0.01);
277     costFunction.init(mockCluster(new int[]{1, 1, 1, 1, 1}));
278     assertEquals(0, costFunction.cost(), 0.01);
279     costFunction.init(mockCluster(new int[]{10000, 0, 0, 0, 0}));
280     assertEquals(1, costFunction.cost(), 0.01);
281   }
282 
283   @Test
284   public void testCostAfterUndoAction() {
285     final int runs = 10;
286     loadBalancer.setConf(conf);
287     for (int[] mockCluster : clusterStateMocks) {
288       BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
289       loadBalancer.initCosts(cluster);
290       for (int i = 0; i != runs; ++i) {
291         final double expectedCost = loadBalancer.computeCost(cluster, Double.MAX_VALUE);
292         Cluster.Action action = loadBalancer.nextAction(cluster);
293         cluster.doAction(action);
294         loadBalancer.updateCostsWithAction(cluster, action);
295         Cluster.Action undoAction = action.undoAction();
296         cluster.doAction(undoAction);
297         loadBalancer.updateCostsWithAction(cluster, undoAction);
298         final double actualCost = loadBalancer.computeCost(cluster, Double.MAX_VALUE);
299         assertEquals(expectedCost, actualCost, 0);
300       }
301     }
302   }
303 
304   @Test
305   public void testTableSkewCost() {
306     Configuration conf = HBaseConfiguration.create();
307     StochasticLoadBalancer.CostFunction
308         costFunction = new StochasticLoadBalancer.TableSkewCostFunction(conf);
309     for (int[] mockCluster : clusterStateMocks) {
310       BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
311       costFunction.init(cluster);
312       double cost = costFunction.cost();
313       assertTrue(cost >= 0);
314       assertTrue(cost <= 1.01);
315     }
316   }
317 
318   @Test
319   public void testRegionLoadCost() {
320     List<RegionLoad> regionLoads = new ArrayList<>();
321     for (int i = 1; i < 5; i++) {
322       RegionLoad regionLoad = mock(RegionLoad.class);
323       when(regionLoad.getReadRequestsCount()).thenReturn(new Long(i));
324       when(regionLoad.getStorefileSizeMB()).thenReturn(i);
325       regionLoads.add(regionLoad);
326     }
327 
328     Configuration conf = HBaseConfiguration.create();
329     StochasticLoadBalancer.ReadRequestCostFunction readCostFunction =
330         new StochasticLoadBalancer.ReadRequestCostFunction(conf);
331     double rateResult = readCostFunction.getRegionLoadCost(regionLoads);
332     // read requests are treated as a rate so the average rate here is simply 1
333     assertEquals(1, rateResult, 0.01);
334 
335     StochasticLoadBalancer.StoreFileCostFunction storeFileCostFunction =
336         new StochasticLoadBalancer.StoreFileCostFunction(conf);
337     double result = storeFileCostFunction.getRegionLoadCost(regionLoads);
338     // storefile size cost is simply an average of it's value over time
339     assertEquals(2.5, result, 0.01);
340   }
341 
342   @Test
343   public void testCostFromArray() {
344     Configuration conf = HBaseConfiguration.create();
345     StochasticLoadBalancer.CostFromRegionLoadFunction
346         costFunction = new StochasticLoadBalancer.MemstoreSizeCostFunction(conf);
347     costFunction.init(mockCluster(new int[]{0, 0, 0, 0, 1}));
348 
349     double[] statOne = new double[100];
350     for (int i =0; i < 100; i++) {
351       statOne[i] = 10;
352     }
353     assertEquals(0, costFunction.costFromArray(statOne), 0.01);
354 
355     double[] statTwo= new double[101];
356     for (int i =0; i < 100; i++) {
357       statTwo[i] = 0;
358     }
359     statTwo[100] = 100;
360     assertEquals(1, costFunction.costFromArray(statTwo), 0.01);
361 
362     double[] statThree = new double[200];
363     for (int i =0; i < 100; i++) {
364       statThree[i] = (0);
365       statThree[i+100] = 100;
366     }
367     assertEquals(0.5, costFunction.costFromArray(statThree), 0.01);
368   }
369 
370   @Test(timeout =  60000)
371   public void testLosingRs() throws Exception {
372     int numNodes = 3;
373     int numRegions = 20;
374     int numRegionsPerServer = 3; //all servers except one
375     int replication = 1;
376     int numTables = 2;
377 
378     Map<ServerName, List<HRegionInfo>> serverMap =
379         createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
380     List<ServerAndLoad> list = convertToList(serverMap);
381 
382 
383     List<RegionPlan> plans = loadBalancer.balanceCluster(serverMap);
384     assertNotNull(plans);
385 
386     // Apply the plan to the mock cluster.
387     List<ServerAndLoad> balancedCluster = reconcile(list, plans, serverMap);
388 
389     assertClusterAsBalanced(balancedCluster);
390 
391     ServerName sn = serverMap.keySet().toArray(new ServerName[serverMap.size()])[0];
392 
393     ServerName deadSn = ServerName.valueOf(sn.getHostname(), sn.getPort(), sn.getStartcode() - 100);
394 
395     serverMap.put(deadSn, new ArrayList<HRegionInfo>(0));
396 
397     plans = loadBalancer.balanceCluster(serverMap);
398     assertNull(plans);
399   }
400 
401   @Test
402   public void testReplicaCost() {
403     Configuration conf = HBaseConfiguration.create();
404     StochasticLoadBalancer.CostFunction
405         costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
406     for (int[] mockCluster : clusterStateMocks) {
407       BaseLoadBalancer.Cluster cluster = mockCluster(mockCluster);
408       costFunction.init(cluster);
409       double cost = costFunction.cost();
410       assertTrue(cost >= 0);
411       assertTrue(cost <= 1.01);
412     }
413   }
414 
415   @Test
416   public void testReplicaCostForReplicas() {
417     Configuration conf = HBaseConfiguration.create();
418     StochasticLoadBalancer.CostFunction
419         costFunction = new StochasticLoadBalancer.RegionReplicaHostCostFunction(conf);
420 
421     int [] servers = new int[] {3,3,3,3,3};
422     TreeMap<ServerName, List<HRegionInfo>> clusterState = mockClusterServers(servers);
423 
424     BaseLoadBalancer.Cluster cluster;
425 
426     cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
427     costFunction.init(cluster);
428     double costWithoutReplicas = costFunction.cost();
429     assertEquals(0, costWithoutReplicas, 0);
430 
431     // replicate the region from first server to the last server
432     HRegionInfo replica1 = RegionReplicaUtil.getRegionInfoForReplica(
433       clusterState.firstEntry().getValue().get(0),1);
434     clusterState.lastEntry().getValue().add(replica1);
435 
436     cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
437     costFunction.init(cluster);
438     double costWith1ReplicaDifferentServer = costFunction.cost();
439 
440     assertEquals(0, costWith1ReplicaDifferentServer, 0);
441 
442     // add a third replica to the last server
443     HRegionInfo replica2 = RegionReplicaUtil.getRegionInfoForReplica(replica1, 2);
444     clusterState.lastEntry().getValue().add(replica2);
445 
446     cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
447     costFunction.init(cluster);
448     double costWith1ReplicaSameServer = costFunction.cost();
449 
450     assertTrue(costWith1ReplicaDifferentServer < costWith1ReplicaSameServer);
451 
452     // test with replication = 4 for following:
453 
454     HRegionInfo replica3;
455     Iterator<Entry<ServerName, List<HRegionInfo>>> it;
456     Entry<ServerName, List<HRegionInfo>> entry;
457 
458     clusterState = mockClusterServers(servers);
459     it = clusterState.entrySet().iterator();
460     entry = it.next(); //first server
461     HRegionInfo hri = entry.getValue().get(0);
462     replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
463     replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
464     replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
465     entry.getValue().add(replica1);
466     entry.getValue().add(replica2);
467     it.next().getValue().add(replica3); //2nd server
468 
469     cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
470     costFunction.init(cluster);
471     double costWith3ReplicasSameServer = costFunction.cost();
472 
473     clusterState = mockClusterServers(servers);
474     hri = clusterState.firstEntry().getValue().get(0);
475     replica1 = RegionReplicaUtil.getRegionInfoForReplica(hri, 1);
476     replica2 = RegionReplicaUtil.getRegionInfoForReplica(hri, 2);
477     replica3 = RegionReplicaUtil.getRegionInfoForReplica(hri, 3);
478 
479     clusterState.firstEntry().getValue().add(replica1);
480     clusterState.lastEntry().getValue().add(replica2);
481     clusterState.lastEntry().getValue().add(replica3);
482 
483     cluster = new BaseLoadBalancer.Cluster(clusterState, null, null, null);
484     costFunction.init(cluster);
485     double costWith2ReplicasOnTwoServers = costFunction.cost();
486 
487     assertTrue(costWith2ReplicasOnTwoServers < costWith3ReplicasSameServer);
488   }
489 
490   @Test
491   public void testNeedsBalanceForColocatedReplicas() {
492     // check for the case where there are two hosts and with one rack, and where
493     // both the replicas are hosted on the same server
494     List<HRegionInfo> regions = randomRegions(1);
495     ServerName s1 = ServerName.valueOf("host1", 1000, 11111);
496     ServerName s2 = ServerName.valueOf("host11", 1000, 11111);
497     Map<ServerName, List<HRegionInfo>> map = new HashMap<ServerName, List<HRegionInfo>>();
498     map.put(s1, regions);
499     regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1));
500     // until the step above s1 holds two replicas of a region
501     regions = randomRegions(1);
502     map.put(s2, regions);
503     assertTrue(loadBalancer.needsBalance(new Cluster(map, null, null, null)));
504     // check for the case where there are two hosts on the same rack and there are two racks
505     // and both the replicas are on the same rack
506     map.clear();
507     regions = randomRegions(1);
508     List<HRegionInfo> regionsOnS2 = new ArrayList<HRegionInfo>(1);
509     regionsOnS2.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(0), 1));
510     map.put(s1, regions);
511     map.put(s2, regionsOnS2);
512     // add another server so that the cluster has some host on another rack
513     map.put(ServerName.valueOf("host2", 1000, 11111), randomRegions(1));
514     assertTrue(loadBalancer.needsBalance(new Cluster(map, null, null,
515         new ForTestRackManagerOne())));
516   }
517 
518   @Test (timeout = 60000)
519   public void testSmallCluster() {
520     int numNodes = 10;
521     int numRegions = 1000;
522     int numRegionsPerServer = 40; //all servers except one
523     int replication = 1;
524     int numTables = 10;
525     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
526   }
527 
528   @Test (timeout = 60000)
529   public void testSmallCluster2() {
530     int numNodes = 20;
531     int numRegions = 2000;
532     int numRegionsPerServer = 40; //all servers except one
533     int replication = 1;
534     int numTables = 10;
535     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
536   }
537 
538   @Test (timeout = 60000)
539   public void testSmallCluster3() {
540     int numNodes = 20;
541     int numRegions = 2000;
542     int numRegionsPerServer = 1; // all servers except one
543     int replication = 1;
544     int numTables = 10;
545     /* fails because of max moves */
546     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, false, false);
547   }
548 
549   @Test (timeout = 800000)
550   public void testMidCluster() {
551     int numNodes = 100;
552     int numRegions = 10000;
553     int numRegionsPerServer = 60; // all servers except one
554     int replication = 1;
555     int numTables = 40;
556     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
557   }
558 
559   @Test (timeout = 800000)
560   public void testMidCluster2() {
561     int numNodes = 200;
562     int numRegions = 100000;
563     int numRegionsPerServer = 40; // all servers except one
564     int replication = 1;
565     int numTables = 400;
566     testWithCluster(numNodes,
567         numRegions,
568         numRegionsPerServer,
569         replication,
570         numTables,
571         false, /* num large num regions means may not always get to best balance with one run */
572         false);
573   }
574 
575 
576   @Test (timeout = 800000)
577   public void testMidCluster3() {
578     int numNodes = 100;
579     int numRegions = 2000;
580     int numRegionsPerServer = 9; // all servers except one
581     int replication = 1;
582     int numTables = 110;
583     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
584     // TODO(eclark): Make sure that the tables are well distributed.
585   }
586 
587   @Test
588   public void testLargeCluster() {
589     int numNodes = 1000;
590     int numRegions = 100000; //100 regions per RS
591     int numRegionsPerServer = 80; //all servers except one
592     int numTables = 100;
593     int replication = 1;
594     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
595   }
596 
597   @Test (timeout = 800000)
598   public void testRegionReplicasOnSmallCluster() {
599     int numNodes = 10;
600     int numRegions = 1000;
601     int replication = 3; // 3 replicas per region
602     int numRegionsPerServer = 80; //all regions are mostly balanced
603     int numTables = 10;
604     testWithCluster(numNodes, numRegions, numRegionsPerServer, replication, numTables, true, true);
605   }
606 
607   @Ignore @Test (timeout = 800000) // Test is flakey. TODO: Fix!
608   public void testRegionReplicationOnMidClusterSameHosts() {
609     conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 2000000L);
610     conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 90 * 1000); // 90 sec
611     conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
612     loadBalancer.setConf(conf);
613     int numHosts = 100;
614     int numRegions = 100 * 100;
615     int replication = 3; // 3 replicas per region
616     int numRegionsPerServer = 5;
617     int numTables = 10;
618     Map<ServerName, List<HRegionInfo>> serverMap =
619         createServerMap(numHosts, numRegions, numRegionsPerServer, replication, numTables);
620     int numNodesPerHost = 4;
621 
622     // create a new map with 4 RS per host.
623     Map<ServerName, List<HRegionInfo>> newServerMap = new TreeMap<ServerName, List<HRegionInfo>>(serverMap);
624     for (Map.Entry<ServerName, List<HRegionInfo>> entry : serverMap.entrySet()) {
625       for (int i=1; i < numNodesPerHost; i++) {
626         ServerName s1 = entry.getKey();
627         ServerName s2 = ServerName.valueOf(s1.getHostname(), s1.getPort() + i, 1); // create an RS for the same host
628         newServerMap.put(s2, new ArrayList<HRegionInfo>());
629       }
630     }
631 
632     testWithCluster(newServerMap, null, true, true);
633   }
634 
635   private static class ForTestRackManager extends RackManager {
636     int numRacks;
637     public ForTestRackManager(int numRacks) {
638       this.numRacks = numRacks;
639     }
640     @Override
641     public String getRack(ServerName server) {
642       return "rack_" + (server.hashCode() % numRacks);
643     }
644   }
645 
646   private static class ForTestRackManagerOne extends RackManager {
647   @Override
648     public String getRack(ServerName server) {
649       return server.getHostname().endsWith("1") ? "rack1" : "rack2";
650     }
651   }
652 
653   @Test (timeout = 800000)
654   public void testRegionReplicationOnMidClusterWithRacks() {
655     conf.setLong(StochasticLoadBalancer.MAX_STEPS_KEY, 10000000L);
656     conf.setFloat("hbase.master.balancer.stochastic.maxMovePercent", 1.0f);
657     conf.setLong("hbase.master.balancer.stochastic.maxRunningTime", 120 * 1000); // 120 sec
658     loadBalancer.setConf(conf);
659     int numNodes = 30;
660     int numRegions = numNodes * 30;
661     int replication = 3; // 3 replicas per region
662     int numRegionsPerServer = 28;
663     int numTables = 10;
664     int numRacks = 4; // all replicas should be on a different rack
665     Map<ServerName, List<HRegionInfo>> serverMap =
666         createServerMap(numNodes, numRegions, numRegionsPerServer, replication, numTables);
667     RackManager rm = new ForTestRackManager(numRacks);
668 
669     testWithCluster(serverMap, rm, false, true);
670   }
671 
672   // This mock allows us to test the LocalityCostFunction
673   private class MockCluster extends BaseLoadBalancer.Cluster {
674 
675     private int[][] localities = null;   // [region][server] = percent of blocks
676 
677     public MockCluster(int[][] regions) {
678 
679       // regions[0] is an array where index = serverIndex an value = number of regions
680       super(mockClusterServers(regions[0], 1), null, null, null);
681 
682       localities = new int[regions.length - 1][];
683       for (int i = 1; i < regions.length; i++) {
684         int regionIndex = i - 1;
685         localities[regionIndex] = new int[regions[i].length - 1];
686         regionIndexToServerIndex[regionIndex] = regions[i][0];
687         for (int j = 1; j < regions[i].length; j++) {
688           int serverIndex = j - 1;
689           localities[regionIndex][serverIndex] = regions[i][j] > 100 ? regions[i][j] % 100 : regions[i][j];
690         }
691       }
692     }
693 
694     @Override
695     float getLocalityOfRegion(int region, int server) {
696       // convert the locality percentage to a fraction
697       return localities[region][server] / 100.0f;
698     }
699 
700     @Override
701     public int getRegionSizeMB(int region) {
702       return 1;
703     }
704 
705   }
706 
707 }