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.assertTrue; 021 022import java.util.ArrayDeque; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.LinkedHashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.NavigableSet; 030import java.util.Queue; 031import java.util.Random; 032import java.util.Set; 033import java.util.TreeMap; 034import java.util.TreeSet; 035import java.util.concurrent.ThreadLocalRandom; 036import java.util.stream.Collectors; 037import java.util.stream.Stream; 038import org.apache.hadoop.conf.Configuration; 039import org.apache.hadoop.hbase.ServerName; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.client.RegionInfo; 042import org.apache.hadoop.hbase.client.RegionInfoBuilder; 043import org.apache.hadoop.hbase.client.RegionReplicaUtil; 044import org.apache.hadoop.hbase.master.RackManager; 045import org.apache.hadoop.hbase.master.RegionPlan; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.net.DNSToSwitchMapping; 048import org.junit.Assert; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052/** 053 * Class used to be the base of unit tests on load balancers. It gives helper methods to create maps 054 * of {@link ServerName} to lists of {@link RegionInfo} and to check list of region plans. 055 */ 056public class BalancerTestBase { 057 private static final Logger LOG = LoggerFactory.getLogger(BalancerTestBase.class); 058 static int regionId = 0; 059 protected static Configuration conf; 060 061 protected int[] largeCluster = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 062 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 063 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 064 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 065 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 066 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 067 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 068 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 069 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 070 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 071 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 072 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 073 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 }; 074 075 // int[testnum][servernumber] -> numregions 076 protected int[][] clusterStateMocks = new int[][] { 077 // 1 node 078 new int[] { 0 }, new int[] { 1 }, new int[] { 10 }, 079 // 2 node 080 new int[] { 0, 0 }, new int[] { 2, 0 }, new int[] { 2, 1 }, new int[] { 2, 2 }, 081 new int[] { 2, 3 }, new int[] { 2, 4 }, new int[] { 1, 1 }, new int[] { 0, 1 }, 082 new int[] { 10, 1 }, new int[] { 514, 1432 }, new int[] { 48, 53 }, 083 // 3 node 084 new int[] { 0, 1, 2 }, new int[] { 1, 2, 3 }, new int[] { 0, 2, 2 }, new int[] { 0, 3, 0 }, 085 new int[] { 0, 4, 0 }, new int[] { 20, 20, 0 }, 086 // 4 node 087 new int[] { 0, 1, 2, 3 }, new int[] { 4, 0, 0, 0 }, new int[] { 5, 0, 0, 0 }, 088 new int[] { 6, 6, 0, 0 }, new int[] { 6, 2, 0, 0 }, new int[] { 6, 1, 0, 0 }, 089 new int[] { 6, 0, 0, 0 }, new int[] { 4, 4, 4, 7 }, new int[] { 4, 4, 4, 8 }, 090 new int[] { 0, 0, 0, 7 }, 091 // 5 node 092 new int[] { 1, 1, 1, 1, 4 }, 093 // 6 nodes 094 new int[] { 1500, 500, 500, 500, 10, 0 }, new int[] { 1500, 500, 500, 500, 500, 0 }, 095 // more nodes 096 new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 097 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 }, new int[] { 6, 6, 5, 6, 6, 6, 6, 6, 6, 1 }, 098 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 55 }, 099 new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 }, new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 }, 100 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 8 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 9 }, 101 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 10 }, new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 123 }, 102 new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 155 }, new int[] { 10, 7, 12, 8, 11, 10, 9, 14 }, 103 new int[] { 13, 14, 6, 10, 10, 10, 8, 10 }, new int[] { 130, 14, 60, 10, 100, 10, 80, 10 }, 104 new int[] { 130, 140, 60, 100, 100, 100, 80, 100 }, new int[] { 0, 5, 5, 5, 5 }, largeCluster, 105 106 }; 107 108 // int[testnum][servernumber] -> numregions 109 protected int[][] clusterStateMocksWithNoSlop = new int[][] { 110 // 1 node 111 new int[] { 0 }, new int[] { 1 }, new int[] { 10 }, 112 // 2 node 113 new int[] { 0, 0 }, new int[] { 2, 1 }, new int[] { 2, 2 }, new int[] { 2, 3 }, 114 new int[] { 1, 1 }, new int[] { 80, 120 }, new int[] { 1428, 1432 }, 115 // more nodes 116 new int[] { 100, 90, 120, 90, 110, 100, 90, 120 }, }; 117 118 // int[testnum][servernumber] -> numregions 119 protected int[][] clusterStateMocksWithSlop = new int[][] { 120 // 2 node 121 new int[] { 1, 4 }, new int[] { 10, 20 }, new int[] { 80, 123 }, 122 // more nodes 123 new int[] { 100, 100, 100, 100, 100, 100, 100, 100, 100, 200 }, 124 new int[] { 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 125 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 126 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 127 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }, }; 128 129 // This class is introduced because IP to rack resolution can be lengthy. 130 public static class MockMapping implements DNSToSwitchMapping { 131 public MockMapping(Configuration conf) { 132 } 133 134 @Override 135 public List<String> resolve(List<String> names) { 136 return Stream.generate(() -> "rack").limit(names.size()).collect(Collectors.toList()); 137 } 138 139 @Override 140 public void reloadCachedMappings() { 141 } 142 143 @Override 144 public void reloadCachedMappings(List<String> arg0) { 145 } 146 } 147 148 /** 149 * Invariant is that all servers have between floor(avg) and ceiling(avg) number of regions. 150 */ 151 public void assertClusterAsBalanced(List<ServerAndLoad> servers) { 152 int numServers = servers.size(); 153 int numRegions = 0; 154 int maxRegions = 0; 155 int minRegions = Integer.MAX_VALUE; 156 for (ServerAndLoad server : servers) { 157 int nr = server.getLoad(); 158 if (nr > maxRegions) { 159 maxRegions = nr; 160 } 161 if (nr < minRegions) { 162 minRegions = nr; 163 } 164 numRegions += nr; 165 } 166 if (maxRegions - minRegions < 2) { 167 // less than 2 between max and min, can't balance 168 return; 169 } 170 int min = numRegions / numServers; 171 int max = numRegions % numServers == 0 ? min : min + 1; 172 173 for (ServerAndLoad server : servers) { 174 assertTrue("All servers should have a positive load. " + server, server.getLoad() >= 0); 175 assertTrue("All servers should have load no more than " + max + ". " + server, 176 server.getLoad() <= max); 177 assertTrue("All servers should have load no less than " + min + ". " + server, 178 server.getLoad() >= min); 179 } 180 } 181 182 /** 183 * Invariant is that all servers have between acceptable range number of regions. 184 */ 185 public boolean assertClusterOverallAsBalanced(List<ServerAndLoad> servers, int tablenum) { 186 int numServers = servers.size(); 187 int numRegions = 0; 188 int maxRegions = 0; 189 int minRegions = Integer.MAX_VALUE; 190 for (ServerAndLoad server : servers) { 191 int nr = server.getLoad(); 192 if (nr > maxRegions) { 193 maxRegions = nr; 194 } 195 if (nr < minRegions) { 196 minRegions = nr; 197 } 198 numRegions += nr; 199 } 200 if (maxRegions - minRegions < 2) { 201 // less than 2 between max and min, can't balance 202 return true; 203 } 204 int min = numRegions / numServers; 205 int max = numRegions % numServers == 0 ? min : min + 1; 206 207 for (ServerAndLoad server : servers) { 208 // The '5' in below is arbitrary. 209 if ( 210 server.getLoad() < 0 || server.getLoad() > max + (tablenum / 2 + 5) 211 || server.getLoad() < (min - tablenum / 2 - 5) 212 ) { 213 LOG.warn("server={}, load={}, max={}, tablenum={}, min={}", server.getServerName(), 214 server.getLoad(), max, tablenum, min); 215 return false; 216 } 217 } 218 return true; 219 } 220 221 /** 222 * Checks whether region replicas are not hosted on the same host. 223 */ 224 public void assertRegionReplicaPlacement(Map<ServerName, List<RegionInfo>> serverMap, 225 RackManager rackManager) { 226 TreeMap<String, Set<RegionInfo>> regionsPerHost = new TreeMap<>(); 227 TreeMap<String, Set<RegionInfo>> regionsPerRack = new TreeMap<>(); 228 229 for (Map.Entry<ServerName, List<RegionInfo>> entry : serverMap.entrySet()) { 230 String hostname = entry.getKey().getHostname(); 231 Set<RegionInfo> infos = regionsPerHost.get(hostname); 232 if (infos == null) { 233 infos = new HashSet<>(); 234 regionsPerHost.put(hostname, infos); 235 } 236 237 for (RegionInfo info : entry.getValue()) { 238 RegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info); 239 if (!infos.add(primaryInfo)) { 240 Assert.fail("Two or more region replicas are hosted on the same host after balance"); 241 } 242 } 243 } 244 245 if (rackManager == null) { 246 return; 247 } 248 249 for (Map.Entry<ServerName, List<RegionInfo>> entry : serverMap.entrySet()) { 250 String rack = rackManager.getRack(entry.getKey()); 251 Set<RegionInfo> infos = regionsPerRack.get(rack); 252 if (infos == null) { 253 infos = new HashSet<>(); 254 regionsPerRack.put(rack, infos); 255 } 256 257 for (RegionInfo info : entry.getValue()) { 258 RegionInfo primaryInfo = RegionReplicaUtil.getRegionInfoForDefaultReplica(info); 259 if (!infos.add(primaryInfo)) { 260 Assert.fail("Two or more region replicas are hosted on the same rack after balance"); 261 } 262 } 263 } 264 } 265 266 protected String printStats(List<ServerAndLoad> servers) { 267 int numServers = servers.size(); 268 int totalRegions = 0; 269 for (ServerAndLoad server : servers) { 270 totalRegions += server.getLoad(); 271 } 272 float average = (float) totalRegions / numServers; 273 int max = (int) Math.ceil(average); 274 int min = (int) Math.floor(average); 275 return "[srvr=" + numServers + " rgns=" + totalRegions + " avg=" + average + " max=" + max 276 + " min=" + min + "]"; 277 } 278 279 protected List<ServerAndLoad> convertToList(final Map<ServerName, List<RegionInfo>> servers) { 280 List<ServerAndLoad> list = new ArrayList<>(servers.size()); 281 for (Map.Entry<ServerName, List<RegionInfo>> e : servers.entrySet()) { 282 list.add(new ServerAndLoad(e.getKey(), e.getValue().size())); 283 } 284 return list; 285 } 286 287 protected String printMock(List<ServerAndLoad> balancedCluster) { 288 if (balancedCluster == null) { 289 return "null"; 290 } 291 NavigableSet<ServerAndLoad> sorted = new TreeSet<>(balancedCluster); 292 ServerAndLoad[] arr = sorted.toArray(new ServerAndLoad[sorted.size()]); 293 StringBuilder sb = new StringBuilder(sorted.size() * 4 + 4); 294 sb.append("{ "); 295 for (int i = 0; i < arr.length; i++) { 296 if (i != 0) { 297 sb.append(" , "); 298 } 299 sb.append(arr[i].getServerName().getHostname()); 300 sb.append(":"); 301 sb.append(arr[i].getLoad()); 302 } 303 sb.append(" }"); 304 return sb.toString(); 305 } 306 307 /** 308 * This assumes the RegionPlan HSI instances are the same ones in the map, so actually no need to 309 * even pass in the map, but I think it's clearer. 310 * @return a list of all added {@link ServerAndLoad} values. 311 */ 312 protected List<ServerAndLoad> reconcile(List<ServerAndLoad> list, List<RegionPlan> plans, 313 Map<ServerName, List<RegionInfo>> servers) { 314 List<ServerAndLoad> result = new ArrayList<>(list.size()); 315 316 Map<ServerName, ServerAndLoad> map = new HashMap<>(list.size()); 317 for (ServerAndLoad sl : list) { 318 map.put(sl.getServerName(), sl); 319 } 320 if (plans != null) { 321 for (RegionPlan plan : plans) { 322 ServerName source = plan.getSource(); 323 324 updateLoad(map, source, -1); 325 ServerName destination = plan.getDestination(); 326 updateLoad(map, destination, +1); 327 328 servers.get(source).remove(plan.getRegionInfo()); 329 servers.get(destination).add(plan.getRegionInfo()); 330 } 331 } 332 result.clear(); 333 result.addAll(map.values()); 334 return result; 335 } 336 337 protected void updateLoad(final Map<ServerName, ServerAndLoad> map, final ServerName sn, 338 final int diff) { 339 ServerAndLoad sal = map.get(sn); 340 if (sal == null) sal = new ServerAndLoad(sn, 0); 341 sal = new ServerAndLoad(sn, sal.getLoad() + diff); 342 map.put(sn, sal); 343 } 344 345 protected TreeMap<ServerName, List<RegionInfo>> mockClusterServers(int[][] mockCluster) { 346 // dimension1: table, dimension2: regions per server 347 int numTables = mockCluster.length; 348 TreeMap<ServerName, List<RegionInfo>> servers = new TreeMap<>(); 349 for (int i = 0; i < numTables; i++) { 350 TableName tableName = TableName.valueOf("table" + i); 351 for (int j = 0; j < mockCluster[i].length; j++) { 352 ServerName serverName = ServerName.valueOf("server" + j, 1000, -1); 353 int numRegions = mockCluster[i][j]; 354 List<RegionInfo> regions = createRegions(numRegions, tableName); 355 servers.putIfAbsent(serverName, new ArrayList<>()); 356 servers.get(serverName).addAll(regions); 357 } 358 } 359 return servers; 360 } 361 362 protected TreeMap<ServerName, List<RegionInfo>> mockClusterServers(int[] mockCluster) { 363 return mockClusterServers(mockCluster, -1); 364 } 365 366 protected BalancerClusterState mockCluster(int[] mockCluster) { 367 return new BalancerClusterState(mockClusterServers(mockCluster, -1), null, null, null); 368 } 369 370 protected TreeMap<ServerName, List<RegionInfo>> mockClusterServers(int[] mockCluster, 371 int numTables) { 372 int numServers = mockCluster.length; 373 TreeMap<ServerName, List<RegionInfo>> servers = new TreeMap<>(); 374 for (int i = 0; i < numServers; i++) { 375 int numRegions = mockCluster[i]; 376 ServerAndLoad sal = randomServer(0); 377 List<RegionInfo> regions = randomRegions(numRegions, numTables); 378 servers.put(sal.getServerName(), regions); 379 } 380 return servers; 381 } 382 383 protected Map<ServerName, List<RegionInfo>> mockClusterServersUnsorted(int[] mockCluster, 384 int numTables) { 385 int numServers = mockCluster.length; 386 Map<ServerName, List<RegionInfo>> servers = new LinkedHashMap<>(); 387 for (int i = 0; i < numServers; i++) { 388 int numRegions = mockCluster[i]; 389 ServerAndLoad sal = randomServer(0); 390 List<RegionInfo> regions = randomRegions(numRegions, numTables); 391 servers.put(sal.getServerName(), regions); 392 } 393 return servers; 394 } 395 396 protected TreeMap<ServerName, List<RegionInfo>> mockUniformClusterServers(int[] mockCluster) { 397 int numServers = mockCluster.length; 398 TreeMap<ServerName, List<RegionInfo>> servers = new TreeMap<>(); 399 for (int i = 0; i < numServers; i++) { 400 int numRegions = mockCluster[i]; 401 ServerAndLoad sal = randomServer(0); 402 List<RegionInfo> regions = uniformRegions(numRegions); 403 servers.put(sal.getServerName(), regions); 404 } 405 return servers; 406 } 407 408 protected HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> 409 mockClusterServersWithTables(Map<ServerName, List<RegionInfo>> clusterServers) { 410 HashMap<TableName, TreeMap<ServerName, List<RegionInfo>>> result = new HashMap<>(); 411 for (Map.Entry<ServerName, List<RegionInfo>> entry : clusterServers.entrySet()) { 412 ServerName sal = entry.getKey(); 413 List<RegionInfo> regions = entry.getValue(); 414 for (RegionInfo hri : regions) { 415 TreeMap<ServerName, List<RegionInfo>> servers = result.get(hri.getTable()); 416 if (servers == null) { 417 servers = new TreeMap<>(); 418 result.put(hri.getTable(), servers); 419 } 420 List<RegionInfo> hrilist = servers.get(sal); 421 if (hrilist == null) { 422 hrilist = new ArrayList<>(); 423 servers.put(sal, hrilist); 424 } 425 hrilist.add(hri); 426 } 427 } 428 for (Map.Entry<TableName, TreeMap<ServerName, List<RegionInfo>>> entry : result.entrySet()) { 429 for (ServerName srn : clusterServers.keySet()) { 430 if (!entry.getValue().containsKey(srn)) entry.getValue().put(srn, new ArrayList<>()); 431 } 432 } 433 return result; 434 } 435 436 private Queue<RegionInfo> regionQueue = new ArrayDeque<>(); 437 438 protected List<RegionInfo> randomRegions(int numRegions) { 439 return randomRegions(numRegions, -1); 440 } 441 442 protected List<RegionInfo> createRegions(int numRegions, TableName tableName) { 443 List<RegionInfo> regions = new ArrayList<>(numRegions); 444 byte[] start = new byte[16]; 445 Bytes.random(start); 446 byte[] end = new byte[16]; 447 Bytes.random(end); 448 for (int i = 0; i < numRegions; i++) { 449 Bytes.putInt(start, 0, numRegions << 1); 450 Bytes.putInt(end, 0, (numRegions << 1) + 1); 451 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 452 .setSplit(false).build(); 453 regions.add(hri); 454 } 455 return regions; 456 } 457 458 protected List<RegionInfo> randomRegions(int numRegions, int numTables) { 459 List<RegionInfo> regions = new ArrayList<>(numRegions); 460 byte[] start = new byte[16]; 461 Bytes.random(start); 462 byte[] end = new byte[16]; 463 Bytes.random(end); 464 for (int i = 0; i < numRegions; i++) { 465 if (!regionQueue.isEmpty()) { 466 regions.add(regionQueue.poll()); 467 continue; 468 } 469 Bytes.putInt(start, 0, numRegions << 1); 470 Bytes.putInt(end, 0, (numRegions << 1) + 1); 471 TableName tableName = TableName 472 .valueOf("table" + (numTables > 0 ? ThreadLocalRandom.current().nextInt(numTables) : i)); 473 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 474 .setSplit(false).setRegionId(regionId++).build(); 475 regions.add(hri); 476 } 477 return regions; 478 } 479 480 protected List<RegionInfo> uniformRegions(int numRegions) { 481 List<RegionInfo> regions = new ArrayList<>(numRegions); 482 byte[] start = new byte[16]; 483 Bytes.random(start); 484 byte[] end = new byte[16]; 485 Bytes.random(end); 486 for (int i = 0; i < numRegions; i++) { 487 Bytes.putInt(start, 0, numRegions << 1); 488 Bytes.putInt(end, 0, (numRegions << 1) + 1); 489 TableName tableName = TableName.valueOf("table" + i); 490 RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(start).setEndKey(end) 491 .setSplit(false).build(); 492 regions.add(hri); 493 } 494 return regions; 495 } 496 497 protected void returnRegions(List<RegionInfo> regions) { 498 regionQueue.addAll(regions); 499 } 500 501 private Queue<ServerName> serverQueue = new ArrayDeque<>(); 502 503 protected ServerAndLoad randomServer(final int numRegionsPerServer) { 504 if (!this.serverQueue.isEmpty()) { 505 ServerName sn = this.serverQueue.poll(); 506 return new ServerAndLoad(sn, numRegionsPerServer); 507 } 508 Random rand = ThreadLocalRandom.current(); 509 String host = "srv" + rand.nextInt(Integer.MAX_VALUE); 510 int port = rand.nextInt(60000); 511 long startCode = rand.nextLong(); 512 ServerName sn = ServerName.valueOf(host, port, startCode); 513 return new ServerAndLoad(sn, numRegionsPerServer); 514 } 515 516 protected List<ServerAndLoad> randomServers(int numServers, int numRegionsPerServer) { 517 List<ServerAndLoad> servers = new ArrayList<>(numServers); 518 for (int i = 0; i < numServers; i++) { 519 servers.add(randomServer(numRegionsPerServer)); 520 } 521 return servers; 522 } 523 524 protected void returnServer(ServerName server) { 525 serverQueue.add(server); 526 } 527 528 protected void returnServers(List<ServerName> servers) { 529 this.serverQueue.addAll(servers); 530 } 531 532 protected Map<ServerName, List<RegionInfo>> createServerMap(int numNodes, int numRegions, 533 int numRegionsPerServer, int replication, int numTables) { 534 // construct a cluster of numNodes, having a total of numRegions. Each RS will hold 535 // numRegionsPerServer many regions except for the last one, which will host all the 536 // remaining regions 537 int[] cluster = new int[numNodes]; 538 for (int i = 0; i < numNodes; i++) { 539 cluster[i] = numRegionsPerServer; 540 } 541 cluster[cluster.length - 1] = numRegions - ((cluster.length - 1) * numRegionsPerServer); 542 Map<ServerName, List<RegionInfo>> clusterState = mockClusterServers(cluster, numTables); 543 if (replication > 0) { 544 // replicate the regions to the same servers 545 for (List<RegionInfo> regions : clusterState.values()) { 546 int length = regions.size(); 547 for (int i = 0; i < length; i++) { 548 for (int r = 1; r < replication; r++) { 549 regions.add(RegionReplicaUtil.getRegionInfoForReplica(regions.get(i), r)); 550 } 551 } 552 } 553 } 554 555 return clusterState; 556 } 557}