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;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertNotNull;
022import static org.junit.jupiter.api.Assertions.assertNull;
023import static org.junit.jupiter.api.Assertions.assertTrue;
024import static org.junit.jupiter.api.Assertions.fail;
025
026import java.io.IOException;
027import java.net.InetSocketAddress;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034import java.util.Random;
035import java.util.concurrent.ThreadLocalRandom;
036import java.util.concurrent.atomic.AtomicInteger;
037import org.apache.hadoop.conf.Configuration;
038import org.apache.hadoop.hbase.CatalogFamilyFormat;
039import org.apache.hadoop.hbase.ClientMetaTableAccessor;
040import org.apache.hadoop.hbase.HBaseTestingUtil;
041import org.apache.hadoop.hbase.HConstants;
042import org.apache.hadoop.hbase.HRegionLocation;
043import org.apache.hadoop.hbase.MetaTableAccessor;
044import org.apache.hadoop.hbase.NamespaceDescriptor;
045import org.apache.hadoop.hbase.ServerName;
046import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
047import org.apache.hadoop.hbase.TableName;
048import org.apache.hadoop.hbase.client.Admin;
049import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
050import org.apache.hadoop.hbase.client.Connection;
051import org.apache.hadoop.hbase.client.RegionInfo;
052import org.apache.hadoop.hbase.client.RegionLocator;
053import org.apache.hadoop.hbase.client.Result;
054import org.apache.hadoop.hbase.client.TableDescriptor;
055import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
056import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper;
057import org.apache.hadoop.hbase.favored.FavoredNodeLoadBalancer;
058import org.apache.hadoop.hbase.favored.FavoredNodesPlan;
059import org.apache.hadoop.hbase.favored.FavoredNodesPlan.Position;
060import org.apache.hadoop.hbase.regionserver.HRegion;
061import org.apache.hadoop.hbase.regionserver.HRegionServer;
062import org.apache.hadoop.hbase.regionserver.Region;
063import org.apache.hadoop.hbase.testclassification.MasterTests;
064import org.apache.hadoop.hbase.testclassification.MediumTests;
065import org.apache.hadoop.hbase.util.Bytes;
066import org.apache.hadoop.hbase.util.Pair;
067import org.apache.zookeeper.KeeperException;
068import org.junit.jupiter.api.AfterAll;
069import org.junit.jupiter.api.BeforeAll;
070import org.junit.jupiter.api.Disabled;
071import org.junit.jupiter.api.Tag;
072import org.junit.jupiter.api.Test;
073import org.slf4j.Logger;
074import org.slf4j.LoggerFactory;
075
076@Tag(MasterTests.TAG)
077@Tag(MediumTests.TAG)
078public class TestRegionPlacement {
079
080  private static final Logger LOG = LoggerFactory.getLogger(TestRegionPlacement.class);
081  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
082  private final static int SLAVES = 10;
083  private static Connection CONNECTION;
084  private static Admin admin;
085  private static RegionPlacementMaintainer rp;
086  private static Position[] positions = Position.values();
087  private int lastRegionOnPrimaryRSCount = 0;
088  private int REGION_NUM = 10;
089  private Map<RegionInfo, ServerName[]> favoredNodesAssignmentPlan = new HashMap<>();
090
091  @BeforeAll
092  public static void setupBeforeClass() throws Exception {
093    Configuration conf = TEST_UTIL.getConfiguration();
094    // Enable the favored nodes based load balancer
095    conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, FavoredNodeLoadBalancer.class,
096      LoadBalancer.class);
097    conf.setBoolean("hbase.tests.use.shortcircuit.reads", false);
098    TEST_UTIL.startMiniCluster(SLAVES);
099    CONNECTION = TEST_UTIL.getConnection();
100    admin = CONNECTION.getAdmin();
101    rp = new RegionPlacementMaintainer(conf);
102  }
103
104  @AfterAll
105  public static void tearDownAfterClass() throws Exception {
106    TEST_UTIL.shutdownMiniCluster();
107  }
108
109  @Disabled("Test for unfinished feature")
110  @Test
111  public void testRegionPlacement() throws Exception {
112    String tableStr = "testRegionAssignment";
113    TableName table = TableName.valueOf(tableStr);
114    // Create a table with REGION_NUM regions.
115    createTable(table, REGION_NUM);
116
117    TEST_UTIL.waitTableAvailable(table);
118
119    // Verify all the user regions are assigned to the primary region server
120    // based on the plan
121    verifyRegionOnPrimaryRS(REGION_NUM);
122
123    FavoredNodesPlan currentPlan = rp.getRegionAssignmentSnapshot().getExistingAssignmentPlan();
124    // Verify all the region server are update with the latest favored nodes
125    verifyRegionServerUpdated(currentPlan);
126    // Test Case 2: To verify whether the region placement tools can
127    // correctly update the new assignment plan to hbase:meta and Region Server.
128    // The new assignment plan is generated by shuffle the existing assignment
129    // plan by switching PRIMARY, SECONDARY and TERTIARY nodes.
130    // Shuffle the plan by switching the secondary region server with
131    // the tertiary.
132
133    // Shuffle the secondary with tertiary favored nodes
134    FavoredNodesPlan shuffledPlan = this.shuffleAssignmentPlan(currentPlan,
135      FavoredNodesPlan.Position.SECONDARY, FavoredNodesPlan.Position.TERTIARY);
136    // Let the region placement update the hbase:meta and Region Servers
137    rp.updateAssignmentPlan(shuffledPlan);
138
139    // Verify the region assignment. There are supposed to no region reassignment
140    // All the regions are still on the primary region server
141    verifyRegionAssignment(shuffledPlan, 0, REGION_NUM);
142
143    // Shuffle the plan by switching the primary with secondary and
144    // verify the region reassignment is consistent with the plan.
145    shuffledPlan = this.shuffleAssignmentPlan(currentPlan, FavoredNodesPlan.Position.PRIMARY,
146      FavoredNodesPlan.Position.SECONDARY);
147
148    // Let the region placement update the hbase:meta and Region Servers
149    rp.updateAssignmentPlan(shuffledPlan);
150
151    verifyRegionAssignment(shuffledPlan, REGION_NUM, REGION_NUM);
152
153    // also verify that the AssignmentVerificationReport has the correct information
154    RegionPlacementMaintainer rp = new RegionPlacementMaintainer(TEST_UTIL.getConfiguration());
155    // we are interested in only one table (and hence one report)
156    rp.setTargetTableName(new String[] { tableStr });
157    List<AssignmentVerificationReport> reports = rp.verifyRegionPlacement(false);
158    AssignmentVerificationReport report = reports.get(0);
159    assertTrue(report.getRegionsWithoutValidFavoredNodes().isEmpty());
160    assertTrue(report.getNonFavoredAssignedRegions().isEmpty());
161    assertTrue(report.getTotalFavoredAssignments() >= REGION_NUM);
162    assertTrue(report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY) != 0);
163    assertTrue(
164      report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY) == 0);
165    assertTrue(
166      report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY) == 0);
167    assertTrue(report.getUnassignedRegions().isEmpty());
168
169    // Check when a RS stops, the regions get assigned to their secondary/tertiary
170    killRandomServerAndVerifyAssignment();
171
172    // also verify that the AssignmentVerificationReport has the correct information
173    reports = rp.verifyRegionPlacement(false);
174    report = reports.get(0);
175    assertTrue(report.getRegionsWithoutValidFavoredNodes().isEmpty());
176    assertTrue(report.getNonFavoredAssignedRegions().isEmpty());
177    assertTrue(report.getTotalFavoredAssignments() >= REGION_NUM);
178    assertTrue(report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY) > 0);
179    assertTrue(
180      (report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY) > 0
181        || report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY) > 0),
182      "secondary "
183        + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY)
184        + " tertiary "
185        + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY));
186    assertTrue((report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.PRIMARY)
187      + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.SECONDARY)
188      + report.getNumRegionsOnFavoredNodeByPosition(FavoredNodesPlan.Position.TERTIARY))
189        == REGION_NUM);
190    RegionPlacementMaintainer.printAssignmentPlan(currentPlan);
191  }
192
193  private void killRandomServerAndVerifyAssignment()
194    throws IOException, InterruptedException, KeeperException {
195    ServerName serverToKill = null;
196    int killIndex = 0;
197    Random rand = ThreadLocalRandom.current();
198    ServerName metaServer = TEST_UTIL.getHBaseCluster().getServerHoldingMeta();
199    LOG.debug("Server holding meta " + metaServer);
200    boolean isNamespaceServer = false;
201    do {
202      // kill a random non-meta server carrying at least one region
203      killIndex = rand.nextInt(SLAVES);
204      serverToKill = TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getServerName();
205      Collection<HRegion> regs =
206        TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getOnlineRegionsLocalContext();
207      isNamespaceServer = false;
208      for (HRegion r : regs) {
209        if (
210          r.getRegionInfo().getTable().getNamespaceAsString()
211            .equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR)
212        ) {
213          isNamespaceServer = true;
214          break;
215        }
216      }
217    } while (
218      ServerName.isSameAddress(metaServer, serverToKill) || isNamespaceServer
219        || TEST_UTIL.getHBaseCluster().getRegionServer(killIndex).getNumberOfOnlineRegions() == 0
220    );
221    LOG.debug("Stopping RS " + serverToKill);
222    Map<RegionInfo, Pair<ServerName, ServerName>> regionsToVerify = new HashMap<>();
223    // mark the regions to track
224    for (Map.Entry<RegionInfo, ServerName[]> entry : favoredNodesAssignmentPlan.entrySet()) {
225      ServerName s = entry.getValue()[0];
226      if (ServerName.isSameAddress(s, serverToKill)) {
227        regionsToVerify.put(entry.getKey(), new Pair<>(entry.getValue()[1], entry.getValue()[2]));
228        LOG.debug("Adding " + entry.getKey() + " with sedcondary/tertiary " + entry.getValue()[1]
229          + " " + entry.getValue()[2]);
230      }
231    }
232    int orig = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
233    TEST_UTIL.getHBaseCluster().stopRegionServer(serverToKill);
234    TEST_UTIL.getHBaseCluster().waitForRegionServerToStop(serverToKill, 60000);
235    int curr = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
236    while (curr - orig < regionsToVerify.size()) {
237      LOG.debug("Waiting for " + regionsToVerify.size() + " to come online " + " Current #regions "
238        + curr + " Original #regions " + orig);
239      Thread.sleep(200);
240      curr = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getNumRegionsOpened();
241    }
242    // now verify
243    for (Map.Entry<RegionInfo, Pair<ServerName, ServerName>> entry : regionsToVerify.entrySet()) {
244      ServerName newDestination = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager()
245        .getRegionStates().getRegionServerOfRegion(entry.getKey());
246      Pair<ServerName, ServerName> secondaryTertiaryServers = entry.getValue();
247      LOG.debug("New destination for region " + entry.getKey().getEncodedName() + " "
248        + newDestination + ". Secondary/Tertiary are " + secondaryTertiaryServers.getFirst() + "/"
249        + secondaryTertiaryServers.getSecond());
250      if (
251        !(ServerName.isSameAddress(newDestination, secondaryTertiaryServers.getFirst())
252          || ServerName.isSameAddress(newDestination, secondaryTertiaryServers.getSecond()))
253      ) {
254        fail("Region " + entry.getKey() + " not present on any of the expected servers");
255      }
256    }
257    // start(reinstate) region server since we killed one before
258    TEST_UTIL.getHBaseCluster().startRegionServer();
259  }
260
261  /**
262   * Used to test the correctness of this class.
263   */
264  @Disabled("Test for unfinished feature")
265  @Test
266  public void testRandomizedMatrix() {
267    int rows = 100;
268    int cols = 100;
269    float[][] matrix = new float[rows][cols];
270    Random rand = ThreadLocalRandom.current();
271    for (int i = 0; i < rows; i++) {
272      for (int j = 0; j < cols; j++) {
273        matrix[i][j] = rand.nextFloat();
274      }
275    }
276
277    // Test that inverting a transformed matrix gives the original matrix.
278    RegionPlacementMaintainer.RandomizedMatrix rm =
279      new RegionPlacementMaintainer.RandomizedMatrix(rows, cols);
280    float[][] transformed = rm.transform(matrix);
281    float[][] invertedTransformed = rm.invert(transformed);
282    for (int i = 0; i < rows; i++) {
283      for (int j = 0; j < cols; j++) {
284        if (matrix[i][j] != invertedTransformed[i][j]) {
285          throw new RuntimeException();
286        }
287      }
288    }
289
290    // Test that the indices on a transformed matrix can be inverted to give
291    // the same values on the original matrix.
292    int[] transformedIndices = new int[rows];
293    for (int i = 0; i < rows; i++) {
294      transformedIndices[i] = rand.nextInt(cols);
295    }
296    int[] invertedTransformedIndices = rm.invertIndices(transformedIndices);
297    float[] transformedValues = new float[rows];
298    float[] invertedTransformedValues = new float[rows];
299    for (int i = 0; i < rows; i++) {
300      transformedValues[i] = transformed[i][transformedIndices[i]];
301      invertedTransformedValues[i] = matrix[i][invertedTransformedIndices[i]];
302    }
303    Arrays.sort(transformedValues);
304    Arrays.sort(invertedTransformedValues);
305    if (!Arrays.equals(transformedValues, invertedTransformedValues)) {
306      throw new RuntimeException();
307    }
308  }
309
310  /**
311   * Shuffle the assignment plan by switching two favored node positions.
312   * @param plan The assignment plan
313   * @param p1   The first switch position
314   * @param p2   The second switch position
315   * @return the shuffled assignment plan
316   */
317  private FavoredNodesPlan shuffleAssignmentPlan(FavoredNodesPlan plan,
318    FavoredNodesPlan.Position p1, FavoredNodesPlan.Position p2) throws IOException {
319    FavoredNodesPlan shuffledPlan = new FavoredNodesPlan();
320
321    Map<String, RegionInfo> regionToHRegion =
322      rp.getRegionAssignmentSnapshot().getRegionNameToRegionInfoMap();
323    for (Map.Entry<String, List<ServerName>> entry : plan.getAssignmentMap().entrySet()) {
324
325      // copy the server list from the original plan
326      List<ServerName> shuffledServerList = new ArrayList<>();
327      shuffledServerList.addAll(entry.getValue());
328
329      // start to shuffle
330      shuffledServerList.set(p1.ordinal(), entry.getValue().get(p2.ordinal()));
331      shuffledServerList.set(p2.ordinal(), entry.getValue().get(p1.ordinal()));
332
333      // update the plan
334      shuffledPlan.updateFavoredNodesMap(regionToHRegion.get(entry.getKey()), shuffledServerList);
335    }
336    return shuffledPlan;
337  }
338
339  /**
340   * To verify the region assignment status. It will check the assignment plan consistency between
341   * hbase:meta and region servers. Also it will verify weather the number of region movement and
342   * the number regions on the primary region server are expected
343   */
344  private void verifyRegionAssignment(FavoredNodesPlan plan, int regionMovementNum,
345    int numRegionsOnPrimaryRS) throws InterruptedException, IOException {
346    // Verify the assignment plan in hbase:meta is consistent with the expected plan.
347    verifyMETAUpdated(plan);
348
349    // Verify the number of region movement is expected
350    verifyRegionMovementNum(regionMovementNum);
351
352    // Verify the number of regions is assigned to the primary region server
353    // based on the plan is expected
354    verifyRegionOnPrimaryRS(numRegionsOnPrimaryRS);
355
356    // Verify all the online region server are updated with the assignment plan
357    verifyRegionServerUpdated(plan);
358  }
359
360  /**
361   * Verify the meta has updated to the latest assignment plan
362   * @param expectedPlan the region assignment plan
363   * @throws IOException if an IO problem is encountered
364   */
365  private void verifyMETAUpdated(FavoredNodesPlan expectedPlan) throws IOException {
366    FavoredNodesPlan planFromMETA = rp.getRegionAssignmentSnapshot().getExistingAssignmentPlan();
367    assertTrue(planFromMETA.equals(expectedPlan),
368      "The assignment plan is NOT consistent with the expected plan ");
369  }
370
371  /**
372   * Verify the number of region movement is expected
373   */
374  private void verifyRegionMovementNum(int expected) throws InterruptedException, IOException {
375    SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
376    HMaster m = cluster.getMaster();
377    int lastRegionOpenedCount = m.getAssignmentManager().getNumRegionsOpened();
378    // get the assignments start to execute
379    m.balance();
380
381    int retry = 10;
382    long sleep = 3000;
383    int attempt = 0;
384    int currentRegionOpened, regionMovement;
385    do {
386      currentRegionOpened = m.getAssignmentManager().getNumRegionsOpened();
387      regionMovement = currentRegionOpened - lastRegionOpenedCount;
388      LOG.debug("There are " + regionMovement + "/" + expected + " regions moved after " + attempt
389        + " attempts");
390      Thread.sleep((++attempt) * sleep);
391    } while (regionMovement != expected && attempt <= retry);
392
393    // update the lastRegionOpenedCount
394    lastRegionOpenedCount = currentRegionOpened;
395
396    assertEquals(expected, regionMovement, "There are only " + regionMovement + " instead of "
397      + expected + " region movement for " + attempt + " attempts");
398  }
399
400  /**
401   * Verify the number of user regions is assigned to the primary region server based on the plan is
402   * expected
403   * @param expectedNum the expected number of assigned regions
404   */
405  private void verifyRegionOnPrimaryRS(int expectedNum) throws IOException {
406    lastRegionOnPrimaryRSCount = getNumRegionisOnPrimaryRS();
407    assertEquals(expectedNum, lastRegionOnPrimaryRSCount,
408      "Only " + expectedNum + " of user regions running " + "on the primary region server");
409  }
410
411  /**
412   * Verify all the online region servers has been updated to the latest assignment plan
413   */
414  private void verifyRegionServerUpdated(FavoredNodesPlan plan) throws IOException {
415    // Verify all region servers contain the correct favored nodes information
416    SingleProcessHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
417    for (int i = 0; i < SLAVES; i++) {
418      HRegionServer rs = cluster.getRegionServer(i);
419      for (Region region : rs.getRegions(TableName.valueOf("testRegionAssignment"))) {
420        InetSocketAddress[] favoredSocketAddress =
421          rs.getFavoredNodesForRegion(region.getRegionInfo().getEncodedName());
422        String regionName = region.getRegionInfo().getRegionNameAsString();
423        List<ServerName> favoredServerList = plan.getAssignmentMap().get(regionName);
424
425        // All regions are supposed to have favored nodes,
426        // except for hbase:meta and ROOT
427        if (favoredServerList == null) {
428          TableDescriptor desc = region.getTableDescriptor();
429          // Verify they are ROOT and hbase:meta regions since no favored nodes
430          assertNull(favoredSocketAddress);
431          assertTrue(desc.isMetaRegion(), "User region "
432            + region.getTableDescriptor().getTableName() + " should have favored nodes");
433        } else {
434          // For user region, the favored nodes in the region server should be
435          // identical to favored nodes in the assignmentPlan
436          assertTrue(favoredSocketAddress.length == favoredServerList.size());
437          assertTrue(favoredServerList.size() > 0);
438          for (int j = 0; j < favoredServerList.size(); j++) {
439            InetSocketAddress addrFromRS = favoredSocketAddress[j];
440            InetSocketAddress addrFromPlan = InetSocketAddress.createUnresolved(
441              favoredServerList.get(j).getHostname(), favoredServerList.get(j).getPort());
442
443            assertNotNull(addrFromRS);
444            assertNotNull(addrFromPlan);
445            assertTrue(addrFromRS.equals(addrFromPlan),
446              "Region server " + rs.getServerName().getAddress() + " has the " + positions[j]
447                + " for region " + region.getRegionInfo().getRegionNameAsString() + " is "
448                + addrFromRS + " which is inconsistent with the plan " + addrFromPlan);
449          }
450        }
451      }
452    }
453  }
454
455  /**
456   * Check whether regions are assigned to servers consistent with the explicit hints that are
457   * persisted in the hbase:meta table. Also keep track of the number of the regions are assigned to
458   * the primary region server.
459   * @return the number of regions are assigned to the primary region server
460   */
461  private int getNumRegionisOnPrimaryRS() throws IOException {
462    final AtomicInteger regionOnPrimaryNum = new AtomicInteger(0);
463    final AtomicInteger totalRegionNum = new AtomicInteger(0);
464    LOG.info("The start of region placement verification");
465    ClientMetaTableAccessor.Visitor visitor = new ClientMetaTableAccessor.Visitor() {
466      @Override
467      public boolean visit(Result result) throws IOException {
468        try {
469          @SuppressWarnings("deprecation")
470          RegionInfo info = CatalogFamilyFormat.getRegionInfo(result);
471          if (
472            info.getTable().getNamespaceAsString()
473              .equals(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR)
474          ) {
475            return true;
476          }
477          byte[] server = result.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
478          byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY,
479            FavoredNodeAssignmentHelper.FAVOREDNODES_QUALIFIER);
480          // Add the favored nodes into assignment plan
481          ServerName[] favoredServerList =
482            FavoredNodeAssignmentHelper.getFavoredNodesList(favoredNodes);
483          favoredNodesAssignmentPlan.put(info, favoredServerList);
484
485          Position[] positions = Position.values();
486          if (info != null) {
487            totalRegionNum.incrementAndGet();
488            if (server != null) {
489              ServerName serverName = ServerName.valueOf(Bytes.toString(server), -1);
490              if (favoredNodes != null) {
491                String placement = "[NOT FAVORED NODE]";
492                for (int i = 0; i < favoredServerList.length; i++) {
493                  if (favoredServerList[i].equals(serverName)) {
494                    placement = positions[i].toString();
495                    if (i == Position.PRIMARY.ordinal()) {
496                      regionOnPrimaryNum.incrementAndGet();
497                    }
498                    break;
499                  }
500                }
501                LOG.info(info.getRegionNameAsString() + " on " + serverName + " " + placement);
502              } else {
503                LOG.info(info.getRegionNameAsString() + " running on " + serverName
504                  + " but there is no favored region server");
505              }
506            } else {
507              LOG.info(info.getRegionNameAsString() + " not assigned to any server");
508            }
509          }
510          return true;
511        } catch (RuntimeException e) {
512          LOG.error("Result=" + result);
513          throw e;
514        }
515      }
516    };
517    MetaTableAccessor.fullScanRegions(CONNECTION, visitor);
518    LOG.info("There are " + regionOnPrimaryNum.intValue() + " out of " + totalRegionNum.intValue()
519      + " regions running on the primary" + " region servers");
520    return regionOnPrimaryNum.intValue();
521  }
522
523  /**
524   * Create a table with specified table name and region number.
525   * @param tableName the name of the table to be created
526   * @param regionNum number of regions to create
527   */
528  private static void createTable(TableName tableName, int regionNum) throws IOException {
529    int expectedRegions = regionNum;
530    byte[][] splitKeys = new byte[expectedRegions - 1][];
531    for (int i = 1; i < expectedRegions; i++) {
532      byte splitKey = (byte) i;
533      splitKeys[i - 1] = new byte[] { splitKey, splitKey, splitKey };
534    }
535
536    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(tableName)
537      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(HConstants.CATALOG_FAMILY)).build();
538    admin.createTable(tableDescriptor, splitKeys);
539
540    try (RegionLocator r = CONNECTION.getRegionLocator(tableName)) {
541      List<HRegionLocation> regions = r.getAllRegionLocations();
542      assertEquals(expectedRegions, regions.size(),
543        "Tried to create " + expectedRegions + " regions " + "but only found " + regions.size());
544    }
545  }
546}