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.client;
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  import com.google.common.collect.Lists;
28  
29  import java.io.IOException;
30  import java.lang.reflect.Field;
31  import java.lang.reflect.Modifier;
32  import java.net.SocketTimeoutException;
33  import java.util.ArrayList;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Random;
38  import java.util.concurrent.ExecutorService;
39  import java.util.concurrent.SynchronousQueue;
40  import java.util.concurrent.ThreadPoolExecutor;
41  import java.util.concurrent.TimeUnit;
42  import java.util.concurrent.atomic.AtomicBoolean;
43  import java.util.concurrent.atomic.AtomicInteger;
44  import java.util.concurrent.atomic.AtomicLong;
45  import java.util.concurrent.atomic.AtomicReference;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  import org.apache.hadoop.conf.Configuration;
50  import org.apache.hadoop.hbase.CategoryBasedTimeout;
51  import org.apache.hadoop.hbase.Cell;
52  import org.apache.hadoop.hbase.HBaseConfiguration;
53  import org.apache.hadoop.hbase.HBaseTestingUtility;
54  import org.apache.hadoop.hbase.HConstants;
55  import org.apache.hadoop.hbase.HRegionLocation;
56  import org.apache.hadoop.hbase.HTableDescriptor;
57  import org.apache.hadoop.hbase.RegionLocations;
58  import org.apache.hadoop.hbase.ServerName;
59  import org.apache.hadoop.hbase.TableName;
60  import org.apache.hadoop.hbase.Waiter;
61  import org.apache.hadoop.hbase.client.ConnectionManager.HConnectionImplementation;
62  import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
63  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
64  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
65  import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
66  import org.apache.hadoop.hbase.exceptions.DeserializationException;
67  import org.apache.hadoop.hbase.exceptions.RegionMovedException;
68  import org.apache.hadoop.hbase.filter.Filter;
69  import org.apache.hadoop.hbase.filter.FilterBase;
70  import org.apache.hadoop.hbase.ipc.CallTimeoutException;
71  import org.apache.hadoop.hbase.ipc.RpcClient;
72  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
73  import org.apache.hadoop.hbase.master.HMaster;
74  import org.apache.hadoop.hbase.regionserver.HRegionServer;
75  import org.apache.hadoop.hbase.regionserver.Region;
76  import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
77  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
78  import org.apache.hadoop.hbase.testclassification.LargeTests;
79  import org.apache.hadoop.hbase.util.Bytes;
80  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
81  import org.apache.hadoop.hbase.util.JVMClusterUtil;
82  import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
83  import org.apache.hadoop.hbase.util.Threads;
84  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
85  import org.junit.AfterClass;
86  import org.junit.Assert;
87  import org.junit.BeforeClass;
88  import org.junit.Ignore;
89  import org.junit.Rule;
90  import org.junit.Test;
91  import org.junit.experimental.categories.Category;
92  import org.junit.rules.TestRule;
93  
94  import static org.junit.Assert.assertEquals;
95  import static org.junit.Assert.assertFalse;
96  import static org.junit.Assert.assertNotNull;
97  import static org.junit.Assert.assertNull;
98  import static org.junit.Assert.assertTrue;
99  import static org.junit.Assert.fail;
100 
101 /**
102  * This class is for testing HBaseConnectionManager features
103  */
104 @Category({LargeTests.class})
105 public class TestHCM {
106   @Rule public final TestRule timeout = CategoryBasedTimeout.builder()
107       .withTimeout(this.getClass())
108       .withLookingForStuckThread(true)
109       .build();
110   private static final Log LOG = LogFactory.getLog(TestHCM.class);
111   private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
112   private static final TableName TABLE_NAME =
113       TableName.valueOf("test");
114   private static final TableName TABLE_NAME1 =
115       TableName.valueOf("test1");
116   private static final TableName TABLE_NAME2 =
117       TableName.valueOf("test2");
118   private static final TableName TABLE_NAME3 =
119       TableName.valueOf("test3");
120   private static final TableName TABLE_NAME4 =
121       TableName.valueOf("test4");
122   private static final byte[] FAM_NAM = Bytes.toBytes("f");
123   private static final byte[] ROW = Bytes.toBytes("bbb");
124   private static final byte[] ROW_X = Bytes.toBytes("xxx");
125   private static Random _randy = new Random();
126   private static final int RPC_RETRY = 5;
127 
128 /**
129 * This copro sleeps 20 second. The first call it fails. The second time, it works.
130 */
131   public static class SleepAndFailFirstTime extends BaseRegionObserver {
132   static final AtomicLong ct = new AtomicLong(0);
133   static final String SLEEP_TIME_CONF_KEY =
134       "hbase.coprocessor.SleepAndFailFirstTime.sleepTime";
135   static final long DEFAULT_SLEEP_TIME = 20000;
136   static final AtomicLong sleepTime = new AtomicLong(DEFAULT_SLEEP_TIME);
137 
138   public SleepAndFailFirstTime() {
139   }
140 
141     @Override
142     public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
143       RegionCoprocessorEnvironment env = c.getEnvironment();
144       Configuration conf = env.getConfiguration();
145       sleepTime.set(conf.getLong(SLEEP_TIME_CONF_KEY, DEFAULT_SLEEP_TIME));
146     }
147 
148     @Override
149     public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> e,
150         final Get get, final List<Cell> results) throws IOException {
151       Threads.sleep(sleepTime.get());
152       if (ct.incrementAndGet() == 1) {
153         throw new IOException("first call I fail");
154       }
155     }
156 
157     @Override
158     public void prePut(final ObserverContext<RegionCoprocessorEnvironment> e,
159         final Put put, final WALEdit edit, final Durability durability) throws IOException {
160       Threads.sleep(sleepTime.get());
161       if (ct.incrementAndGet() == 1) {
162         throw new IOException("first call I fail");
163       }
164     }
165 
166     @Override
167     public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> e,
168         final Delete delete,
169         final WALEdit edit, final Durability durability) throws IOException {
170       Threads.sleep(sleepTime.get());
171       if (ct.incrementAndGet() == 1) {
172         throw new IOException("first call I fail");
173       }
174     }
175 
176     @Override
177     public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> e,
178         final Increment increment) throws IOException {
179       Threads.sleep(sleepTime.get());
180       if (ct.incrementAndGet() == 1) {
181         throw new IOException("first call I fail");
182       }
183       return super.preIncrement(e, increment);
184     }
185 
186   }
187 
188   public static class SleepCoprocessor extends BaseRegionObserver {
189     public static final int SLEEP_TIME = 5000;
190     @Override
191     public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> e,
192         final Get get, final List<Cell> results) throws IOException {
193       Threads.sleep(SLEEP_TIME);
194     }
195 
196     @Override
197     public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> e,
198         final Increment increment) throws IOException {
199       Threads.sleep(SLEEP_TIME);
200       return super.preIncrement(e, increment);
201     }
202 
203     @Override
204     public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> e, final Delete delete,
205         final WALEdit edit, final Durability durability) throws IOException {
206       Threads.sleep(SLEEP_TIME);
207     }
208 
209     @Override
210     public void prePut(final ObserverContext<RegionCoprocessorEnvironment> e,
211         final Put put, final WALEdit edit, final Durability durability) throws IOException {
212       Threads.sleep(SLEEP_TIME);
213     }
214 
215   }
216 
217   public static class SleepLongerAtFirstCoprocessor extends BaseRegionObserver {
218     public static final int SLEEP_TIME = 2000;
219     static final AtomicLong ct = new AtomicLong(0);
220 
221     @Override
222     public void preGetOp(final ObserverContext<RegionCoprocessorEnvironment> e,
223         final Get get, final List<Cell> results) throws IOException {
224       // After first sleep, all requests are timeout except the last retry. If we handle
225       // all the following requests, finally the last request is also timeout. If we drop all
226       // timeout requests, we can handle the last request immediately and it will not timeout.
227       if (ct.incrementAndGet() <= 1) {
228         Threads.sleep(SLEEP_TIME * (RPC_RETRY-1) * 2);
229       } else {
230         Threads.sleep(SLEEP_TIME);
231       }
232     }
233   }
234 
235   @BeforeClass
236   public static void setUpBeforeClass() throws Exception {
237     TEST_UTIL.getConfiguration().setBoolean(HConstants.STATUS_PUBLISHED, true);
238     // Up the handlers; this test needs more than usual.
239     TEST_UTIL.getConfiguration().setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10);
240     TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, RPC_RETRY);
241     // simulate queue blocking in testDropTimeoutRequest
242     TEST_UTIL.getConfiguration().setInt(HConstants.REGION_SERVER_HANDLER_COUNT, 1);
243     TEST_UTIL.startMiniCluster(2);
244   }
245 
246   @AfterClass public static void tearDownAfterClass() throws Exception {
247     TEST_UTIL.shutdownMiniCluster();
248   }
249 
250 
251   private static int getHConnectionManagerCacheSize(){
252     return HConnectionTestingUtility.getConnectionCount();
253   }
254 
255   @Test
256   public void testClusterConnection() throws IOException {
257     ThreadPoolExecutor otherPool = new ThreadPoolExecutor(1, 1,
258         5, TimeUnit.SECONDS,
259         new SynchronousQueue<Runnable>(),
260         Threads.newDaemonThreadFactory("test-hcm"));
261 
262     HConnection con1 = HConnectionManager.createConnection(TEST_UTIL.getConfiguration());
263     HConnection con2 = HConnectionManager.createConnection(TEST_UTIL.getConfiguration(), otherPool);
264     // make sure the internally created ExecutorService is the one passed
265     assertTrue(otherPool == ((HConnectionImplementation)con2).getCurrentBatchPool());
266 
267     String tableName = "testClusterConnection";
268     TEST_UTIL.createTable(tableName.getBytes(), FAM_NAM).close();
269     HTable t = (HTable)con1.getTable(tableName, otherPool);
270     // make sure passing a pool to the getTable does not trigger creation of an internal pool
271     assertNull("Internal Thread pool should be null", ((HConnectionImplementation)con1).getCurrentBatchPool());
272     // table should use the pool passed
273     assertTrue(otherPool == t.getPool());
274     t.close();
275 
276     t = (HTable)con2.getTable(tableName);
277     // table should use the connectin's internal pool
278     assertTrue(otherPool == t.getPool());
279     t.close();
280 
281     t = (HTable)con2.getTable(Bytes.toBytes(tableName));
282     // try other API too
283     assertTrue(otherPool == t.getPool());
284     t.close();
285 
286     t = (HTable)con2.getTable(TableName.valueOf(tableName));
287     // try other API too
288     assertTrue(otherPool == t.getPool());
289     t.close();
290 
291     t = (HTable)con1.getTable(tableName);
292     ExecutorService pool = ((HConnectionImplementation)con1).getCurrentBatchPool();
293     // make sure an internal pool was created
294     assertNotNull("An internal Thread pool should have been created", pool);
295     // and that the table is using it
296     assertTrue(t.getPool() == pool);
297     t.close();
298 
299     t = (HTable)con1.getTable(tableName);
300     // still using the *same* internal pool
301     assertTrue(t.getPool() == pool);
302     t.close();
303 
304     con1.close();
305     // if the pool was created on demand it should be closed upon connection close
306     assertTrue(pool.isShutdown());
307 
308     con2.close();
309     // if the pool is passed, it is not closed
310     assertFalse(otherPool.isShutdown());
311     otherPool.shutdownNow();
312   }
313 
314   /**
315    * Naive test to check that HConnection#getAdmin returns a properly constructed HBaseAdmin object
316    * @throws IOException Unable to construct admin
317    */
318   @Test
319   public void testAdminFactory() throws IOException {
320     Connection con1 = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
321     Admin admin = con1.getAdmin();
322     assertTrue(admin.getConnection() == con1);
323     assertTrue(admin.getConfiguration() == TEST_UTIL.getConfiguration());
324     con1.close();
325   }
326 
327   // Fails too often!  Needs work.  HBASE-12558
328   @Ignore @Test (expected = RegionServerStoppedException.class)
329   public void testClusterStatus() throws Exception {
330 
331     TableName tn =
332         TableName.valueOf("testClusterStatus");
333     byte[] cf = "cf".getBytes();
334     byte[] rk = "rk1".getBytes();
335 
336     JVMClusterUtil.RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer();
337     rs.waitForServerOnline();
338     final ServerName sn = rs.getRegionServer().getServerName();
339 
340     HTable t = TEST_UTIL.createTable(tn, cf);
341     TEST_UTIL.waitTableAvailable(tn);
342     TEST_UTIL.waitUntilNoRegionsInTransition();
343 
344     final HConnectionImplementation hci =  (HConnectionImplementation)t.getConnection();
345     while (t.getRegionLocation(rk).getPort() != sn.getPort()){
346       TEST_UTIL.getHBaseAdmin().move(t.getRegionLocation(rk).getRegionInfo().
347           getEncodedNameAsBytes(), Bytes.toBytes(sn.toString()));
348       TEST_UTIL.waitUntilNoRegionsInTransition();
349       hci.clearRegionCache(tn);
350     }
351     Assert.assertNotNull(hci.clusterStatusListener);
352     TEST_UTIL.assertRegionOnServer(t.getRegionLocation(rk).getRegionInfo(), sn, 20000);
353 
354     Put p1 = new Put(rk);
355     p1.add(cf, "qual".getBytes(), "val".getBytes());
356     t.put(p1);
357 
358     rs.getRegionServer().abort("I'm dead");
359 
360     // We want the status to be updated. That's a least 10 second
361     TEST_UTIL.waitFor(40000, 1000, true, new Waiter.Predicate<Exception>() {
362       @Override
363       public boolean evaluate() throws Exception {
364         return TEST_UTIL.getHBaseCluster().getMaster().getServerManager().
365             getDeadServers().isDeadServer(sn);
366       }
367     });
368 
369     TEST_UTIL.waitFor(40000, 1000, true, new Waiter.Predicate<Exception>() {
370       @Override
371       public boolean evaluate() throws Exception {
372         return hci.clusterStatusListener.isDeadServer(sn);
373       }
374     });
375 
376     t.close();
377     hci.getClient(sn);  // will throw an exception: RegionServerStoppedException
378   }
379 
380   /**
381    * Test that we can handle connection close: it will trigger a retry, but the calls will
382    *  finish.
383    */
384   @Test
385   public void testConnectionCloseAllowsInterrupt() throws Exception {
386     testConnectionClose(true);
387   }
388 
389   @Test
390   public void testConnectionNotAllowsInterrupt() throws Exception {
391     testConnectionClose(false);
392   }
393 
394   /**
395    * Test that an operation can fail if we read the global operation timeout, even if the
396    * individual timeout is fine. We do that with:
397    * - client side: an operation timeout of 30 seconds
398    * - server side: we sleep 20 second at each attempt. The first work fails, the second one
399    * succeeds. But the client won't wait that much, because 20 + 20 > 30, so the client
400    * timeouted when the server answers.
401    */
402   @Test
403   public void testGetOperationTimeout() throws Exception {
404     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testGetOperationTimeout");
405     hdt.addCoprocessor(SleepAndFailFirstTime.class.getName());
406     Table table = TEST_UTIL.createTable(hdt, new byte[][]{FAM_NAM}, TEST_UTIL.getConfiguration());
407     table.setRpcTimeout(Integer.MAX_VALUE);
408     SleepAndFailFirstTime.ct.set(0);
409     // Check that it works if the timeout is big enough
410     table.setOperationTimeout(120 * 1000);
411     table.get(new Get(FAM_NAM));
412 
413     // Resetting and retrying. Will fail this time, not enough time for the second try
414     SleepAndFailFirstTime.ct.set(0);
415     try {
416       table.setOperationTimeout(30 * 1000);
417       table.get(new Get(FAM_NAM));
418       Assert.fail("We expect an exception here");
419     } catch (SocketTimeoutException e) {
420       // The client has a CallTimeout class, but it's not shared.We're not very clean today,
421       //  in the general case you can expect the call to stop, but the exception may vary.
422       // In this test however, we're sure that it will be a socket timeout.
423       LOG.info("We received an exception, as expected ", e);
424     } catch (IOException e) {
425       Assert.fail("Wrong exception:" + e.getMessage());
426     } finally {
427       table.close();
428     }
429   }
430 
431   @Test
432   public void testPutOperationTimeout() throws Exception {
433     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testPutOperationTimeout");
434     hdt.addCoprocessor(SleepAndFailFirstTime.class.getName());
435     Table table = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM },TEST_UTIL.getConfiguration());
436     table.setRpcTimeout(Integer.MAX_VALUE);
437     SleepAndFailFirstTime.ct.set(0);
438     // Check that it works if the timeout is big enough
439     table.setOperationTimeout(120 * 1000);
440     table.put(new Put(FAM_NAM).addColumn(FAM_NAM, FAM_NAM, FAM_NAM));
441 
442     // Resetting and retrying. Will fail this time, not enough time for the second try
443     SleepAndFailFirstTime.ct.set(0);
444     try {
445       table.setOperationTimeout(30 * 1000);
446       table.put(new Put(FAM_NAM).addColumn(FAM_NAM, FAM_NAM, FAM_NAM));
447       Assert.fail("We expect an exception here");
448     } catch (RetriesExhaustedWithDetailsException e) {
449       // The client has a CallTimeout class, but it's not shared.We're not very clean today,
450       //  in the general case you can expect the call to stop, but the exception may vary.
451       // In this test however, we're sure that it will be a socket timeout.
452       LOG.info("We received an exception, as expected ", e);
453     } catch (IOException e) {
454       Assert.fail("Wrong exception:" + e.getMessage());
455     } finally {
456       table.close();
457     }
458   }
459 
460   @Test
461   public void testDeleteOperationTimeout() throws Exception {
462     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testDeleteOperationTimeout");
463     hdt.addCoprocessor(SleepAndFailFirstTime.class.getName());
464     Table table = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM },TEST_UTIL.getConfiguration());
465     table.setRpcTimeout(Integer.MAX_VALUE);
466     SleepAndFailFirstTime.ct.set(0);
467     // Check that it works if the timeout is big enough
468     table.setOperationTimeout(120 * 1000);
469     table.delete(new Delete(FAM_NAM));
470 
471     // Resetting and retrying. Will fail this time, not enough time for the second try
472     SleepAndFailFirstTime.ct.set(0);
473     try {
474       table.setOperationTimeout(30 * 1000);
475       table.delete(new Delete(FAM_NAM));
476       Assert.fail("We expect an exception here");
477     } catch (IOException e) {
478       // The client has a CallTimeout class, but it's not shared.We're not very clean today,
479       //  in the general case you can expect the call to stop, but the exception may vary.
480       // In this test however, we're sure that it will be a socket timeout.
481       LOG.info("We received an exception, as expected ", e);
482     } finally {
483       table.close();
484     }
485   }
486   @Test
487   public void testDeleteRpcTimeout() throws Exception {
488     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testDeleteRpcTimeout");
489     hdt.addCoprocessor(SleepCoprocessor.class.getName());
490     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
491 
492     try (Table t = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM }, c)) {
493       t.setRpcTimeout(SleepCoprocessor.SLEEP_TIME / 2);
494       t.setOperationTimeout(SleepCoprocessor.SLEEP_TIME * 100);
495       Delete d = new Delete(FAM_NAM);
496       d.addColumn(FAM_NAM, FAM_NAM, 1);
497       t.delete(d);
498       fail("Write should not have succeeded");
499     } catch (RetriesExhaustedException e) {
500       // expected
501     }
502 
503   }
504 
505   @Test
506   public void testPutRpcTimeout() throws Exception {
507     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testPutRpcTimeout");
508     hdt.addCoprocessor(SleepCoprocessor.class.getName());
509     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
510 
511     try (Table t = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM }, c)) {
512       t.setRpcTimeout(SleepCoprocessor.SLEEP_TIME / 2);
513       t.setOperationTimeout(SleepCoprocessor.SLEEP_TIME * 100);
514       Put p = new Put(FAM_NAM);
515       p.addColumn(FAM_NAM, FAM_NAM, FAM_NAM);
516       t.put(p);
517       fail("Write should not have succeeded");
518     } catch (IOException e) {
519       // expected
520     }
521 
522   }
523 
524   @Test(expected = RetriesExhaustedException.class)
525   public void testRpcTimeout() throws Exception {
526     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testRpcTimeout");
527     hdt.addCoprocessor(SleepCoprocessor.class.getName());
528     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
529 
530     try (Table t = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM }, c)) {
531       assert t instanceof HTable;
532       HTable table = (HTable) t;
533       table.setRpcTimeout(SleepCoprocessor.SLEEP_TIME / 2);
534       table.setOperationTimeout(SleepCoprocessor.SLEEP_TIME * 100);
535       table.get(new Get(FAM_NAM));
536     }
537   }
538 
539   @Test
540   public void testDropTimeoutRequest() throws Exception {
541     // Simulate the situation that the server is slow and client retries for several times because
542     // of timeout. When a request can be handled after waiting in the queue, we will drop it if
543     // it has been considered as timeout at client. If we don't drop it, the server will waste time
544     // on handling timeout requests and finally all requests timeout and client throws exception.
545     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testDropTimeputRequest");
546     hdt.addCoprocessor(SleepLongerAtFirstCoprocessor.class.getName());
547     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
548     try (Table t = TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM }, c)) {
549       t.setRpcTimeout(SleepLongerAtFirstCoprocessor.SLEEP_TIME * 2);
550       t.get(new Get(FAM_NAM));
551     }
552   }
553 
554   /**
555    * Test starting from 0 index when RpcRetryingCaller calculate the backoff time.
556    */
557   @Test
558   public void testRpcRetryingCallerSleep() throws Exception {
559     HTableDescriptor hdt = TEST_UTIL.createTableDescriptor("HCM-testRpcRetryingCallerSleep");
560     hdt.addCoprocessorWithSpec("|" + SleepAndFailFirstTime.class.getName() + "||"
561         + SleepAndFailFirstTime.SLEEP_TIME_CONF_KEY + "=2000");
562     TEST_UTIL.createTable(hdt, new byte[][] { FAM_NAM }).close();
563 
564     Configuration c = new Configuration(TEST_UTIL.getConfiguration());
565     SleepAndFailFirstTime.ct.set(0);
566     c.setInt(HConstants.HBASE_CLIENT_PAUSE, 3000);
567     c.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 4000);
568 
569     Connection connection = ConnectionFactory.createConnection(c);
570     Table t = connection.getTable(TableName.valueOf("HCM-testRpcRetryingCallerSleep"));
571     if (t instanceof HTable) {
572       HTable table = (HTable) t;
573       table.setOperationTimeout(8000);
574       // Check that it works. Because 2s + 3s * RETRY_BACKOFF[0] + 2s < 8s
575       table.get(new Get(FAM_NAM));
576 
577       // Resetting and retrying.
578       SleepAndFailFirstTime.ct.set(0);
579       try {
580         table.setOperationTimeout(6000);
581         // Will fail this time. After sleep, there are not enough time for second retry
582         // Beacuse 2s + 3s + 2s > 6s
583         table.get(new Get(FAM_NAM));
584         Assert.fail("We expect an exception here");
585       } catch (SocketTimeoutException e) {
586         LOG.info("We received an exception, as expected ", e);
587       } catch (IOException e) {
588         Assert.fail("Wrong exception:" + e.getMessage());
589       } finally {
590         table.close();
591         connection.close();
592       }
593     }
594   }
595 
596   @Test
597   public void testCallableSleep() throws Exception {
598     long pauseTime;
599     long baseTime = 100;
600     TableName tableName = TableName.valueOf("HCM-testCallableSleep");
601     HTable table = TEST_UTIL.createTable(tableName, FAM_NAM);
602     RegionServerCallable<Object> regionServerCallable = new RegionServerCallable<Object>(
603         TEST_UTIL.getConnection(), tableName, ROW) {
604       public Object call(int timeout) throws IOException {
605         return null;
606       }
607     };
608 
609     regionServerCallable.prepare(false);
610     for (int i = 0; i < HConstants.RETRY_BACKOFF.length; i++) {
611       pauseTime = regionServerCallable.sleep(baseTime, i);
612       assertTrue(pauseTime >= (baseTime * HConstants.RETRY_BACKOFF[i]));
613       assertTrue(pauseTime <= (baseTime * HConstants.RETRY_BACKOFF[i] * 1.01f));
614     }
615 
616     RegionAdminServiceCallable<Object> regionAdminServiceCallable =
617         new RegionAdminServiceCallable<Object>(
618         (ClusterConnection) TEST_UTIL.getConnection(), new RpcControllerFactory(
619             TEST_UTIL.getConfiguration()), tableName, ROW) {
620       public Object call(int timeout) throws IOException {
621         return null;
622       }
623     };
624 
625     regionAdminServiceCallable.prepare(false);
626     for (int i = 0; i < HConstants.RETRY_BACKOFF.length; i++) {
627       pauseTime = regionAdminServiceCallable.sleep(baseTime, i);
628       assertTrue(pauseTime >= (baseTime * HConstants.RETRY_BACKOFF[i]));
629       assertTrue(pauseTime <= (baseTime * HConstants.RETRY_BACKOFF[i] * 1.01f));
630     }
631 
632     MasterCallable masterCallable = new MasterCallable((HConnection) TEST_UTIL.getConnection()) {
633       public Object call(int timeout) throws IOException {
634         return null;
635       }
636     };
637 
638     for (int i = 0; i < HConstants.RETRY_BACKOFF.length; i++) {
639       pauseTime = masterCallable.sleep(baseTime, i);
640       assertTrue(pauseTime >= (baseTime * HConstants.RETRY_BACKOFF[i]));
641       assertTrue(pauseTime <= (baseTime * HConstants.RETRY_BACKOFF[i] * 1.01f));
642     }
643   }
644 
645   private void testConnectionClose(boolean allowsInterrupt) throws Exception {
646     TableName tableName = TableName.valueOf("HCM-testConnectionClose" + allowsInterrupt);
647     TEST_UTIL.createTable(tableName, FAM_NAM).close();
648 
649     boolean previousBalance = TEST_UTIL.getHBaseAdmin().setBalancerRunning(false, true);
650 
651     Configuration c2 = new Configuration(TEST_UTIL.getConfiguration());
652     // We want to work on a separate connection.
653     c2.set(HConstants.HBASE_CLIENT_INSTANCE_ID, String.valueOf(-1));
654     c2.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 100); // retry a lot
655     c2.setInt(HConstants.HBASE_CLIENT_PAUSE, 1); // don't wait between retries.
656     c2.setInt(RpcClient.FAILED_SERVER_EXPIRY_KEY, 0); // Server do not really expire
657     c2.setBoolean(RpcClient.SPECIFIC_WRITE_THREAD, allowsInterrupt);
658 
659     final HTable table = new HTable(c2, tableName);
660 
661     Put put = new Put(ROW);
662     put.add(FAM_NAM, ROW, ROW);
663     table.put(put);
664 
665     // 4 steps: ready=0; doGets=1; mustStop=2; stopped=3
666     final AtomicInteger step = new AtomicInteger(0);
667 
668     final AtomicReference<Throwable> failed = new AtomicReference<Throwable>(null);
669     Thread t = new Thread("testConnectionCloseThread") {
670       @Override
671       public void run() {
672         int done = 0;
673         try {
674           step.set(1);
675           while (step.get() == 1) {
676             Get get = new Get(ROW);
677             table.get(get);
678             done++;
679             if (done % 100 == 0)
680               LOG.info("done=" + done);
681           }
682         } catch (Throwable t) {
683           failed.set(t);
684           LOG.error(t);
685         }
686         step.set(3);
687       }
688     };
689     t.start();
690     TEST_UTIL.waitFor(20000, new Waiter.Predicate<Exception>() {
691       @Override
692       public boolean evaluate() throws Exception {
693         return step.get() == 1;
694       }
695     });
696 
697     ServerName sn = table.getRegionLocation(ROW).getServerName();
698     ConnectionManager.HConnectionImplementation conn =
699         (ConnectionManager.HConnectionImplementation) table.getConnection();
700     RpcClient rpcClient = conn.getRpcClient();
701 
702     LOG.info("Going to cancel connections. connection=" + conn.toString() + ", sn=" + sn);
703     for (int i = 0; i < 5000; i++) {
704       rpcClient.cancelConnections(sn);
705       Thread.sleep(5);
706     }
707 
708     step.compareAndSet(1, 2);
709     // The test may fail here if the thread doing the gets is stuck. The way to find
710     //  out what's happening is to look for the thread named 'testConnectionCloseThread'
711     TEST_UTIL.waitFor(40000, new Waiter.Predicate<Exception>() {
712       @Override
713       public boolean evaluate() throws Exception {
714         return step.get() == 3;
715       }
716     });
717     table.close();
718     Assert.assertTrue("Unexpected exception is " + failed.get(), failed.get() == null);
719     TEST_UTIL.getHBaseAdmin().setBalancerRunning(previousBalance, true);
720   }
721 
722   /**
723    * Test that connection can become idle without breaking everything.
724    */
725   @Test
726   public void testConnectionIdle() throws Exception {
727     TableName tableName = TableName.valueOf("HCM-testConnectionIdle");
728     TEST_UTIL.createTable(tableName, FAM_NAM).close();
729     int idleTime =  20000;
730     boolean previousBalance = TEST_UTIL.getHBaseAdmin().setBalancerRunning(false, true);
731 
732     Configuration c2 = new Configuration(TEST_UTIL.getConfiguration());
733     // We want to work on a separate connection.
734     c2.set(HConstants.HBASE_CLIENT_INSTANCE_ID, String.valueOf(-1));
735     c2.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1); // Don't retry: retry = test failed
736     c2.setInt(RpcClient.IDLE_TIME, idleTime);
737 
738     final Table table = new HTable(c2, tableName);
739 
740     Put put = new Put(ROW);
741     put.add(FAM_NAM, ROW, ROW);
742     table.put(put);
743 
744     ManualEnvironmentEdge mee = new ManualEnvironmentEdge();
745     mee.setValue(System.currentTimeMillis());
746     EnvironmentEdgeManager.injectEdge(mee);
747     LOG.info("first get");
748     table.get(new Get(ROW));
749 
750     LOG.info("first get - changing the time & sleeping");
751     mee.incValue(idleTime + 1000);
752     Thread.sleep(1500); // we need to wait a little for the connection to be seen as idle.
753                         // 1500 = sleep time in RpcClient#waitForWork + a margin
754 
755     LOG.info("second get - connection has been marked idle in the middle");
756     // To check that the connection actually became idle would need to read some private
757     //  fields of RpcClient.
758     table.get(new Get(ROW));
759     mee.incValue(idleTime + 1000);
760 
761     LOG.info("third get - connection is idle, but the reader doesn't know yet");
762     // We're testing here a special case:
763     //  time limit reached BUT connection not yet reclaimed AND a new call.
764     //  in this situation, we don't close the connection, instead we use it immediately.
765     // If we're very unlucky we can have a race condition in the test: the connection is already
766     //  under closing when we do the get, so we have an exception, and we don't retry as the
767     //  retry number is 1. The probability is very very low, and seems acceptable for now. It's
768     //  a test issue only.
769     table.get(new Get(ROW));
770 
771     LOG.info("we're done - time will change back");
772 
773     table.close();
774     EnvironmentEdgeManager.reset();
775     TEST_UTIL.getHBaseAdmin().setBalancerRunning(previousBalance, true);
776   }
777 
778     /**
779      * Test that the connection to the dead server is cut immediately when we receive the
780      *  notification.
781      * @throws Exception
782      */
783   @Test
784   public void testConnectionCut() throws Exception {
785 
786     TableName tableName = TableName.valueOf("HCM-testConnectionCut");
787 
788     TEST_UTIL.createTable(tableName, FAM_NAM).close();
789     boolean previousBalance = TEST_UTIL.getHBaseAdmin().setBalancerRunning(false, true);
790 
791     Configuration c2 = new Configuration(TEST_UTIL.getConfiguration());
792     // We want to work on a separate connection.
793     c2.set(HConstants.HBASE_CLIENT_INSTANCE_ID, String.valueOf(-1));
794     c2.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
795     c2.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 30 * 1000);
796 
797     HTable table = new HTable(c2, tableName);
798 
799     Put p = new Put(FAM_NAM);
800     p.add(FAM_NAM, FAM_NAM, FAM_NAM);
801     table.put(p);
802 
803     final HConnectionImplementation hci =  (HConnectionImplementation)table.getConnection();
804     final HRegionLocation loc = table.getRegionLocation(FAM_NAM);
805 
806     Get get = new Get(FAM_NAM);
807     Assert.assertNotNull(table.get(get));
808 
809     get = new Get(FAM_NAM);
810     get.setFilter(new BlockingFilter());
811 
812     // This thread will mark the server as dead while we're waiting during a get.
813     Thread t = new Thread() {
814       @Override
815       public void run() {
816         synchronized (syncBlockingFilter) {
817           try {
818             syncBlockingFilter.wait();
819           } catch (InterruptedException e) {
820             throw new RuntimeException(e);
821           }
822         }
823         hci.clusterStatusListener.deadServerHandler.newDead(loc.getServerName());
824       }
825     };
826 
827     t.start();
828     try {
829       table.get(get);
830       Assert.fail();
831     } catch (IOException expected) {
832       LOG.debug("Received: " + expected);
833       Assert.assertFalse(expected instanceof SocketTimeoutException);
834       Assert.assertFalse(syncBlockingFilter.get());
835     } finally {
836       syncBlockingFilter.set(true);
837       t.join();
838       HConnectionManager.getConnection(c2).close();
839       TEST_UTIL.getHBaseAdmin().setBalancerRunning(previousBalance, true);
840     }
841 
842     table.close();
843   }
844 
845   protected static final AtomicBoolean syncBlockingFilter = new AtomicBoolean(false);
846 
847   public static class BlockingFilter extends FilterBase {
848     @Override
849     public boolean filterRowKey(byte[] buffer, int offset, int length) throws IOException {
850       int i = 0;
851       while (i++ < 1000 && !syncBlockingFilter.get()) {
852         synchronized (syncBlockingFilter) {
853           syncBlockingFilter.notifyAll();
854         }
855         Threads.sleep(100);
856       }
857       syncBlockingFilter.set(true);
858       return false;
859     }
860     @Override
861     public ReturnCode filterKeyValue(Cell ignored) throws IOException {
862       return ReturnCode.INCLUDE;
863     }
864 
865     public static Filter parseFrom(final byte [] pbBytes) throws DeserializationException{
866       return new BlockingFilter();
867     }
868   }
869 
870   @Test (timeout=120000)
871   public void abortingHConnectionRemovesItselfFromHCM() throws Exception {
872     // Save off current HConnections
873     Map<HConnectionKey, HConnectionImplementation> oldHBaseInstances =
874         new HashMap<HConnectionKey, HConnectionImplementation>();
875     oldHBaseInstances.putAll(ConnectionManager.CONNECTION_INSTANCES);
876 
877     ConnectionManager.CONNECTION_INSTANCES.clear();
878 
879     try {
880       HConnection connection = HConnectionManager.getConnection(TEST_UTIL.getConfiguration());
881       connection.abort("test abortingHConnectionRemovesItselfFromHCM", new Exception(
882           "test abortingHConnectionRemovesItselfFromHCM"));
883       Assert.assertNotSame(connection,
884         HConnectionManager.getConnection(TEST_UTIL.getConfiguration()));
885     } finally {
886       // Put original HConnections back
887       ConnectionManager.CONNECTION_INSTANCES.clear();
888       ConnectionManager.CONNECTION_INSTANCES.putAll(oldHBaseInstances);
889     }
890   }
891 
892   /**
893    * Test that when we delete a location using the first row of a region
894    * that we really delete it.
895    * @throws Exception
896    */
897   @Test
898   public void testRegionCaching() throws Exception{
899     TEST_UTIL.createMultiRegionTable(TABLE_NAME, FAM_NAM).close();
900     Configuration conf =  new Configuration(TEST_UTIL.getConfiguration());
901     conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
902     HTable table = new HTable(conf, TABLE_NAME);
903 
904     TEST_UTIL.waitUntilAllRegionsAssigned(table.getName());
905     Put put = new Put(ROW);
906     put.add(FAM_NAM, ROW, ROW);
907     table.put(put);
908     ConnectionManager.HConnectionImplementation conn =
909       (ConnectionManager.HConnectionImplementation)table.getConnection();
910 
911     assertNotNull(conn.getCachedLocation(TABLE_NAME, ROW));
912 
913     final int nextPort = conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation().getPort() + 1;
914     HRegionLocation loc = conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation();
915     conn.updateCachedLocation(loc.getRegionInfo(), loc.getServerName(),
916         ServerName.valueOf("127.0.0.1", nextPort,
917         HConstants.LATEST_TIMESTAMP), HConstants.LATEST_TIMESTAMP);
918     Assert.assertEquals(conn.getCachedLocation(TABLE_NAME, ROW)
919       .getRegionLocation().getPort(), nextPort);
920 
921     conn.clearRegionCache(TABLE_NAME, ROW.clone());
922     RegionLocations rl = conn.getCachedLocation(TABLE_NAME, ROW);
923     assertNull("What is this location?? " + rl, rl);
924 
925     // We're now going to move the region and check that it works for the client
926     // First a new put to add the location in the cache
927     conn.clearRegionCache(TABLE_NAME);
928     Assert.assertEquals(0, conn.getNumberOfCachedRegionLocations(TABLE_NAME));
929     Put put2 = new Put(ROW);
930     put2.add(FAM_NAM, ROW, ROW);
931     table.put(put2);
932     assertNotNull(conn.getCachedLocation(TABLE_NAME, ROW));
933     assertNotNull(conn.getCachedLocation(TableName.valueOf(TABLE_NAME.getName()), ROW.clone()));
934 
935     TEST_UTIL.getHBaseAdmin().setBalancerRunning(false, false);
936     HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
937 
938     // We can wait for all regions to be online, that makes log reading easier when debugging
939     TEST_UTIL.waitUntilNoRegionsInTransition();
940 
941     // Now moving the region to the second server
942     HRegionLocation toMove = conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation();
943     byte[] regionName = toMove.getRegionInfo().getRegionName();
944     byte[] encodedRegionNameBytes = toMove.getRegionInfo().getEncodedNameAsBytes();
945 
946     // Choose the other server.
947     int curServerId = TEST_UTIL.getHBaseCluster().getServerWith(regionName);
948     int destServerId = (curServerId == 0 ? 1 : 0);
949 
950     HRegionServer curServer = TEST_UTIL.getHBaseCluster().getRegionServer(curServerId);
951     HRegionServer destServer = TEST_UTIL.getHBaseCluster().getRegionServer(destServerId);
952 
953     ServerName destServerName = destServer.getServerName();
954 
955     // Check that we are in the expected state
956     Assert.assertTrue(curServer != destServer);
957     Assert.assertFalse(curServer.getServerName().equals(destServer.getServerName()));
958     Assert.assertFalse( toMove.getPort() == destServerName.getPort());
959     Assert.assertNotNull(curServer.getOnlineRegion(regionName));
960     Assert.assertNull(destServer.getOnlineRegion(regionName));
961     Assert.assertFalse(TEST_UTIL.getMiniHBaseCluster().getMaster().
962         getAssignmentManager().getRegionStates().isRegionsInTransition());
963 
964     // Moving. It's possible that we don't have all the regions online at this point, so
965     //  the test must depends only on the region we're looking at.
966     LOG.info("Move starting region="+toMove.getRegionInfo().getRegionNameAsString());
967     TEST_UTIL.getHBaseAdmin().move(
968       toMove.getRegionInfo().getEncodedNameAsBytes(),
969       destServerName.getServerName().getBytes()
970     );
971 
972     while (destServer.getOnlineRegion(regionName) == null ||
973         destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
974         curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
975         master.getAssignmentManager().getRegionStates().isRegionsInTransition()) {
976       // wait for the move to be finished
977       Thread.sleep(1);
978     }
979 
980     LOG.info("Move finished for region="+toMove.getRegionInfo().getRegionNameAsString());
981 
982     // Check our new state.
983     Assert.assertNull(curServer.getOnlineRegion(regionName));
984     Assert.assertNotNull(destServer.getOnlineRegion(regionName));
985     Assert.assertFalse(destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));
986     Assert.assertFalse(curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));
987 
988 
989     // Cache was NOT updated and points to the wrong server
990     Assert.assertFalse(
991         conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation()
992           .getPort() == destServerName.getPort());
993 
994     // This part relies on a number of tries equals to 1.
995     // We do a put and expect the cache to be updated, even if we don't retry
996     LOG.info("Put starting");
997     Put put3 = new Put(ROW);
998     put3.add(FAM_NAM, ROW, ROW);
999     try {
1000       table.put(put3);
1001       Assert.fail("Unreachable point");
1002     } catch (RetriesExhaustedWithDetailsException e){
1003       LOG.info("Put done, exception caught: " + e.getClass());
1004       Assert.assertEquals(1, e.getNumExceptions());
1005       Assert.assertEquals(1, e.getCauses().size());
1006       Assert.assertArrayEquals(e.getRow(0).getRow(), ROW);
1007 
1008       // Check that we unserialized the exception as expected
1009       Throwable cause = ClientExceptionsUtil.findException(e.getCause(0));
1010       Assert.assertNotNull(cause);
1011       Assert.assertTrue(cause instanceof RegionMovedException);
1012     }
1013     Assert.assertNotNull("Cached connection is null", conn.getCachedLocation(TABLE_NAME, ROW));
1014     Assert.assertEquals(
1015         "Previous server was " + curServer.getServerName().getHostAndPort(),
1016         destServerName.getPort(),
1017         conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation().getPort());
1018 
1019     Assert.assertFalse(destServer.getRegionsInTransitionInRS()
1020       .containsKey(encodedRegionNameBytes));
1021     Assert.assertFalse(curServer.getRegionsInTransitionInRS()
1022       .containsKey(encodedRegionNameBytes));
1023 
1024     // We move it back to do another test with a scan
1025     LOG.info("Move starting region=" + toMove.getRegionInfo().getRegionNameAsString());
1026     TEST_UTIL.getHBaseAdmin().move(
1027       toMove.getRegionInfo().getEncodedNameAsBytes(),
1028       curServer.getServerName().getServerName().getBytes()
1029     );
1030 
1031     while (curServer.getOnlineRegion(regionName) == null ||
1032         destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
1033         curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
1034         master.getAssignmentManager().getRegionStates().isRegionsInTransition()) {
1035       // wait for the move to be finished
1036       Thread.sleep(1);
1037     }
1038 
1039     // Check our new state.
1040     Assert.assertNotNull(curServer.getOnlineRegion(regionName));
1041     Assert.assertNull(destServer.getOnlineRegion(regionName));
1042     LOG.info("Move finished for region=" + toMove.getRegionInfo().getRegionNameAsString());
1043 
1044     // Cache was NOT updated and points to the wrong server
1045     Assert.assertFalse(conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation().getPort() ==
1046       curServer.getServerName().getPort());
1047 
1048     Scan sc = new Scan();
1049     sc.setStopRow(ROW);
1050     sc.setStartRow(ROW);
1051 
1052     // The scanner takes the max retries from the connection configuration, not the table as
1053     // the put.
1054     TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
1055 
1056     try {
1057       ResultScanner rs = table.getScanner(sc);
1058       while (rs.next() != null) {
1059       }
1060       Assert.fail("Unreachable point");
1061     } catch (RetriesExhaustedException e) {
1062       LOG.info("Scan done, expected exception caught: " + e.getClass());
1063     }
1064 
1065     // Cache is updated with the right value.
1066     Assert.assertNotNull(conn.getCachedLocation(TABLE_NAME, ROW));
1067     Assert.assertEquals(
1068       "Previous server was "+destServer.getServerName().getHostAndPort(),
1069       curServer.getServerName().getPort(),
1070       conn.getCachedLocation(TABLE_NAME, ROW).getRegionLocation().getPort());
1071 
1072     TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, RPC_RETRY);
1073     table.close();
1074   }
1075 
1076   /**
1077    * Test that Connection or Pool are not closed when managed externally
1078    * @throws Exception
1079    */
1080   @Test
1081   public void testConnectionManagement() throws Exception{
1082     Table table0 = TEST_UTIL.createTable(TABLE_NAME1, FAM_NAM);
1083     Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
1084     HTable table = (HTable) conn.getTable(TABLE_NAME1);
1085     table.close();
1086     assertFalse(conn.isClosed());
1087     assertFalse(table.getPool().isShutdown());
1088     table = (HTable) conn.getTable(TABLE_NAME1);
1089     table.close();
1090     assertFalse(table.getPool().isShutdown());
1091     conn.close();
1092     assertTrue(table.getPool().isShutdown());
1093     table0.close();
1094   }
1095 
1096   /**
1097    * Test that stale cache updates don't override newer cached values.
1098    */
1099   @Test
1100   public void testCacheSeqNums() throws Exception{
1101     HTable table = TEST_UTIL.createMultiRegionTable(TABLE_NAME2, FAM_NAM);
1102     Put put = new Put(ROW);
1103     put.add(FAM_NAM, ROW, ROW);
1104     table.put(put);
1105     ConnectionManager.HConnectionImplementation conn =
1106       (ConnectionManager.HConnectionImplementation)table.getConnection();
1107 
1108     HRegionLocation location = conn.getCachedLocation(TABLE_NAME2, ROW).getRegionLocation();
1109     assertNotNull(location);
1110 
1111     ServerName anySource = ServerName.valueOf(location.getHostname(), location.getPort() - 1, 0L);
1112 
1113     // Same server as already in cache reporting - overwrites any value despite seqNum.
1114     int nextPort = location.getPort() + 1;
1115     conn.updateCachedLocation(location.getRegionInfo(), location.getServerName(),
1116         ServerName.valueOf("127.0.0.1", nextPort, 0), location.getSeqNum() - 1);
1117     location = conn.getCachedLocation(TABLE_NAME2, ROW).getRegionLocation();
1118     Assert.assertEquals(nextPort, location.getPort());
1119 
1120     // No source specified - same.
1121     nextPort = location.getPort() + 1;
1122     conn.updateCachedLocation(location.getRegionInfo(), location.getServerName(),
1123         ServerName.valueOf("127.0.0.1", nextPort, 0), location.getSeqNum() - 1);
1124     location = conn.getCachedLocation(TABLE_NAME2, ROW).getRegionLocation();
1125     Assert.assertEquals(nextPort, location.getPort());
1126 
1127     // Higher seqNum - overwrites lower seqNum.
1128     nextPort = location.getPort() + 1;
1129     conn.updateCachedLocation(location.getRegionInfo(), anySource,
1130         ServerName.valueOf("127.0.0.1", nextPort, 0), location.getSeqNum() + 1);
1131     location = conn.getCachedLocation(TABLE_NAME2, ROW).getRegionLocation();
1132     Assert.assertEquals(nextPort, location.getPort());
1133 
1134     // Lower seqNum - does not overwrite higher seqNum.
1135     nextPort = location.getPort() + 1;
1136     conn.updateCachedLocation(location.getRegionInfo(), anySource,
1137         ServerName.valueOf("127.0.0.1", nextPort, 0), location.getSeqNum() - 1);
1138     location = conn.getCachedLocation(TABLE_NAME2, ROW).getRegionLocation();
1139     Assert.assertEquals(nextPort - 1, location.getPort());
1140     table.close();
1141   }
1142 
1143   /**
1144    * Make sure that {@link Configuration} instances that are essentially the
1145    * same map to the same {@link HConnection} instance.
1146    */
1147   @Test
1148   public void testConnectionSameness() throws Exception {
1149     Connection previousConnection = null;
1150     for (int i = 0; i < 2; i++) {
1151       // set random key to differentiate the connection from previous ones
1152       Configuration configuration = TEST_UTIL.getConfiguration();
1153       configuration.set("some_key", String.valueOf(_randy.nextInt()));
1154       LOG.info("The hash code of the current configuration is: "
1155           + configuration.hashCode());
1156       Connection currentConnection = HConnectionManager
1157           .getConnection(configuration);
1158       if (previousConnection != null) {
1159         assertTrue(
1160             "Did not get the same connection even though its key didn't change",
1161             previousConnection == currentConnection);
1162       }
1163       previousConnection = currentConnection;
1164       // change the configuration, so that it is no longer reachable from the
1165       // client's perspective. However, since its part of the LRU doubly linked
1166       // list, it will eventually get thrown out, at which time it should also
1167       // close the corresponding {@link HConnection}.
1168       configuration.set("other_key", String.valueOf(_randy.nextInt()));
1169     }
1170   }
1171 
1172   /**
1173    * Makes sure that there is no leaking of
1174    * {@link ConnectionManager.HConnectionImplementation} in the {@link HConnectionManager}
1175    * class.
1176    * @deprecated Tests deprecated functionality.  Remove in 1.0.
1177    */
1178   @Deprecated
1179   @Test
1180   public void testConnectionUniqueness() throws Exception {
1181     int zkmaxconnections = TEST_UTIL.getConfiguration().
1182       getInt(HConstants.ZOOKEEPER_MAX_CLIENT_CNXNS,
1183           HConstants.DEFAULT_ZOOKEPER_MAX_CLIENT_CNXNS);
1184     // Test up to a max that is < the maximum number of zk connections.  If we
1185     // go above zk connections, we just fall into cycle where we are failing
1186     // to set up a session and test runs for a long time.
1187     int maxConnections = Math.min(zkmaxconnections - 1, 20);
1188     List<HConnection> connections = new ArrayList<HConnection>(maxConnections);
1189     Connection previousConnection = null;
1190     try {
1191       for (int i = 0; i < maxConnections; i++) {
1192         // set random key to differentiate the connection from previous ones
1193         Configuration configuration = new Configuration(TEST_UTIL.getConfiguration());
1194         configuration.set("some_key", String.valueOf(_randy.nextInt()));
1195         configuration.set(HConstants.HBASE_CLIENT_INSTANCE_ID,
1196             String.valueOf(_randy.nextInt()));
1197         LOG.info("The hash code of the current configuration is: "
1198             + configuration.hashCode());
1199         HConnection currentConnection =
1200           HConnectionManager.getConnection(configuration);
1201         if (previousConnection != null) {
1202           assertTrue("Got the same connection even though its key changed!",
1203               previousConnection != currentConnection);
1204         }
1205         // change the configuration, so that it is no longer reachable from the
1206         // client's perspective. However, since its part of the LRU doubly linked
1207         // list, it will eventually get thrown out, at which time it should also
1208         // close the corresponding {@link HConnection}.
1209         configuration.set("other_key", String.valueOf(_randy.nextInt()));
1210 
1211         previousConnection = currentConnection;
1212         LOG.info("The current HConnectionManager#HBASE_INSTANCES cache size is: "
1213             + getHConnectionManagerCacheSize());
1214         Thread.sleep(50);
1215         connections.add(currentConnection);
1216       }
1217     } finally {
1218       for (Connection c: connections) {
1219         // Clean up connections made so we don't interfere w/ subsequent tests.
1220         HConnectionManager.deleteConnection(c.getConfiguration());
1221       }
1222     }
1223   }
1224 
1225   @Test
1226   public void testClosing() throws Exception {
1227     Configuration configuration =
1228       new Configuration(TEST_UTIL.getConfiguration());
1229     configuration.set(HConstants.HBASE_CLIENT_INSTANCE_ID,
1230         String.valueOf(_randy.nextInt()));
1231 
1232     Connection c1 = ConnectionFactory.createConnection(configuration);
1233     // We create two connections with the same key.
1234     Connection c2 = ConnectionFactory.createConnection(configuration);
1235 
1236     Connection c3 = HConnectionManager.getConnection(configuration);
1237     Connection c4 = HConnectionManager.getConnection(configuration);
1238     assertTrue(c3 == c4);
1239 
1240     c1.close();
1241     assertTrue(c1.isClosed());
1242     assertFalse(c2.isClosed());
1243     assertFalse(c3.isClosed());
1244 
1245     c3.close();
1246     // still a reference left
1247     assertFalse(c3.isClosed());
1248     c3.close();
1249     assertTrue(c3.isClosed());
1250     // c3 was removed from the cache
1251     Connection c5 = HConnectionManager.getConnection(configuration);
1252     assertTrue(c5 != c3);
1253 
1254     assertFalse(c2.isClosed());
1255     c2.close();
1256     assertTrue(c2.isClosed());
1257     c5.close();
1258     assertTrue(c5.isClosed());
1259   }
1260 
1261   /**
1262    * Trivial test to verify that nobody messes with
1263    * {@link HConnectionManager#createConnection(Configuration)}
1264    */
1265   @Test
1266   public void testCreateConnection() throws Exception {
1267     Configuration configuration = TEST_UTIL.getConfiguration();
1268     Connection c1 = ConnectionFactory.createConnection(configuration);
1269     Connection c2 = ConnectionFactory.createConnection(configuration);
1270     // created from the same configuration, yet they are different
1271     assertTrue(c1 != c2);
1272     assertTrue(c1.getConfiguration() == c2.getConfiguration());
1273     // make sure these were not cached
1274     Connection c3 = HConnectionManager.getConnection(configuration);
1275     assertTrue(c1 != c3);
1276     assertTrue(c2 != c3);
1277   }
1278 
1279 
1280   /**
1281    * This test checks that one can connect to the cluster with only the
1282    *  ZooKeeper quorum set. Other stuff like master address will be read
1283    *  from ZK by the client.
1284    */
1285   @Test
1286   public void testConnection() throws Exception{
1287     // We create an empty config and add the ZK address.
1288     Configuration c = new Configuration();
1289     c.set(HConstants.ZOOKEEPER_QUORUM,
1290       TEST_UTIL.getConfiguration().get(HConstants.ZOOKEEPER_QUORUM));
1291     c.set(HConstants.ZOOKEEPER_CLIENT_PORT ,
1292       TEST_UTIL.getConfiguration().get(HConstants.ZOOKEEPER_CLIENT_PORT));
1293 
1294     // This should be enough to connect
1295     HConnection conn = HConnectionManager.getConnection(c);
1296     assertTrue( conn.isMasterRunning() );
1297     conn.close();
1298   }
1299 
1300   private int setNumTries(HConnectionImplementation hci, int newVal) throws Exception {
1301     Field numTries = hci.getClass().getDeclaredField("numTries");
1302     numTries.setAccessible(true);
1303     Field modifiersField = Field.class.getDeclaredField("modifiers");
1304     modifiersField.setAccessible(true);
1305     modifiersField.setInt(numTries, numTries.getModifiers() & ~Modifier.FINAL);
1306     final int prevNumRetriesVal = (Integer)numTries.get(hci);
1307     numTries.set(hci, newVal);
1308 
1309     return prevNumRetriesVal;
1310   }
1311 
1312   @Test
1313   public void testMulti() throws Exception {
1314     HTable table = TEST_UTIL.createMultiRegionTable(TABLE_NAME3, FAM_NAM);
1315     try {
1316        ConnectionManager.HConnectionImplementation conn =
1317            ( ConnectionManager.HConnectionImplementation)table.getConnection();
1318 
1319        // We're now going to move the region and check that it works for the client
1320        // First a new put to add the location in the cache
1321        conn.clearRegionCache(TABLE_NAME3);
1322        Assert.assertEquals(0, conn.getNumberOfCachedRegionLocations(TABLE_NAME3));
1323 
1324        TEST_UTIL.getHBaseAdmin().setBalancerRunning(false, false);
1325        HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
1326 
1327        // We can wait for all regions to be online, that makes log reading easier when debugging
1328        TEST_UTIL.waitUntilNoRegionsInTransition();
1329 
1330        Put put = new Put(ROW_X);
1331        put.add(FAM_NAM, ROW_X, ROW_X);
1332        table.put(put);
1333 
1334        // Now moving the region to the second server
1335        HRegionLocation toMove = conn.getCachedLocation(TABLE_NAME3, ROW_X).getRegionLocation();
1336        byte[] regionName = toMove.getRegionInfo().getRegionName();
1337        byte[] encodedRegionNameBytes = toMove.getRegionInfo().getEncodedNameAsBytes();
1338 
1339        // Choose the other server.
1340        int curServerId = TEST_UTIL.getHBaseCluster().getServerWith(regionName);
1341        int destServerId = (curServerId == 0 ? 1 : 0);
1342 
1343        HRegionServer curServer = TEST_UTIL.getHBaseCluster().getRegionServer(curServerId);
1344        HRegionServer destServer = TEST_UTIL.getHBaseCluster().getRegionServer(destServerId);
1345 
1346        ServerName destServerName = destServer.getServerName();
1347 
1348        //find another row in the cur server that is less than ROW_X
1349        List<Region> regions = curServer.getOnlineRegions(TABLE_NAME3);
1350        byte[] otherRow = null;
1351        for (Region region : regions) {
1352          if (!region.getRegionInfo().getEncodedName().equals(toMove.getRegionInfo().getEncodedName())
1353              && Bytes.BYTES_COMPARATOR.compare(region.getRegionInfo().getStartKey(), ROW_X) < 0) {
1354            otherRow = region.getRegionInfo().getStartKey();
1355            break;
1356          }
1357        }
1358        assertNotNull(otherRow);
1359        // If empty row, set it to first row.-f
1360        if (otherRow.length <= 0) otherRow = Bytes.toBytes("aaa");
1361        Put put2 = new Put(otherRow);
1362        put2.add(FAM_NAM, otherRow, otherRow);
1363        table.put(put2); //cache put2's location
1364 
1365        // Check that we are in the expected state
1366        Assert.assertTrue(curServer != destServer);
1367        Assert.assertNotEquals(curServer.getServerName(), destServer.getServerName());
1368        Assert.assertNotEquals(toMove.getPort(), destServerName.getPort());
1369        Assert.assertNotNull(curServer.getOnlineRegion(regionName));
1370        Assert.assertNull(destServer.getOnlineRegion(regionName));
1371        Assert.assertFalse(TEST_UTIL.getMiniHBaseCluster().getMaster().
1372            getAssignmentManager().getRegionStates().isRegionsInTransition());
1373 
1374        // Moving. It's possible that we don't have all the regions online at this point, so
1375        //  the test must depends only on the region we're looking at.
1376        LOG.info("Move starting region="+toMove.getRegionInfo().getRegionNameAsString());
1377        TEST_UTIL.getHBaseAdmin().move(
1378            toMove.getRegionInfo().getEncodedNameAsBytes(),
1379            destServerName.getServerName().getBytes()
1380            );
1381 
1382        while (destServer.getOnlineRegion(regionName) == null ||
1383            destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
1384            curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes) ||
1385            master.getAssignmentManager().getRegionStates().isRegionsInTransition()) {
1386          // wait for the move to be finished
1387          Thread.sleep(1);
1388         }
1389 
1390        LOG.info("Move finished for region="+toMove.getRegionInfo().getRegionNameAsString());
1391 
1392        // Check our new state.
1393        Assert.assertNull(curServer.getOnlineRegion(regionName));
1394        Assert.assertNotNull(destServer.getOnlineRegion(regionName));
1395        Assert.assertFalse(destServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));
1396        Assert.assertFalse(curServer.getRegionsInTransitionInRS().containsKey(encodedRegionNameBytes));
1397 
1398 
1399        // Cache was NOT updated and points to the wrong server
1400        Assert.assertFalse(
1401            conn.getCachedLocation(TABLE_NAME3, ROW_X).getRegionLocation()
1402             .getPort() == destServerName.getPort());
1403 
1404        // Hijack the number of retry to fail after 2 tries
1405        final int prevNumRetriesVal = setNumTries(conn, 2);
1406 
1407        Put put3 = new Put(ROW_X);
1408        put3.add(FAM_NAM, ROW_X, ROW_X);
1409        Put put4 = new Put(otherRow);
1410        put4.add(FAM_NAM, otherRow, otherRow);
1411 
1412        // do multi
1413        table.batch(Lists.newArrayList(put4, put3)); // first should be a valid row,
1414        // second we get RegionMovedException.
1415 
1416        setNumTries(conn, prevNumRetriesVal);
1417      } finally {
1418        table.close();
1419      }
1420   }
1421 
1422   @Test
1423   public void testErrorBackoffTimeCalculation() throws Exception {
1424     // TODO: This test would seem to presume hardcoded RETRY_BACKOFF which it should not.
1425     final long ANY_PAUSE = 100;
1426     ServerName location = ServerName.valueOf("127.0.0.1", 1, 0);
1427     ServerName diffLocation = ServerName.valueOf("127.0.0.1", 2, 0);
1428 
1429     ManualEnvironmentEdge timeMachine = new ManualEnvironmentEdge();
1430     EnvironmentEdgeManager.injectEdge(timeMachine);
1431     try {
1432       long timeBase = timeMachine.currentTime();
1433       long largeAmountOfTime = ANY_PAUSE * 1000;
1434       ConnectionManager.ServerErrorTracker tracker =
1435           new ConnectionManager.ServerErrorTracker(largeAmountOfTime, 100);
1436 
1437       // The default backoff is 0.
1438       assertEquals(0, tracker.calculateBackoffTime(location, ANY_PAUSE));
1439 
1440       // Check some backoff values from HConstants sequence.
1441       tracker.reportServerError(location);
1442       assertEqualsWithJitter(ANY_PAUSE * HConstants.RETRY_BACKOFF[0],
1443         tracker.calculateBackoffTime(location, ANY_PAUSE));
1444       tracker.reportServerError(location);
1445       tracker.reportServerError(location);
1446       tracker.reportServerError(location);
1447       assertEqualsWithJitter(ANY_PAUSE * HConstants.RETRY_BACKOFF[3],
1448         tracker.calculateBackoffTime(location, ANY_PAUSE));
1449 
1450       // All of this shouldn't affect backoff for different location.
1451       assertEquals(0, tracker.calculateBackoffTime(diffLocation, ANY_PAUSE));
1452       tracker.reportServerError(diffLocation);
1453       assertEqualsWithJitter(ANY_PAUSE * HConstants.RETRY_BACKOFF[0],
1454         tracker.calculateBackoffTime(diffLocation, ANY_PAUSE));
1455 
1456       // Check with different base.
1457       assertEqualsWithJitter(ANY_PAUSE * 2 * HConstants.RETRY_BACKOFF[3],
1458           tracker.calculateBackoffTime(location, ANY_PAUSE * 2));
1459     } finally {
1460       EnvironmentEdgeManager.reset();
1461     }
1462   }
1463 
1464   private static void assertEqualsWithJitter(long expected, long actual) {
1465     assertEqualsWithJitter(expected, actual, expected);
1466   }
1467 
1468   private static void assertEqualsWithJitter(long expected, long actual, long jitterBase) {
1469     assertTrue("Value not within jitter: " + expected + " vs " + actual,
1470         Math.abs(actual - expected) <= (0.01f * jitterBase));
1471   }
1472 
1473   /**
1474    * Tests that a destroyed connection does not have a live zookeeper.
1475    * Below is timing based.  We put up a connection to a table and then close the connection while
1476    * having a background thread running that is forcing close of the connection to try and
1477    * provoke a close catastrophe; we are hoping for a car crash so we can see if we are leaking
1478    * zk connections.
1479    * @throws Exception
1480    */
1481   @Ignore ("Flakey test: See HBASE-8996")@Test
1482   public void testDeleteForZKConnLeak() throws Exception {
1483     TEST_UTIL.createTable(TABLE_NAME4, FAM_NAM);
1484     final Configuration config = HBaseConfiguration.create(TEST_UTIL.getConfiguration());
1485     config.setInt("zookeeper.recovery.retry", 1);
1486     config.setInt("zookeeper.recovery.retry.intervalmill", 1000);
1487     config.setInt("hbase.rpc.timeout", 2000);
1488     config.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
1489 
1490     ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 10,
1491       5, TimeUnit.SECONDS,
1492       new SynchronousQueue<Runnable>(),
1493       Threads.newDaemonThreadFactory("test-hcm-delete"));
1494 
1495     pool.submit(new Runnable() {
1496       @Override
1497       public void run() {
1498         while (!Thread.interrupted()) {
1499           try {
1500             HConnection conn = HConnectionManager.getConnection(config);
1501             LOG.info("Connection " + conn);
1502             HConnectionManager.deleteStaleConnection(conn);
1503             LOG.info("Connection closed " + conn);
1504             // TODO: This sleep time should be less than the time that it takes to open and close
1505             // a table.  Ideally we would do a few runs first to measure.  For now this is
1506             // timing based; hopefully we hit the bad condition.
1507             Threads.sleep(10);
1508           } catch (Exception e) {
1509           }
1510         }
1511       }
1512     });
1513 
1514     // Use connection multiple times.
1515     for (int i = 0; i < 30; i++) {
1516       Connection c1 = null;
1517       try {
1518         c1 = ConnectionManager.getConnectionInternal(config);
1519         LOG.info("HTable connection " + i + " " + c1);
1520         Table table = new HTable(config, TABLE_NAME4, pool);
1521         table.close();
1522         LOG.info("HTable connection " + i + " closed " + c1);
1523       } catch (Exception e) {
1524         LOG.info("We actually want this to happen!!!!  So we can see if we are leaking zk", e);
1525       } finally {
1526         if (c1 != null) {
1527           if (c1.isClosed()) {
1528             // cannot use getZooKeeper as method instantiates watcher if null
1529             Field zkwField = c1.getClass().getDeclaredField("keepAliveZookeeper");
1530             zkwField.setAccessible(true);
1531             Object watcher = zkwField.get(c1);
1532 
1533             if (watcher != null) {
1534               if (((ZooKeeperWatcher)watcher).getRecoverableZooKeeper().getState().isAlive()) {
1535                 // non-synchronized access to watcher; sleep and check again in case zk connection
1536                 // hasn't been cleaned up yet.
1537                 Thread.sleep(1000);
1538                 if (((ZooKeeperWatcher) watcher).getRecoverableZooKeeper().getState().isAlive()) {
1539                   pool.shutdownNow();
1540                   fail("Live zookeeper in closed connection");
1541                 }
1542               }
1543             }
1544           }
1545           c1.close();
1546         }
1547       }
1548     }
1549     pool.shutdownNow();
1550   }
1551 
1552   @Test
1553   public void testConnectionRideOverClusterRestart() throws IOException, InterruptedException {
1554     Configuration config = new Configuration(TEST_UTIL.getConfiguration());
1555 
1556     TableName tableName = TableName.valueOf("testConnectionRideOverClusterRestart");
1557     TEST_UTIL.createTable(tableName.getName(), new byte[][] {FAM_NAM}, config).close();
1558 
1559     Connection connection = ConnectionFactory.createConnection(config);
1560     Table table = connection.getTable(tableName);
1561 
1562     // this will cache the meta location and table's region location
1563     table.get(new Get(Bytes.toBytes("foo")));
1564 
1565     // restart HBase
1566     TEST_UTIL.shutdownMiniHBaseCluster();
1567     TEST_UTIL.restartHBaseCluster(2);
1568     // this should be able to discover new locations for meta and table's region
1569     table.get(new Get(Bytes.toBytes("foo")));
1570     TEST_UTIL.deleteTable(tableName);
1571     table.close();
1572     connection.close();
1573   }
1574 }