View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertNotNull;
24  import static org.junit.Assert.assertNull;
25  import static org.junit.Assert.assertTrue;
26  import static org.junit.Assert.fail;
27  
28  import java.io.IOException;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  import org.apache.hadoop.conf.Configuration;
37  import org.apache.hadoop.hbase.client.Admin;
38  import org.apache.hadoop.hbase.client.Get;
39  import org.apache.hadoop.hbase.client.HConnection;
40  import org.apache.hadoop.hbase.client.HConnectionManager;
41  import org.apache.hadoop.hbase.client.HTable;
42  import org.apache.hadoop.hbase.client.Put;
43  import org.apache.hadoop.hbase.client.Result;
44  import org.apache.hadoop.hbase.client.ResultScanner;
45  import org.apache.hadoop.hbase.client.Scan;
46  import org.apache.hadoop.hbase.client.Table;
47  import org.apache.hadoop.hbase.coordination.ZkSplitLogWorkerCoordination;
48  import org.apache.hadoop.hbase.master.HMaster;
49  import org.apache.hadoop.hbase.master.LoadBalancer;
50  import org.apache.hadoop.hbase.master.balancer.SimpleLoadBalancer;
51  import org.apache.hadoop.hbase.testclassification.LargeTests;
52  import org.apache.hadoop.hbase.util.Bytes;
53  import org.apache.hadoop.hbase.util.FSUtils;
54  import org.apache.hadoop.hbase.util.Threads;
55  import org.apache.hadoop.hbase.zookeeper.EmptyWatcher;
56  import org.apache.hadoop.hbase.zookeeper.ZKAssign;
57  import org.apache.hadoop.hbase.zookeeper.ZKConfig;
58  import org.apache.hadoop.hbase.zookeeper.ZKUtil;
59  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
60  import org.apache.zookeeper.CreateMode;
61  import org.apache.zookeeper.KeeperException;
62  import org.apache.zookeeper.ZooDefs;
63  import org.apache.zookeeper.ZooKeeper;
64  import org.apache.zookeeper.ZooKeeper.States;
65  import org.apache.zookeeper.data.ACL;
66  import org.apache.zookeeper.data.Stat;
67  import org.junit.After;
68  import org.junit.AfterClass;
69  import org.junit.Assert;
70  import org.junit.Before;
71  import org.junit.BeforeClass;
72  import org.junit.Ignore;
73  import org.junit.Test;
74  import org.junit.experimental.categories.Category;
75  
76  
77  
78  @Category(LargeTests.class)
79  public class TestZooKeeper {
80    private static final Log LOG = LogFactory.getLog(TestZooKeeper.class);
81  
82    private final static HBaseTestingUtility
83        TEST_UTIL = new HBaseTestingUtility();
84  
85    /**
86     * @throws java.lang.Exception
87     */
88    @BeforeClass
89    public static void setUpBeforeClass() throws Exception {
90      // Test we can first start the ZK cluster by itself
91      Configuration conf = TEST_UTIL.getConfiguration();
92      TEST_UTIL.startMiniDFSCluster(2);
93      TEST_UTIL.startMiniZKCluster();
94      conf.setBoolean("dfs.support.append", true);
95      conf.setInt(HConstants.ZK_SESSION_TIMEOUT, 1000);
96      conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, MockLoadBalancer.class,
97          LoadBalancer.class);
98    }
99  
100   /**
101    * @throws java.lang.Exception
102    */
103   @AfterClass
104   public static void tearDownAfterClass() throws Exception {
105     TEST_UTIL.shutdownMiniCluster();
106   }
107 
108   /**
109    * @throws java.lang.Exception
110    */
111   @Before
112   public void setUp() throws Exception {
113     TEST_UTIL.startMiniHBaseCluster(2, 2);
114   }
115 
116   @After
117   public void after() throws Exception {
118     try {
119       // Some regionserver could fail to delete its znode.
120       // So shutdown could hang. Let's kill them all instead.
121       TEST_UTIL.getHBaseCluster().killAll();
122 
123       // Still need to clean things up
124       TEST_UTIL.shutdownMiniHBaseCluster();
125     } finally {
126       TEST_UTIL.getTestFileSystem().delete(FSUtils.getRootDir(TEST_UTIL.getConfiguration()), true);
127       ZKUtil.deleteNodeRecursively(TEST_UTIL.getZooKeeperWatcher(), "/hbase");
128     }
129   }
130 
131   private ZooKeeperWatcher getZooKeeperWatcher(HConnection c)
132   throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
133     Method getterZK = c.getClass().getDeclaredMethod("getKeepAliveZooKeeperWatcher");
134     getterZK.setAccessible(true);
135     return (ZooKeeperWatcher) getterZK.invoke(c);
136   }
137 
138 
139   /**
140    * See HBASE-1232 and http://wiki.apache.org/hadoop/ZooKeeper/FAQ#4.
141    * @throws IOException
142    * @throws InterruptedException
143    */
144   // fails frequently, disabled for now, see HBASE-6406
145   @Ignore
146   @Test
147   public void testClientSessionExpired() throws Exception {
148     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
149 
150     // We don't want to share the connection as we will check its state
151     c.set(HConstants.HBASE_CLIENT_INSTANCE_ID, "1111");
152 
153     HConnection connection = HConnectionManager.getConnection(c);
154 
155     ZooKeeperWatcher connectionZK = getZooKeeperWatcher(connection);
156     LOG.info("ZooKeeperWatcher= 0x"+ Integer.toHexString(
157       connectionZK.hashCode()));
158     LOG.info("getRecoverableZooKeeper= 0x"+ Integer.toHexString(
159       connectionZK.getRecoverableZooKeeper().hashCode()));
160     LOG.info("session="+Long.toHexString(
161       connectionZK.getRecoverableZooKeeper().getSessionId()));
162 
163     TEST_UTIL.expireSession(connectionZK);
164 
165     LOG.info("Before using zkw state=" +
166       connectionZK.getRecoverableZooKeeper().getState());
167     // provoke session expiration by doing something with ZK
168     try {
169       connectionZK.getRecoverableZooKeeper().getZooKeeper().exists(
170         "/1/1", false);
171     } catch (KeeperException ignored) {
172     }
173 
174     // Check that the old ZK connection is closed, means we did expire
175     States state = connectionZK.getRecoverableZooKeeper().getState();
176     LOG.info("After using zkw state=" + state);
177     LOG.info("session="+Long.toHexString(
178       connectionZK.getRecoverableZooKeeper().getSessionId()));
179 
180     // It's asynchronous, so we may have to wait a little...
181     final long limit1 = System.currentTimeMillis() + 3000;
182     while (System.currentTimeMillis() < limit1 && state != States.CLOSED){
183       state = connectionZK.getRecoverableZooKeeper().getState();
184     }
185     LOG.info("After using zkw loop=" + state);
186     LOG.info("ZooKeeper should have timed out");
187     LOG.info("session="+Long.toHexString(
188       connectionZK.getRecoverableZooKeeper().getSessionId()));
189 
190     // It's surprising but sometimes we can still be in connected state.
191     // As it's known (even if not understood) we don't make the the test fail
192     // for this reason.)
193     // Assert.assertTrue("state=" + state, state == States.CLOSED);
194 
195     // Check that the client recovered
196     ZooKeeperWatcher newConnectionZK = getZooKeeperWatcher(connection);
197 
198     States state2 = newConnectionZK.getRecoverableZooKeeper().getState();
199     LOG.info("After new get state=" +state2);
200 
201     // As it's an asynchronous event we may got the same ZKW, if it's not
202     //  yet invalidated. Hence this loop.
203     final long limit2 = System.currentTimeMillis() + 3000;
204     while (System.currentTimeMillis() < limit2 &&
205       state2 != States.CONNECTED && state2 != States.CONNECTING) {
206 
207       newConnectionZK = getZooKeeperWatcher(connection);
208       state2 = newConnectionZK.getRecoverableZooKeeper().getState();
209     }
210     LOG.info("After new get state loop=" + state2);
211 
212     Assert.assertTrue(
213       state2 == States.CONNECTED || state2 == States.CONNECTING);
214 
215     connection.close();
216   }
217 
218   @Test (timeout = 120000)
219   public void testRegionServerSessionExpired() throws Exception {
220     LOG.info("Starting testRegionServerSessionExpired");
221     TEST_UTIL.expireRegionServerSession(0);
222     testSanity("testRegionServerSessionExpired");
223   }
224 
225   @Test(timeout = 300000)
226   public void testMasterSessionExpired() throws Exception {
227     LOG.info("Starting testMasterSessionExpired");
228     TEST_UTIL.expireMasterSession();
229     testSanity("testMasterSessionExpired");
230   }
231 
232   /**
233    * Master recovery when the znode already exists. Internally, this
234    *  test differs from {@link #testMasterSessionExpired} because here
235    *  the master znode will exist in ZK.
236    */
237   @Test(timeout = 300000)
238   public void testMasterZKSessionRecoveryFailure() throws Exception {
239     LOG.info("Starting testMasterZKSessionRecoveryFailure");
240     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
241     HMaster m = cluster.getMaster();
242     m.abort("Test recovery from zk session expired",
243         new KeeperException.SessionExpiredException());
244     assertTrue(m.isStopped()); // Master doesn't recover any more
245     testSanity("testMasterZKSessionRecoveryFailure");
246   }
247 
248   /**
249    * Make sure we can use the cluster
250    * @throws Exception
251    */
252   private void testSanity(final String testName) throws Exception{
253     String tableName = testName + "_" + System.currentTimeMillis();
254     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
255     HColumnDescriptor family = new HColumnDescriptor("fam");
256     desc.addFamily(family);
257     LOG.info("Creating table " + tableName);
258     Admin admin = TEST_UTIL.getHBaseAdmin();
259     try {
260       admin.createTable(desc);
261     } finally {
262       admin.close();
263     }
264 
265     Table table =
266       new HTable(new Configuration(TEST_UTIL.getConfiguration()), desc.getTableName());
267     Put put = new Put(Bytes.toBytes("testrow"));
268     put.add(Bytes.toBytes("fam"),
269         Bytes.toBytes("col"), Bytes.toBytes("testdata"));
270     LOG.info("Putting table " + tableName);
271     table.put(put);
272     table.close();
273   }
274 
275   @Test
276   public void testMultipleZK()
277   throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
278     Table localMeta =
279       new HTable(new Configuration(TEST_UTIL.getConfiguration()), TableName.META_TABLE_NAME);
280     Configuration otherConf = new Configuration(TEST_UTIL.getConfiguration());
281     otherConf.set(HConstants.ZOOKEEPER_QUORUM, "127.0.0.1");
282     Table ipMeta = new HTable(otherConf, TableName.META_TABLE_NAME);
283 
284     // dummy, just to open the connection
285     final byte [] row = new byte [] {'r'};
286     localMeta.exists(new Get(row));
287     ipMeta.exists(new Get(row));
288 
289     // make sure they aren't the same
290     ZooKeeperWatcher z1 =
291       getZooKeeperWatcher(HConnectionManager.getConnection(localMeta.getConfiguration()));
292     ZooKeeperWatcher z2 =
293       getZooKeeperWatcher(HConnectionManager.getConnection(otherConf));
294     assertFalse(z1 == z2);
295     assertFalse(z1.getQuorum().equals(z2.getQuorum()));
296 
297     localMeta.close();
298     ipMeta.close();
299   }
300 
301   /**
302    * Create a znode with data
303    * @throws Exception
304    */
305   @Test
306   public void testCreateWithParents() throws Exception {
307     ZooKeeperWatcher zkw =
308         new ZooKeeperWatcher(new Configuration(TEST_UTIL.getConfiguration()),
309             TestZooKeeper.class.getName(), null);
310     byte[] expectedData = new byte[] { 1, 2, 3 };
311     ZKUtil.createWithParents(zkw, "/l1/l2/l3/l4/testCreateWithParents", expectedData);
312     byte[] data = ZKUtil.getData(zkw, "/l1/l2/l3/l4/testCreateWithParents");
313     assertTrue(Bytes.equals(expectedData, data));
314     ZKUtil.deleteNodeRecursively(zkw, "/l1");
315 
316     ZKUtil.createWithParents(zkw, "/testCreateWithParents", expectedData);
317     data = ZKUtil.getData(zkw, "/testCreateWithParents");
318     assertTrue(Bytes.equals(expectedData, data));
319     ZKUtil.deleteNodeRecursively(zkw, "/testCreateWithParents");
320   }
321 
322   /**
323    * Create a bunch of znodes in a hierarchy, try deleting one that has childs (it will fail), then
324    * delete it recursively, then delete the last znode
325    * @throws Exception
326    */
327   @Test
328   public void testZNodeDeletes() throws Exception {
329     ZooKeeperWatcher zkw = new ZooKeeperWatcher(
330       new Configuration(TEST_UTIL.getConfiguration()),
331       TestZooKeeper.class.getName(), null);
332     ZKUtil.createWithParents(zkw, "/l1/l2/l3/l4");
333     try {
334       ZKUtil.deleteNode(zkw, "/l1/l2");
335       fail("We should not be able to delete if znode has childs");
336     } catch (KeeperException ex) {
337       assertNotNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null));
338     }
339     ZKUtil.deleteNodeRecursively(zkw, "/l1/l2");
340     // make sure it really is deleted
341     assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2/l3/l4", null));
342 
343     // do the same delete again and make sure it doesn't crash
344     ZKUtil.deleteNodeRecursively(zkw, "/l1/l2");
345 
346     ZKUtil.deleteNode(zkw, "/l1");
347     assertNull(ZKUtil.getDataNoWatch(zkw, "/l1/l2", null));
348   }
349 
350   /**
351    * A test for HBASE-3238
352    * @throws IOException A connection attempt to zk failed
353    * @throws InterruptedException One of the non ZKUtil actions was interrupted
354    * @throws KeeperException Any of the zookeeper connections had a
355    * KeeperException
356    */
357   @Test
358   public void testCreateSilentIsReallySilent() throws InterruptedException,
359       KeeperException, IOException {
360     Configuration c = TEST_UTIL.getConfiguration();
361 
362     String aclZnode = "/aclRoot";
363     String quorumServers = ZKConfig.getZKQuorumServersString(c);
364     int sessionTimeout = 5 * 1000; // 5 seconds
365     ZooKeeper zk = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance);
366     zk.addAuthInfo("digest", "hbase:rox".getBytes());
367 
368     // Assumes the  root of the ZooKeeper space is writable as it creates a node
369     // wherever the cluster home is defined.
370     ZooKeeperWatcher zk2 = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(),
371       "testCreateSilentIsReallySilent", null);
372 
373     // Save the previous ACL
374     Stat s =  null;
375     List<ACL> oldACL = null;
376     while (true) {
377       try {
378         s = new Stat();
379         oldACL = zk.getACL("/", s);
380         break;
381       } catch (KeeperException e) {
382         switch (e.code()) {
383           case CONNECTIONLOSS:
384           case SESSIONEXPIRED:
385           case OPERATIONTIMEOUT:
386             LOG.warn("Possibly transient ZooKeeper exception", e);
387             Threads.sleep(100);
388             break;
389          default:
390             throw e;
391         }
392       }
393     }
394 
395     // I set this acl after the attempted creation of the cluster home node.
396     // Add retries in case of retryable zk exceptions.
397     while (true) {
398       try {
399         zk.setACL("/", ZooDefs.Ids.CREATOR_ALL_ACL, -1);
400         break;
401       } catch (KeeperException e) {
402         switch (e.code()) {
403           case CONNECTIONLOSS:
404           case SESSIONEXPIRED:
405           case OPERATIONTIMEOUT:
406             LOG.warn("Possibly transient ZooKeeper exception: " + e);
407             Threads.sleep(100);
408             break;
409          default:
410             throw e;
411         }
412       }
413     }
414 
415     while (true) {
416       try {
417         zk.create(aclZnode, null, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);
418         break;
419       } catch (KeeperException e) {
420         switch (e.code()) {
421           case CONNECTIONLOSS:
422           case SESSIONEXPIRED:
423           case OPERATIONTIMEOUT:
424             LOG.warn("Possibly transient ZooKeeper exception: " + e);
425             Threads.sleep(100);
426             break;
427          default:
428             throw e;
429         }
430       }
431     }
432     zk.close();
433     ZKUtil.createAndFailSilent(zk2, aclZnode);
434 
435     // Restore the ACL
436     ZooKeeper zk3 = new ZooKeeper(quorumServers, sessionTimeout, EmptyWatcher.instance);
437     zk3.addAuthInfo("digest", "hbase:rox".getBytes());
438     try {
439       zk3.setACL("/", oldACL, -1);
440     } finally {
441       zk3.close();
442     }
443  }
444 
445   /**
446    * Test should not fail with NPE when getChildDataAndWatchForNewChildren
447    * invoked with wrongNode
448    */
449   @Test
450   @SuppressWarnings("deprecation")
451   public void testGetChildDataAndWatchForNewChildrenShouldNotThrowNPE()
452       throws Exception {
453     ZooKeeperWatcher zkw = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(),
454         "testGetChildDataAndWatchForNewChildrenShouldNotThrowNPE", null);
455     ZKUtil.getChildDataAndWatchForNewChildren(zkw, "/wrongNode");
456   }
457 
458   /**
459    * Tests that the master does not call retainAssignment after recovery from expired zookeeper
460    * session. Without the HBASE-6046 fix master always tries to assign all the user regions by
461    * calling retainAssignment.
462    */
463   @Test(timeout = 300000)
464   public void testRegionAssignmentAfterMasterRecoveryDueToZKExpiry() throws Exception {
465     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
466     cluster.startRegionServer();
467     cluster.waitForActiveAndReadyMaster(10000);
468     HMaster m = cluster.getMaster();
469     final ZooKeeperWatcher zkw = m.getZooKeeper();
470     // now the cluster is up. So assign some regions.
471     try (Admin admin = TEST_UTIL.getHBaseAdmin()) {
472       byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"),
473           Bytes.toBytes("c"), Bytes.toBytes("d"), Bytes.toBytes("e"), Bytes.toBytes("f"),
474           Bytes.toBytes("g"), Bytes.toBytes("h"), Bytes.toBytes("i"), Bytes.toBytes("j") };
475       String tableName = "testRegionAssignmentAfterMasterRecoveryDueToZKExpiry";
476       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
477       htd.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY));
478       admin.createTable(htd, SPLIT_KEYS);
479       TEST_UTIL.waitUntilNoRegionsInTransition(60000);
480       m.getZooKeeper().close();
481       MockLoadBalancer.retainAssignCalled = false;
482       final int expectedNumOfListeners = countPermanentListeners(zkw);
483       m.abort("Test recovery from zk session expired",
484           new KeeperException.SessionExpiredException());
485       assertTrue(m.isStopped()); // Master doesn't recover any more
486       // The recovered master should not call retainAssignment, as it is not a
487       // clean startup.
488       assertFalse("Retain assignment should not be called", MockLoadBalancer.retainAssignCalled);
489       // number of listeners should be same as the value before master aborted
490       // wait for new master is initialized
491       cluster.waitForActiveAndReadyMaster(120000);
492       final HMaster newMaster = cluster.getMasterThread().getMaster();
493       assertEquals(expectedNumOfListeners, countPermanentListeners(newMaster.getZooKeeper()));
494     }
495   }
496 
497   /**
498    * Count listeners in zkw excluding listeners, that belongs to workers or other
499    * temporary processes.
500    */
501   private int countPermanentListeners(ZooKeeperWatcher watcher) {
502     return countListeners(watcher, ZkSplitLogWorkerCoordination.class);
503   }
504 
505   /**
506    * Count listeners in zkw excluding provided classes
507    */
508   private int countListeners(ZooKeeperWatcher watcher, Class<?>... exclude) {
509     int cnt = 0;
510     for (Object o : watcher.getListeners()) {
511       boolean skip = false;
512       for (Class<?> aClass : exclude) {
513         if (aClass.isAssignableFrom(o.getClass())) {
514           skip = true;
515           break;
516         }
517       }
518       if (!skip) {
519         cnt += 1;
520       }
521     }
522     return cnt;
523   }
524 
525 
526   /**
527    * Tests whether the logs are split when master recovers from a expired zookeeper session and an
528    * RS goes down.
529    */
530   @Test(timeout = 300000)
531   public void testLogSplittingAfterMasterRecoveryDueToZKExpiry() throws IOException,
532       KeeperException, InterruptedException {
533     MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
534     cluster.startRegionServer();
535     HMaster m = cluster.getMaster();
536     // now the cluster is up. So assign some regions.
537     Admin admin = TEST_UTIL.getHBaseAdmin();
538     Table table = null;
539     try {
540       byte[][] SPLIT_KEYS = new byte[][] { Bytes.toBytes("1"), Bytes.toBytes("2"),
541         Bytes.toBytes("3"), Bytes.toBytes("4"), Bytes.toBytes("5") };
542 
543       String tableName = "testLogSplittingAfterMasterRecoveryDueToZKExpiry";
544       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
545       HColumnDescriptor hcd = new HColumnDescriptor("col");
546       htd.addFamily(hcd);
547       admin.createTable(htd, SPLIT_KEYS);
548       ZooKeeperWatcher zooKeeperWatcher = HBaseTestingUtility.getZooKeeperWatcher(TEST_UTIL);
549       ZKAssign.blockUntilNoRIT(zooKeeperWatcher);
550       table = new HTable(TEST_UTIL.getConfiguration(), htd.getTableName());
551       Put p;
552       int numberOfPuts;
553       for (numberOfPuts = 0; numberOfPuts < 6; numberOfPuts++) {
554         p = new Put(Bytes.toBytes(numberOfPuts));
555         p.add(Bytes.toBytes("col"), Bytes.toBytes("ql"), Bytes.toBytes("value" + numberOfPuts));
556         table.put(p);
557       }
558       m.getZooKeeper().close();
559       m.abort("Test recovery from zk session expired",
560         new KeeperException.SessionExpiredException());
561       assertTrue(m.isStopped()); // Master doesn't recover any more
562       cluster.getRegionServer(0).abort("Aborting");
563       // Without patch for HBASE-6046 this test case will always timeout
564       // with patch the test case should pass.
565       Scan scan = new Scan();
566       int numberOfRows = 0;
567       ResultScanner scanner = table.getScanner(scan);
568       Result[] result = scanner.next(1);
569       while (result != null && result.length > 0) {
570         numberOfRows++;
571         result = scanner.next(1);
572       }
573       assertEquals("Number of rows should be equal to number of puts.", numberOfPuts,
574         numberOfRows);
575     } finally {
576       if (table != null) table.close();
577       admin.close();
578     }
579   }
580 
581   static class MockLoadBalancer extends SimpleLoadBalancer {
582     static boolean retainAssignCalled = false;
583 
584     @Override
585     public Map<ServerName, List<HRegionInfo>> retainAssignment(
586         Map<HRegionInfo, ServerName> regions, List<ServerName> servers) {
587       retainAssignCalled = true;
588       return super.retainAssignment(regions, servers);
589     }
590   }
591 
592 }
593