001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.master.balancer;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNull;
022import static org.junit.Assert.assertTrue;
023import static org.mockito.Mockito.mock;
024import static org.mockito.Mockito.when;
025
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.HashMap;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import java.util.TreeMap;
034import java.util.TreeSet;
035import java.util.stream.Collectors;
036import org.apache.commons.lang3.ArrayUtils;
037import org.apache.hadoop.conf.Configuration;
038import org.apache.hadoop.hbase.HBaseClassTestRule;
039import org.apache.hadoop.hbase.HBaseConfiguration;
040import org.apache.hadoop.hbase.HBaseIOException;
041import org.apache.hadoop.hbase.ServerName;
042import org.apache.hadoop.hbase.TableName;
043import org.apache.hadoop.hbase.client.RegionInfo;
044import org.apache.hadoop.hbase.client.RegionInfoBuilder;
045import org.apache.hadoop.hbase.client.RegionReplicaUtil;
046import org.apache.hadoop.hbase.master.LoadBalancer;
047import org.apache.hadoop.hbase.master.MasterServices;
048import org.apache.hadoop.hbase.master.RackManager;
049import org.apache.hadoop.hbase.master.RegionPlan;
050import org.apache.hadoop.hbase.master.ServerManager;
051import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster;
052import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.MoveRegionAction;
053import org.apache.hadoop.hbase.testclassification.MasterTests;
054import org.apache.hadoop.hbase.testclassification.MediumTests;
055import org.apache.hadoop.net.DNSToSwitchMapping;
056import org.junit.BeforeClass;
057import org.junit.ClassRule;
058import org.junit.Rule;
059import org.junit.Test;
060import org.junit.experimental.categories.Category;
061import org.junit.rules.TestName;
062import org.mockito.Mockito;
063import org.slf4j.Logger;
064import org.slf4j.LoggerFactory;
065
066import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
067
068@Category({MasterTests.class, MediumTests.class})
069public class TestBaseLoadBalancer extends BalancerTestBase {
070
071  @ClassRule
072  public static final HBaseClassTestRule CLASS_RULE =
073      HBaseClassTestRule.forClass(TestBaseLoadBalancer.class);
074
075  private static LoadBalancer loadBalancer;
076  private static final Logger LOG = LoggerFactory.getLogger(TestBaseLoadBalancer.class);
077  private static final ServerName master = ServerName.valueOf("fake-master", 0, 1L);
078  private static RackManager rackManager;
079  private static final int NUM_SERVERS = 15;
080  private static ServerName[] servers = new ServerName[NUM_SERVERS];
081
082  int[][] regionsAndServersMocks = new int[][] {
083      // { num regions, num servers }
084      new int[] { 0, 0 }, new int[] { 0, 1 }, new int[] { 1, 1 }, new int[] { 2, 1 },
085      new int[] { 10, 1 }, new int[] { 1, 2 }, new int[] { 2, 2 }, new int[] { 3, 2 },
086      new int[] { 1, 3 }, new int[] { 2, 3 }, new int[] { 3, 3 }, new int[] { 25, 3 },
087      new int[] { 2, 10 }, new int[] { 2, 100 }, new int[] { 12, 10 }, new int[] { 12, 100 }, };
088
089  @Rule
090  public TestName name = new TestName();
091
092  @BeforeClass
093  public static void beforeAllTests() throws Exception {
094    Configuration conf = HBaseConfiguration.create();
095    conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class);
096    loadBalancer = new MockBalancer();
097    loadBalancer.setConf(conf);
098    MasterServices st = Mockito.mock(MasterServices.class);
099    Mockito.when(st.getServerName()).thenReturn(master);
100    loadBalancer.setMasterServices(st);
101
102    // Set up the rack topologies (5 machines per rack)
103    rackManager = Mockito.mock(RackManager.class);
104    for (int i = 0; i < NUM_SERVERS; i++) {
105      servers[i] = ServerName.valueOf("foo"+i+":1234",-1);
106      if (i < 5) {
107        Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack1");
108      }
109      if (i >= 5 && i < 10) {
110        Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack2");
111      }
112      if (i >= 10) {
113        Mockito.when(rackManager.getRack(servers[i])).thenReturn("rack3");
114      }
115    }
116  }
117
118  public static class MockBalancer extends BaseLoadBalancer {
119    @Override
120    public List<RegionPlan> balanceCluster(Map<ServerName, List<RegionInfo>> clusterState) {
121      return null;
122    }
123
124    @Override
125    public List<RegionPlan> balanceCluster(TableName tableName,
126        Map<ServerName, List<RegionInfo>> clusterState) throws HBaseIOException {
127      return null;
128    }
129  }
130
131  /**
132   * All regions have an assignment.
133   * @param regions
134   * @param servers
135   * @param assignments
136   */
137  private void assertImmediateAssignment(List<RegionInfo> regions, List<ServerName> servers,
138      Map<RegionInfo, ServerName> assignments) {
139    for (RegionInfo region : regions) {
140      assertTrue(assignments.containsKey(region));
141    }
142  }
143
144  /**
145   * Tests the bulk assignment used during cluster startup.
146   *
147   * Round-robin. Should yield a balanced cluster so same invariant as the load
148   * balancer holds, all servers holding either floor(avg) or ceiling(avg).
149   *
150   * @throws Exception
151   */
152  @Test
153  public void testBulkAssignment() throws Exception {
154    List<ServerName> tmp = getListOfServerNames(randomServers(5, 0));
155    List<RegionInfo> hris = randomRegions(20);
156    hris.add(RegionInfoBuilder.FIRST_META_REGIONINFO);
157    tmp.add(master);
158    Map<ServerName, List<RegionInfo>> plans = loadBalancer.roundRobinAssignment(hris, tmp);
159    if (LoadBalancer.isTablesOnMaster(loadBalancer.getConf())) {
160      assertTrue(plans.get(master).contains(RegionInfoBuilder.FIRST_META_REGIONINFO));
161      assertEquals(1, plans.get(master).size());
162    }
163    int totalRegion = 0;
164    for (List<RegionInfo> regions: plans.values()) {
165      totalRegion += regions.size();
166    }
167    assertEquals(hris.size(), totalRegion);
168    for (int[] mock : regionsAndServersMocks) {
169      LOG.debug("testBulkAssignment with " + mock[0] + " regions and " + mock[1] + " servers");
170      List<RegionInfo> regions = randomRegions(mock[0]);
171      List<ServerAndLoad> servers = randomServers(mock[1], 0);
172      List<ServerName> list = getListOfServerNames(servers);
173      Map<ServerName, List<RegionInfo>> assignments =
174          loadBalancer.roundRobinAssignment(regions, list);
175      float average = (float) regions.size() / servers.size();
176      int min = (int) Math.floor(average);
177      int max = (int) Math.ceil(average);
178      if (assignments != null && !assignments.isEmpty()) {
179        for (List<RegionInfo> regionList : assignments.values()) {
180          assertTrue(regionList.size() == min || regionList.size() == max);
181        }
182      }
183      returnRegions(regions);
184      returnServers(list);
185    }
186  }
187
188  /**
189   * Test the cluster startup bulk assignment which attempts to retain
190   * assignment info.
191   * @throws Exception
192   */
193  @Test
194  public void testRetainAssignment() throws Exception {
195    // Test simple case where all same servers are there
196    List<ServerAndLoad> servers = randomServers(10, 10);
197    List<RegionInfo> regions = randomRegions(100);
198    Map<RegionInfo, ServerName> existing = new TreeMap<>(RegionInfo.COMPARATOR);
199    for (int i = 0; i < regions.size(); i++) {
200      ServerName sn = servers.get(i % servers.size()).getServerName();
201      // The old server would have had same host and port, but different
202      // start code!
203      ServerName snWithOldStartCode =
204          ServerName.valueOf(sn.getHostname(), sn.getPort(), sn.getStartcode() - 10);
205      existing.put(regions.get(i), snWithOldStartCode);
206    }
207    List<ServerName> listOfServerNames = getListOfServerNames(servers);
208    Map<ServerName, List<RegionInfo>> assignment =
209        loadBalancer.retainAssignment(existing, listOfServerNames);
210    assertRetainedAssignment(existing, listOfServerNames, assignment);
211
212    // Include two new servers that were not there before
213    List<ServerAndLoad> servers2 = new ArrayList<>(servers);
214    servers2.add(randomServer(10));
215    servers2.add(randomServer(10));
216    listOfServerNames = getListOfServerNames(servers2);
217    assignment = loadBalancer.retainAssignment(existing, listOfServerNames);
218    assertRetainedAssignment(existing, listOfServerNames, assignment);
219
220    // Remove two of the servers that were previously there
221    List<ServerAndLoad> servers3 = new ArrayList<>(servers);
222    servers3.remove(0);
223    servers3.remove(0);
224    listOfServerNames = getListOfServerNames(servers3);
225    assignment = loadBalancer.retainAssignment(existing, listOfServerNames);
226    assertRetainedAssignment(existing, listOfServerNames, assignment);
227  }
228
229  @Test
230  public void testRandomAssignment() throws Exception {
231    for (int i = 1; i != 5; ++i) {
232      LOG.info("run testRandomAssignment() with idle servers:" + i);
233      testRandomAssignment(i);
234    }
235  }
236
237  private void testRandomAssignment(int numberOfIdleServers) throws Exception {
238    assert numberOfIdleServers > 0;
239    List<ServerName> idleServers = new ArrayList<>(numberOfIdleServers);
240    for (int i = 0; i != numberOfIdleServers; ++i) {
241      idleServers.add(ServerName.valueOf("server-" + i, 1000, 1L));
242    }
243    List<ServerName> allServers = new ArrayList<>(idleServers.size() + 1);
244    allServers.add(ServerName.valueOf("server-" + numberOfIdleServers, 1000, 1L));
245    allServers.addAll(idleServers);
246    LoadBalancer balancer = new MockBalancer() {
247      @Override
248      public boolean shouldBeOnMaster(RegionInfo region) {
249        return false;
250      }
251    };
252    Configuration conf = HBaseConfiguration.create();
253    conf.setClass("hbase.util.ip.to.rack.determiner", MockMapping.class, DNSToSwitchMapping.class);
254    balancer.setConf(conf);
255    ServerManager sm = Mockito.mock(ServerManager.class);
256    Mockito.when(sm.getOnlineServersListWithPredicator(allServers, BaseLoadBalancer.IDLE_SERVER_PREDICATOR))
257           .thenReturn(idleServers);
258    MasterServices services = Mockito.mock(MasterServices.class);
259    Mockito.when(services.getServerManager()).thenReturn(sm);
260    balancer.setMasterServices(services);
261    RegionInfo hri1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
262        .setStartKey("key1".getBytes())
263        .setEndKey("key2".getBytes())
264        .setSplit(false)
265        .setRegionId(100)
266        .build();
267    assertNull(balancer.randomAssignment(hri1, Collections.EMPTY_LIST));
268    assertNull(balancer.randomAssignment(hri1, null));
269    for (int i = 0; i != 3; ++i) {
270      ServerName sn = balancer.randomAssignment(hri1, allServers);
271      assertTrue("actual:" + sn + ", except:" + idleServers, idleServers.contains(sn));
272    }
273  }
274
275  @Test
276  public void testRegionAvailability() throws Exception {
277    // Create a cluster with a few servers, assign them to specific racks
278    // then assign some regions. The tests should check whether moving a
279    // replica from one node to a specific other node or rack lowers the
280    // availability of the region or not
281
282    List<RegionInfo> list0 = new ArrayList<>();
283    List<RegionInfo> list1 = new ArrayList<>();
284    List<RegionInfo> list2 = new ArrayList<>();
285    // create a region (region1)
286    RegionInfo hri1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
287        .setStartKey("key1".getBytes())
288        .setEndKey("key2".getBytes())
289        .setSplit(false)
290        .setRegionId(100)
291        .build();
292    // create a replica of the region (replica_of_region1)
293    RegionInfo hri2 = RegionReplicaUtil.getRegionInfoForReplica(hri1, 1);
294    // create a second region (region2)
295    RegionInfo hri3 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
296        .setStartKey("key2".getBytes())
297        .setEndKey("key3".getBytes())
298        .setSplit(false)
299        .setRegionId(101)
300        .build();
301    list0.add(hri1); //only region1
302    list1.add(hri2); //only replica_of_region1
303    list2.add(hri3); //only region2
304    Map<ServerName, List<RegionInfo>> clusterState = new LinkedHashMap<>();
305    clusterState.put(servers[0], list0); //servers[0] hosts region1
306    clusterState.put(servers[1], list1); //servers[1] hosts replica_of_region1
307    clusterState.put(servers[2], list2); //servers[2] hosts region2
308    // create a cluster with the above clusterState. The way in which the
309    // cluster is created (constructor code) would make sure the indices of
310    // the servers are in the order in which it is inserted in the clusterState
311    // map (linkedhashmap is important). A similar thing applies to the region lists
312    Cluster cluster = new Cluster(clusterState, null, null, rackManager);
313    // check whether a move of region1 from servers[0] to servers[1] would lower
314    // the availability of region1
315    assertTrue(cluster.wouldLowerAvailability(hri1, servers[1]));
316    // check whether a move of region1 from servers[0] to servers[2] would lower
317    // the availability of region1
318    assertTrue(!cluster.wouldLowerAvailability(hri1, servers[2]));
319    // check whether a move of replica_of_region1 from servers[0] to servers[2] would lower
320    // the availability of replica_of_region1
321    assertTrue(!cluster.wouldLowerAvailability(hri2, servers[2]));
322    // check whether a move of region2 from servers[0] to servers[1] would lower
323    // the availability of region2
324    assertTrue(!cluster.wouldLowerAvailability(hri3, servers[1]));
325
326    // now lets have servers[1] host replica_of_region2
327    list1.add(RegionReplicaUtil.getRegionInfoForReplica(hri3, 1));
328    // create a new clusterState with the above change
329    cluster = new Cluster(clusterState, null, null, rackManager);
330    // now check whether a move of a replica from servers[0] to servers[1] would lower
331    // the availability of region2
332    assertTrue(cluster.wouldLowerAvailability(hri3, servers[1]));
333
334    // start over again
335    clusterState.clear();
336    clusterState.put(servers[0], list0); //servers[0], rack1 hosts region1
337    clusterState.put(servers[5], list1); //servers[5], rack2 hosts replica_of_region1 and replica_of_region2
338    clusterState.put(servers[6], list2); //servers[6], rack2 hosts region2
339    clusterState.put(servers[10], new ArrayList<>()); //servers[10], rack3 hosts no region
340    // create a cluster with the above clusterState
341    cluster = new Cluster(clusterState, null, null, rackManager);
342    // check whether a move of region1 from servers[0],rack1 to servers[6],rack2 would
343    // lower the availability
344
345    assertTrue(cluster.wouldLowerAvailability(hri1, servers[0]));
346
347    // now create a cluster without the rack manager
348    cluster = new Cluster(clusterState, null, null, null);
349    // now repeat check whether a move of region1 from servers[0] to servers[6] would
350    // lower the availability
351    assertTrue(!cluster.wouldLowerAvailability(hri1, servers[6]));
352  }
353
354  @Test
355  public void testRegionAvailabilityWithRegionMoves() throws Exception {
356    List<RegionInfo> list0 = new ArrayList<>();
357    List<RegionInfo> list1 = new ArrayList<>();
358    List<RegionInfo> list2 = new ArrayList<>();
359    // create a region (region1)
360    RegionInfo hri1 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
361        .setStartKey("key1".getBytes())
362        .setEndKey("key2".getBytes())
363        .setSplit(false)
364        .setRegionId(100)
365        .build();
366    // create a replica of the region (replica_of_region1)
367    RegionInfo hri2 = RegionReplicaUtil.getRegionInfoForReplica(hri1, 1);
368    // create a second region (region2)
369    RegionInfo hri3 = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName()))
370        .setStartKey("key2".getBytes())
371        .setEndKey("key3".getBytes())
372        .setSplit(false)
373        .setRegionId(101)
374        .build();
375    list0.add(hri1); //only region1
376    list1.add(hri2); //only replica_of_region1
377    list2.add(hri3); //only region2
378    Map<ServerName, List<RegionInfo>> clusterState = new LinkedHashMap<>();
379    clusterState.put(servers[0], list0); //servers[0] hosts region1
380    clusterState.put(servers[1], list1); //servers[1] hosts replica_of_region1
381    clusterState.put(servers[2], list2); //servers[2] hosts region2
382    // create a cluster with the above clusterState. The way in which the
383    // cluster is created (constructor code) would make sure the indices of
384    // the servers are in the order in which it is inserted in the clusterState
385    // map (linkedhashmap is important).
386    Cluster cluster = new Cluster(clusterState, null, null, rackManager);
387    // check whether moving region1 from servers[1] to servers[2] would lower availability
388    assertTrue(!cluster.wouldLowerAvailability(hri1, servers[2]));
389
390    // now move region1 from servers[0] to servers[2]
391    cluster.doAction(new MoveRegionAction(0, 0, 2));
392    // check that the numMaxRegionsPerTable for "table" has increased to 2
393    assertEquals(2, cluster.numMaxRegionsPerTable[0]);
394    // now repeat check whether moving region1 from servers[1] to servers[2]
395    // would lower availability
396    assertTrue(cluster.wouldLowerAvailability(hri1, servers[2]));
397
398    // start over again
399    clusterState.clear();
400    List<RegionInfo> list3 = new ArrayList<>();
401    RegionInfo hri4 = RegionReplicaUtil.getRegionInfoForReplica(hri3, 1);
402    list3.add(hri4);
403    clusterState.put(servers[0], list0); //servers[0], rack1 hosts region1
404    clusterState.put(servers[5], list1); //servers[5], rack2 hosts replica_of_region1
405    clusterState.put(servers[6], list2); //servers[6], rack2 hosts region2
406    clusterState.put(servers[12], list3); //servers[12], rack3 hosts replica_of_region2
407    // create a cluster with the above clusterState
408    cluster = new Cluster(clusterState, null, null, rackManager);
409    // check whether a move of replica_of_region2 from servers[12],rack3 to servers[0],rack1 would
410    // lower the availability
411    assertTrue(!cluster.wouldLowerAvailability(hri4, servers[0]));
412    // now move region2 from servers[6],rack2 to servers[0],rack1
413    cluster.doAction(new MoveRegionAction(2, 2, 0));
414    // now repeat check if replica_of_region2 from servers[12],rack3 to servers[0],rack1 would
415    // lower the availability
416    assertTrue(cluster.wouldLowerAvailability(hri3, servers[0]));
417  }
418
419  private List<ServerName> getListOfServerNames(final List<ServerAndLoad> sals) {
420    return sals.stream().map(ServerAndLoad::getServerName).collect(Collectors.toList());
421  }
422
423  /**
424   * Asserts a valid retained assignment plan.
425   * <p>
426   * Must meet the following conditions:
427   * <ul>
428   * <li>Every input region has an assignment, and to an online server
429   * <li>If a region had an existing assignment to a server with the same
430   * address a a currently online server, it will be assigned to it
431   * </ul>
432   * @param existing
433   * @param servers
434   * @param assignment
435   */
436  private void assertRetainedAssignment(Map<RegionInfo, ServerName> existing,
437      List<ServerName> servers, Map<ServerName, List<RegionInfo>> assignment) {
438    // Verify condition 1, every region assigned, and to online server
439    Set<ServerName> onlineServerSet = new TreeSet<>(servers);
440    Set<RegionInfo> assignedRegions = new TreeSet<>(RegionInfo.COMPARATOR);
441    for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) {
442      assertTrue("Region assigned to server that was not listed as online",
443        onlineServerSet.contains(a.getKey()));
444      for (RegionInfo r : a.getValue())
445        assignedRegions.add(r);
446    }
447    assertEquals(existing.size(), assignedRegions.size());
448
449    // Verify condition 2, if server had existing assignment, must have same
450    Set<String> onlineHostNames = new TreeSet<>();
451    for (ServerName s : servers) {
452      onlineHostNames.add(s.getHostname());
453    }
454
455    for (Map.Entry<ServerName, List<RegionInfo>> a : assignment.entrySet()) {
456      ServerName assignedTo = a.getKey();
457      for (RegionInfo r : a.getValue()) {
458        ServerName address = existing.get(r);
459        if (address != null && onlineHostNames.contains(address.getHostname())) {
460          // this region was prevously assigned somewhere, and that
461          // host is still around, then it should be re-assigned on the
462          // same host
463          assertEquals(address.getHostname(), assignedTo.getHostname());
464        }
465      }
466    }
467  }
468
469  @Test
470  public void testClusterServersWithSameHostPort() {
471    // tests whether the BaseLoadBalancer.Cluster can be constructed with servers
472    // sharing same host and port
473    List<ServerName> servers = getListOfServerNames(randomServers(10, 10));
474    List<RegionInfo> regions = randomRegions(101);
475    Map<ServerName, List<RegionInfo>> clusterState = new HashMap<>();
476
477    assignRegions(regions, servers, clusterState);
478
479    // construct another list of servers, but sharing same hosts and ports
480    List<ServerName> oldServers = new ArrayList<>(servers.size());
481    for (ServerName sn : servers) {
482      // The old server would have had same host and port, but different start code!
483      oldServers.add(ServerName.valueOf(sn.getHostname(), sn.getPort(), sn.getStartcode() - 10));
484    }
485
486    regions = randomRegions(9); // some more regions
487    assignRegions(regions, oldServers, clusterState);
488
489    // should not throw exception:
490    BaseLoadBalancer.Cluster cluster = new Cluster(clusterState, null, null, null);
491    assertEquals(101 + 9, cluster.numRegions);
492    assertEquals(10, cluster.numServers); // only 10 servers because they share the same host + port
493  }
494
495  private void assignRegions(List<RegionInfo> regions, List<ServerName> servers,
496      Map<ServerName, List<RegionInfo>> clusterState) {
497    for (int i = 0; i < regions.size(); i++) {
498      ServerName sn = servers.get(i % servers.size());
499      List<RegionInfo> regionsOfServer = clusterState.get(sn);
500      if (regionsOfServer == null) {
501        regionsOfServer = new ArrayList<>(10);
502        clusterState.put(sn, regionsOfServer);
503      }
504
505      regionsOfServer.add(regions.get(i));
506    }
507  }
508
509  @Test
510  public void testClusterRegionLocations() {
511    // tests whether region locations are handled correctly in Cluster
512    List<ServerName> servers = getListOfServerNames(randomServers(10, 10));
513    List<RegionInfo> regions = randomRegions(101);
514    Map<ServerName, List<RegionInfo>> clusterState = new HashMap<>();
515
516    assignRegions(regions, servers, clusterState);
517
518    // mock block locality for some regions
519    RegionLocationFinder locationFinder = mock(RegionLocationFinder.class);
520    // block locality: region:0   => {server:0}
521    //                 region:1   => {server:0, server:1}
522    //                 region:42 => {server:4, server:9, server:5}
523    when(locationFinder.getTopBlockLocations(regions.get(0))).thenReturn(
524        Lists.newArrayList(servers.get(0)));
525    when(locationFinder.getTopBlockLocations(regions.get(1))).thenReturn(
526        Lists.newArrayList(servers.get(0), servers.get(1)));
527    when(locationFinder.getTopBlockLocations(regions.get(42))).thenReturn(
528        Lists.newArrayList(servers.get(4), servers.get(9), servers.get(5)));
529    when(locationFinder.getTopBlockLocations(regions.get(43))).thenReturn(
530        Lists.newArrayList(ServerName.valueOf("foo", 0, 0))); // this server does not exists in clusterStatus
531
532    BaseLoadBalancer.Cluster cluster = new Cluster(clusterState, null, locationFinder, null);
533
534    int r0 = ArrayUtils.indexOf(cluster.regions, regions.get(0)); // this is ok, it is just a test
535    int r1 = ArrayUtils.indexOf(cluster.regions, regions.get(1));
536    int r10 = ArrayUtils.indexOf(cluster.regions, regions.get(10));
537    int r42 = ArrayUtils.indexOf(cluster.regions, regions.get(42));
538    int r43 = ArrayUtils.indexOf(cluster.regions, regions.get(43));
539
540    int s0 = cluster.serversToIndex.get(servers.get(0).getHostAndPort());
541    int s1 = cluster.serversToIndex.get(servers.get(1).getHostAndPort());
542    int s4 = cluster.serversToIndex.get(servers.get(4).getHostAndPort());
543    int s5 = cluster.serversToIndex.get(servers.get(5).getHostAndPort());
544    int s9 = cluster.serversToIndex.get(servers.get(9).getHostAndPort());
545
546    // region 0 locations
547    assertEquals(1, cluster.regionLocations[r0].length);
548    assertEquals(s0, cluster.regionLocations[r0][0]);
549
550    // region 1 locations
551    assertEquals(2, cluster.regionLocations[r1].length);
552    assertEquals(s0, cluster.regionLocations[r1][0]);
553    assertEquals(s1, cluster.regionLocations[r1][1]);
554
555    // region 10 locations
556    assertEquals(0, cluster.regionLocations[r10].length);
557
558    // region 42 locations
559    assertEquals(3, cluster.regionLocations[r42].length);
560    assertEquals(s4, cluster.regionLocations[r42][0]);
561    assertEquals(s9, cluster.regionLocations[r42][1]);
562    assertEquals(s5, cluster.regionLocations[r42][2]);
563
564    // region 43 locations
565    assertEquals(1, cluster.regionLocations[r43].length);
566    assertEquals(-1, cluster.regionLocations[r43][0]);
567  }
568}