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