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.apache.hadoop.hbase.ServerName.NON_STARTCODE;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026
027import java.io.IOException;
028import java.util.EnumSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Map.Entry;
032import java.util.Set;
033import java.util.stream.Collectors;
034import org.apache.hadoop.conf.Configuration;
035import org.apache.hadoop.hbase.ClusterMetrics.Option;
036import org.apache.hadoop.hbase.HBaseClassTestRule;
037import org.apache.hadoop.hbase.HBaseTestingUtility;
038import org.apache.hadoop.hbase.HColumnDescriptor;
039import org.apache.hadoop.hbase.HConstants;
040import org.apache.hadoop.hbase.HTableDescriptor;
041import org.apache.hadoop.hbase.MiniHBaseCluster;
042import org.apache.hadoop.hbase.ServerName;
043import org.apache.hadoop.hbase.TableName;
044import org.apache.hadoop.hbase.Waiter;
045import org.apache.hadoop.hbase.client.Admin;
046import org.apache.hadoop.hbase.client.RegionInfo;
047import org.apache.hadoop.hbase.favored.FavoredNodeAssignmentHelper;
048import org.apache.hadoop.hbase.favored.FavoredNodesManager;
049import org.apache.hadoop.hbase.favored.FavoredNodesPlan;
050import org.apache.hadoop.hbase.master.HMaster;
051import org.apache.hadoop.hbase.master.LoadBalancer;
052import org.apache.hadoop.hbase.master.ServerManager;
053import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
054import org.apache.hadoop.hbase.master.assignment.RegionStates;
055import org.apache.hadoop.hbase.regionserver.HRegion;
056import org.apache.hadoop.hbase.testclassification.MediumTests;
057import org.apache.hadoop.hbase.util.Bytes;
058import org.apache.hadoop.hbase.util.JVMClusterUtil;
059import org.junit.After;
060import org.junit.Before;
061import org.junit.BeforeClass;
062import org.junit.ClassRule;
063import org.junit.Ignore;
064import org.junit.Test;
065import org.junit.experimental.categories.Category;
066import org.slf4j.Logger;
067import org.slf4j.LoggerFactory;
068
069import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
070import org.apache.hbase.thirdparty.com.google.common.collect.Maps;
071import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
072
073@Ignore // Disabled
074@Category(MediumTests.class)
075public class TestFavoredStochasticLoadBalancer extends BalancerTestBase {
076
077  @ClassRule
078  public static final HBaseClassTestRule CLASS_RULE =
079      HBaseClassTestRule.forClass(TestFavoredStochasticLoadBalancer.class);
080
081  private static final Logger LOG =
082      LoggerFactory.getLogger(TestFavoredStochasticLoadBalancer.class);
083
084  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
085  private static final int SLAVES = 8;
086  private static final int REGION_NUM = SLAVES * 3;
087
088  private Admin admin;
089  private HMaster master;
090  private MiniHBaseCluster cluster;
091
092  @BeforeClass
093  public static void setupBeforeClass() throws Exception {
094    Configuration conf = TEST_UTIL.getConfiguration();
095    // Enable the favored nodes based load balancer
096    conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS,
097        LoadOnlyFavoredStochasticBalancer.class, LoadBalancer.class);
098  }
099
100  @Before
101  public void startCluster() throws Exception {
102    TEST_UTIL.startMiniCluster(SLAVES);
103    TEST_UTIL.getDFSCluster().waitClusterUp();
104    cluster = TEST_UTIL.getMiniHBaseCluster();
105    master = TEST_UTIL.getMiniHBaseCluster().getMaster();
106    admin = TEST_UTIL.getAdmin();
107    admin.setBalancerRunning(false, true);
108  }
109
110  @After
111  public void stopCluster() throws Exception {
112    TEST_UTIL.cleanupTestDir();
113    TEST_UTIL.shutdownMiniCluster();
114  }
115
116  @Test
117  public void testBasicBalance() throws Exception {
118
119    TableName tableName = TableName.valueOf("testBasicBalance");
120    HTableDescriptor desc = new HTableDescriptor(tableName);
121    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
122    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
123    TEST_UTIL.waitTableAvailable(tableName);
124    TEST_UTIL.loadTable(admin.getConnection().getTable(tableName), HConstants.CATALOG_FAMILY);
125    admin.flush(tableName);
126    compactTable(tableName);
127
128    JVMClusterUtil.RegionServerThread rs1 = cluster.startRegionServerAndWait(10000);
129    JVMClusterUtil.RegionServerThread rs2 = cluster.startRegionServerAndWait(10000);
130
131    // Now try to run balance, and verify no regions are moved to the 2 region servers recently
132    // started.
133    admin.setBalancerRunning(true, true);
134    assertTrue("Balancer did not run", admin.balancer());
135    TEST_UTIL.waitUntilNoRegionsInTransition(120000);
136
137    List<RegionInfo> hris = admin.getRegions(rs1.getRegionServer().getServerName());
138    for (RegionInfo hri : hris) {
139      assertFalse("New RS contains regions belonging to table: " + tableName,
140        hri.getTable().equals(tableName));
141    }
142    hris = admin.getRegions(rs2.getRegionServer().getServerName());
143    for (RegionInfo hri : hris) {
144      assertFalse("New RS contains regions belonging to table: " + tableName,
145        hri.getTable().equals(tableName));
146    }
147  }
148
149  @Test
150  public void testRoundRobinAssignment() throws Exception {
151
152    TableName tableName = TableName.valueOf("testRoundRobinAssignment");
153    HTableDescriptor desc = new HTableDescriptor(tableName);
154    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
155    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
156    TEST_UTIL.waitTableAvailable(tableName);
157    TEST_UTIL.loadTable(admin.getConnection().getTable(tableName), HConstants.CATALOG_FAMILY);
158    admin.flush(tableName);
159
160    LoadBalancer balancer = master.getLoadBalancer();
161    List<RegionInfo> regions = admin.getRegions(tableName);
162    regions.addAll(admin.getTableRegions(TableName.META_TABLE_NAME));
163    regions.addAll(admin.getTableRegions(TableName.NAMESPACE_TABLE_NAME));
164    List<ServerName> servers = Lists.newArrayList(
165      admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet());
166    Map<ServerName, List<RegionInfo>> map = balancer.roundRobinAssignment(regions, servers);
167    for (List<RegionInfo> regionInfos : map.values()) {
168      regions.removeAll(regionInfos);
169    }
170    assertEquals("No region should be missed by balancer", 0, regions.size());
171  }
172
173
174  @Test
175  public void testBasicRegionPlacementAndReplicaLoad() throws Exception {
176
177    String tableName = "testBasicRegionPlacement";
178    HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
179    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
180    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
181    TEST_UTIL.waitTableAvailable(desc.getTableName());
182
183    FavoredNodesManager fnm = master.getFavoredNodesManager();
184    List<RegionInfo> regionsOfTable = admin.getRegions(TableName.valueOf(tableName));
185    for (RegionInfo rInfo : regionsOfTable) {
186      Set<ServerName> favNodes = Sets.newHashSet(fnm.getFavoredNodes(rInfo));
187      assertNotNull(favNodes);
188      assertEquals(FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, favNodes.size());
189    }
190
191    Map<ServerName, List<Integer>> replicaLoadMap = fnm.getReplicaLoad(
192      Lists.newArrayList(admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
193                              .getLiveServerMetrics().keySet()));
194    assertTrue("Not all replica load collected.",
195      admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
196           .getLiveServerMetrics().size() == replicaLoadMap.size());
197    for (Entry<ServerName, List<Integer>> entry : replicaLoadMap.entrySet()) {
198      assertTrue(entry.getValue().size() == FavoredNodeAssignmentHelper.FAVORED_NODES_NUM);
199      assertTrue(entry.getValue().get(0) >= 0);
200      assertTrue(entry.getValue().get(1) >= 0);
201      assertTrue(entry.getValue().get(2) >= 0);
202    }
203
204    admin.disableTable(TableName.valueOf(tableName));
205    admin.deleteTable(TableName.valueOf(tableName));
206    replicaLoadMap = fnm.getReplicaLoad(Lists.newArrayList(
207      admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics().keySet()));
208    assertTrue("replica load found " + replicaLoadMap.size() + " instead of 0.",
209      replicaLoadMap.size() == admin
210          .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics()
211          .size());
212  }
213
214  @Test
215  public void testRandomAssignmentWithNoFavNodes() throws Exception {
216
217    final String tableName = "testRandomAssignmentWithNoFavNodes";
218    HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
219    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
220    admin.createTable(desc);
221    TEST_UTIL.waitTableAvailable(desc.getTableName());
222
223    RegionInfo hri = admin.getTableRegions(TableName.valueOf(tableName)).get(0);
224
225    FavoredNodesManager fnm = master.getFavoredNodesManager();
226    fnm.deleteFavoredNodesForRegions(Lists.newArrayList(hri));
227    assertNull("Favored nodes not found null after delete", fnm.getFavoredNodes(hri));
228
229    LoadBalancer balancer = master.getLoadBalancer();
230    ServerName destination = balancer.randomAssignment(hri, Lists.newArrayList(admin
231        .getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)).getLiveServerMetrics()
232        .keySet().stream().collect(Collectors.toList())));
233    assertNotNull(destination);
234    List<ServerName> favoredNodes = fnm.getFavoredNodes(hri);
235    assertNotNull(favoredNodes);
236    boolean containsFN = false;
237    for (ServerName sn : favoredNodes) {
238      if (ServerName.isSameAddress(destination, sn)) {
239        containsFN = true;
240      }
241    }
242    assertTrue("Destination server does not belong to favored nodes.", containsFN);
243  }
244
245  @Test
246  public void testBalancerWithoutFavoredNodes() throws Exception {
247
248    TableName tableName = TableName.valueOf("testBalancerWithoutFavoredNodes");
249    HTableDescriptor desc = new HTableDescriptor(tableName);
250    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
251    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
252    TEST_UTIL.waitTableAvailable(tableName);
253
254    final RegionInfo region = admin.getTableRegions(tableName).get(0);
255    LOG.info("Region thats supposed to be in transition: " + region);
256    FavoredNodesManager fnm = master.getFavoredNodesManager();
257    List<ServerName> currentFN = fnm.getFavoredNodes(region);
258    assertNotNull(currentFN);
259
260    fnm.deleteFavoredNodesForRegions(Lists.newArrayList(region));
261
262    RegionStates regionStates = master.getAssignmentManager().getRegionStates();
263    admin.setBalancerRunning(true, true);
264
265    // Balancer should unassign the region
266    assertTrue("Balancer did not run", admin.balancer());
267    TEST_UTIL.waitUntilNoRegionsInTransition();
268
269    admin.assign(region.getEncodedNameAsBytes());
270    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
271
272    currentFN = fnm.getFavoredNodes(region);
273    assertNotNull(currentFN);
274    assertEquals("Expected number of FN not present",
275      FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, currentFN.size());
276
277    assertTrue("Balancer did not run", admin.balancer());
278    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
279
280    checkFavoredNodeAssignments(tableName, fnm, regionStates);
281  }
282
283  @Ignore @Test
284  public void testMisplacedRegions() throws Exception {
285
286    TableName tableName = TableName.valueOf("testMisplacedRegions");
287    HTableDescriptor desc = new HTableDescriptor(tableName);
288    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
289    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
290    TEST_UTIL.waitTableAvailable(tableName);
291
292    final RegionInfo misplacedRegion = admin.getTableRegions(tableName).get(0);
293    FavoredNodesManager fnm = master.getFavoredNodesManager();
294    List<ServerName> currentFN = fnm.getFavoredNodes(misplacedRegion);
295    assertNotNull(currentFN);
296
297    List<ServerName> serversForNewFN = Lists.newArrayList();
298    for (ServerName sn : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
299      .getLiveServerMetrics().keySet()) {
300      serversForNewFN.add(ServerName.valueOf(sn.getHostname(), sn.getPort(), NON_STARTCODE));
301    }
302    for (ServerName sn : currentFN) {
303      serversForNewFN.remove(sn);
304    }
305    FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(serversForNewFN, conf);
306    helper.initialize();
307    List<ServerName> newFavoredNodes = helper.generateFavoredNodes(misplacedRegion);
308    assertNotNull(newFavoredNodes);
309    assertEquals(FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, newFavoredNodes.size());
310    Map<RegionInfo, List<ServerName>> regionFNMap = Maps.newHashMap();
311    regionFNMap.put(misplacedRegion, newFavoredNodes);
312    fnm.updateFavoredNodes(regionFNMap);
313
314    final RegionStates regionStates = master.getAssignmentManager().getRegionStates();
315    final ServerName current = regionStates.getRegionServerOfRegion(misplacedRegion);
316    assertNull("Misplaced region is still hosted on favored node, not expected.",
317        FavoredNodesPlan.getFavoredServerPosition(fnm.getFavoredNodes(misplacedRegion), current));
318    admin.setBalancerRunning(true, true);
319    assertTrue("Balancer did not run", admin.balancer());
320    TEST_UTIL.waitFor(120000, 30000, new Waiter.Predicate<Exception>() {
321      @Override
322      public boolean evaluate() throws Exception {
323        ServerName host = regionStates.getRegionServerOfRegion(misplacedRegion);
324        return !ServerName.isSameAddress(host, current);
325      }
326    });
327    checkFavoredNodeAssignments(tableName, fnm, regionStates);
328  }
329
330  @Test
331  public void test2FavoredNodesDead() throws Exception {
332
333    TableName tableName = TableName.valueOf("testAllFavoredNodesDead");
334    HTableDescriptor desc = new HTableDescriptor(tableName);
335    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
336    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
337    TEST_UTIL.waitTableAvailable(tableName);
338
339    final RegionInfo region = admin.getTableRegions(tableName).get(0);
340    LOG.info("Region that's supposed to be in transition: " + region);
341    FavoredNodesManager fnm = master.getFavoredNodesManager();
342    List<ServerName> currentFN = fnm.getFavoredNodes(region);
343    assertNotNull(currentFN);
344
345    List<ServerName> serversToStop = Lists.newArrayList(currentFN);
346    serversToStop.remove(currentFN.get(0));
347
348    // Lets kill 2 FN for the region. All regions should still be assigned
349    stopServersAndWaitUntilProcessed(serversToStop);
350
351    TEST_UTIL.waitUntilNoRegionsInTransition();
352    final RegionStates regionStates = master.getAssignmentManager().getRegionStates();
353    TEST_UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
354      @Override
355      public boolean evaluate() throws Exception {
356        return regionStates.getRegionState(region).isOpened();
357      }
358    });
359
360    assertEquals("Not all regions are online", REGION_NUM, admin.getTableRegions(tableName).size());
361    admin.setBalancerRunning(true, true);
362    assertTrue("Balancer did not run", admin.balancer());
363    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
364
365    checkFavoredNodeAssignments(tableName, fnm, regionStates);
366  }
367
368  @Ignore @Test
369  public void testAllFavoredNodesDead() throws Exception {
370
371    TableName tableName = TableName.valueOf("testAllFavoredNodesDead");
372    HTableDescriptor desc = new HTableDescriptor(tableName);
373    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
374    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
375    TEST_UTIL.waitTableAvailable(tableName);
376
377    final RegionInfo region = admin.getTableRegions(tableName).get(0);
378    LOG.info("Region that's supposed to be in transition: " + region);
379    FavoredNodesManager fnm = master.getFavoredNodesManager();
380    List<ServerName> currentFN = fnm.getFavoredNodes(region);
381    assertNotNull(currentFN);
382
383    // Lets kill all the RS that are favored nodes for this region.
384    stopServersAndWaitUntilProcessed(currentFN);
385
386    final RegionStates regionStates = master.getAssignmentManager().getRegionStates();
387    TEST_UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
388      @Override
389      public boolean evaluate() throws Exception {
390        return regionStates.getRegionState(region).isFailedOpen();
391      }
392    });
393
394    assertTrue("Region: " + region + " should be RIT",
395        regionStates.getRegionState(region).isFailedOpen());
396
397    // Regenerate FN and assign, everything else should be fine
398    List<ServerName> serversForNewFN = Lists.newArrayList();
399    for (ServerName sn : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
400      .getLiveServerMetrics().keySet()) {
401      serversForNewFN.add(ServerName.valueOf(sn.getHostname(), sn.getPort(), NON_STARTCODE));
402    }
403
404    FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(serversForNewFN, conf);
405    helper.initialize();
406
407    for (RegionStateNode regionState: regionStates.getRegionsInTransition()) {
408      RegionInfo regionInfo = regionState.getRegionInfo();
409      List<ServerName> newFavoredNodes = helper.generateFavoredNodes(regionInfo);
410      assertNotNull(newFavoredNodes);
411      assertEquals(FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, newFavoredNodes.size());
412      LOG.info("Region: " + regionInfo.getEncodedName() + " FN: " + newFavoredNodes);
413
414      Map<RegionInfo, List<ServerName>> regionFNMap = Maps.newHashMap();
415      regionFNMap.put(regionInfo, newFavoredNodes);
416      fnm.updateFavoredNodes(regionFNMap);
417      LOG.info("Assigning region: " + regionInfo.getEncodedName());
418      admin.assign(regionInfo.getEncodedNameAsBytes());
419    }
420    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
421    assertEquals("Not all regions are online", REGION_NUM, admin.getTableRegions(tableName).size());
422
423    admin.setBalancerRunning(true, true);
424    assertTrue("Balancer did not run", admin.balancer());
425    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
426
427    checkFavoredNodeAssignments(tableName, fnm, regionStates);
428  }
429
430  @Ignore @Test
431  public void testAllFavoredNodesDeadMasterRestarted() throws Exception {
432
433    TableName tableName = TableName.valueOf("testAllFavoredNodesDeadMasterRestarted");
434    HTableDescriptor desc = new HTableDescriptor(tableName);
435    desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
436    admin.createTable(desc, Bytes.toBytes("aaa"), Bytes.toBytes("zzz"), REGION_NUM);
437    TEST_UTIL.waitTableAvailable(tableName);
438
439    final RegionInfo region = admin.getTableRegions(tableName).get(0);
440    LOG.info("Region that's supposed to be in transition: " + region);
441    FavoredNodesManager fnm = master.getFavoredNodesManager();
442    List<ServerName> currentFN = fnm.getFavoredNodes(region);
443    assertNotNull(currentFN);
444
445    // Lets kill all the RS that are favored nodes for this region.
446    stopServersAndWaitUntilProcessed(currentFN);
447
448    final RegionStates regionStatesBeforeMaster =
449        master.getAssignmentManager().getRegionStates();
450    TEST_UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
451      @Override
452      public boolean evaluate() throws Exception {
453        return regionStatesBeforeMaster.getRegionState(region).isFailedOpen();
454      }
455    });
456
457    assertTrue("Region: " + region + " should be RIT",
458        regionStatesBeforeMaster.getRegionState(region).isFailedOpen());
459
460    List<RegionInfo> rit = Lists.newArrayList();
461    for (RegionStateNode regionState: regionStatesBeforeMaster.getRegionsInTransition()) {
462      RegionInfo regionInfo = regionState.getRegionInfo();
463      LOG.debug("Region in transition after stopping FN's: " + regionInfo);
464      rit.add(regionInfo);
465      assertTrue("Region: " + regionInfo + " should be RIT",
466          regionStatesBeforeMaster.getRegionState(regionInfo).isFailedOpen());
467      assertEquals("Region: " + regionInfo + " does not belong to table: " + tableName,
468          tableName, regionInfo.getTable());
469    }
470
471    Configuration conf = cluster.getConf();
472    conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
473        SLAVES - FavoredNodeAssignmentHelper.FAVORED_NODES_NUM);
474
475    cluster.stopMaster(master.getServerName());
476    cluster.waitForMasterToStop(master.getServerName(), 60000);
477
478    cluster.startMaster();
479    cluster.waitForActiveAndReadyMaster();
480    master = cluster.getMaster();
481    fnm = master.getFavoredNodesManager();
482
483    RegionStates regionStates = master.getAssignmentManager().getRegionStates();
484    assertTrue("Region: " + region + " should be RIT",
485        regionStates.getRegionState(region).isFailedOpen());
486
487    for (RegionInfo regionInfo : rit) {
488      assertTrue("Region: " + regionInfo + " should be RIT",
489          regionStates.getRegionState(regionInfo).isFailedOpen());
490    }
491
492    // Regenerate FN and assign, everything else should be fine
493    List<ServerName> serversForNewFN = Lists.newArrayList();
494    for (ServerName sn : admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS))
495      .getLiveServerMetrics().keySet()) {
496      serversForNewFN.add(ServerName.valueOf(sn.getHostname(), sn.getPort(), NON_STARTCODE));
497    }
498
499    FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(serversForNewFN, conf);
500    helper.initialize();
501
502    for (RegionInfo regionInfo : rit) {
503      List<ServerName> newFavoredNodes = helper.generateFavoredNodes(regionInfo);
504      assertNotNull(newFavoredNodes);
505      assertEquals(FavoredNodeAssignmentHelper.FAVORED_NODES_NUM, newFavoredNodes.size());
506      LOG.info("Region: " + regionInfo.getEncodedName() + " FN: " + newFavoredNodes);
507
508      Map<RegionInfo, List<ServerName>> regionFNMap = Maps.newHashMap();
509      regionFNMap.put(regionInfo, newFavoredNodes);
510      fnm.updateFavoredNodes(regionFNMap);
511      LOG.info("Assigning region: " + regionInfo.getEncodedName());
512      admin.assign(regionInfo.getEncodedNameAsBytes());
513    }
514    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
515    assertEquals("Not all regions are online", REGION_NUM, admin.getTableRegions(tableName).size());
516
517    admin.setBalancerRunning(true, true);
518    assertTrue("Balancer did not run", admin.balancer());
519    TEST_UTIL.waitUntilNoRegionsInTransition(60000);
520
521    checkFavoredNodeAssignments(tableName, fnm, regionStates);
522  }
523
524  private void checkFavoredNodeAssignments(TableName tableName, FavoredNodesManager fnm,
525      RegionStates regionStates) throws IOException {
526    for (RegionInfo hri : admin.getTableRegions(tableName)) {
527      ServerName host = regionStates.getRegionServerOfRegion(hri);
528      assertNotNull("Region: " + hri.getEncodedName() + " not on FN, current: " + host
529              + " FN list: " + fnm.getFavoredNodes(hri),
530          FavoredNodesPlan.getFavoredServerPosition(fnm.getFavoredNodes(hri), host));
531    }
532  }
533
534  private void stopServersAndWaitUntilProcessed(List<ServerName> currentFN) throws Exception {
535    for (ServerName sn : currentFN) {
536      for (JVMClusterUtil.RegionServerThread rst : cluster.getLiveRegionServerThreads()) {
537        if (ServerName.isSameAddress(sn, rst.getRegionServer().getServerName())) {
538          LOG.info("Shutting down server: " + sn);
539          cluster.stopRegionServer(rst.getRegionServer().getServerName());
540          cluster.waitForRegionServerToStop(rst.getRegionServer().getServerName(), 60000);
541        }
542      }
543    }
544
545    // Wait until dead servers are processed.
546    TEST_UTIL.waitFor(60000, new Waiter.Predicate<Exception>() {
547      @Override
548      public boolean evaluate() throws Exception {
549        return !master.getServerManager().areDeadServersInProgress();
550      }
551    });
552
553    assertEquals("Not all servers killed",
554        SLAVES - currentFN.size(), cluster.getLiveRegionServerThreads().size());
555  }
556
557  private void compactTable(TableName tableName) throws IOException {
558    for(JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) {
559      for(HRegion region : t.getRegionServer().getRegions(tableName)) {
560        region.compact(true);
561      }
562    }
563  }
564}