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}