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