1   /*
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.client;
21  
22  import static org.junit.Assert.assertNotNull;
23  import static org.junit.Assert.assertNull;
24  import static org.junit.Assert.assertTrue;
25  import static org.junit.Assert.assertFalse;
26  import static org.junit.Assert.assertEquals;
27  
28  import java.lang.reflect.Field;
29  import java.util.ArrayList;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Random;
34  import java.util.Set;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.hadoop.conf.Configuration;
39  import org.apache.hadoop.hbase.HBaseConfiguration;
40  import org.apache.hadoop.hbase.HBaseTestingUtility;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.HRegionLocation;
43  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
44  import org.apache.hadoop.hbase.util.Bytes;
45  import org.junit.AfterClass;
46  import org.junit.Assert;
47  import org.junit.BeforeClass;
48  import org.junit.Test;
49  
50  /**
51   * This class is for testing HCM features
52   */
53  public class TestHCM {
54    private static final Log LOG = LogFactory.getLog(TestHCM.class);
55    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
56    private static final byte[] TABLE_NAME = Bytes.toBytes("test");
57    private static final byte[] FAM_NAM = Bytes.toBytes("f");
58    private static final byte[] ROW = Bytes.toBytes("bbb");
59  
60    @BeforeClass
61    public static void setUpBeforeClass() throws Exception {
62      TEST_UTIL.startMiniCluster(1);
63    }
64  
65    @AfterClass public static void tearDownAfterClass() throws Exception {
66      TEST_UTIL.shutdownMiniCluster();
67    }
68  
69    /**
70     * @throws InterruptedException 
71     * @throws IllegalAccessException 
72     * @throws NoSuchFieldException 
73     * @throws ZooKeeperConnectionException 
74     * @throws IllegalArgumentException 
75     * @throws SecurityException 
76     * @see https://issues.apache.org/jira/browse/HBASE-2925
77     */
78    // Disabling.  Of course this test will OOME using new Configuration each time
79    // St.Ack 20110428
80    // @Test
81    public void testManyNewConnectionsDoesnotOOME()
82    throws SecurityException, IllegalArgumentException,
83    ZooKeeperConnectionException, NoSuchFieldException, IllegalAccessException,
84    InterruptedException {
85      createNewConfigurations();
86    }
87  
88    private static Random _randy = new Random();
89  
90    public static void createNewConfigurations() throws SecurityException,
91    IllegalArgumentException, NoSuchFieldException,
92    IllegalAccessException, InterruptedException, ZooKeeperConnectionException {
93      HConnection last = null;
94      for (int i = 0; i <= (HConnectionManager.MAX_CACHED_HBASE_INSTANCES * 2); i++) {
95        // set random key to differentiate the connection from previous ones
96        Configuration configuration = HBaseConfiguration.create();
97        configuration.set("somekey", String.valueOf(_randy.nextInt()));
98        System.out.println("Hash Code: " + configuration.hashCode());
99        HConnection connection = HConnectionManager.getConnection(configuration);
100       if (last != null) {
101         if (last == connection) {
102           System.out.println("!! Got same connection for once !!");
103         }
104       }
105       // change the configuration once, and the cached connection is lost forever:
106       //      the hashtable holding the cache won't be able to find its own keys
107       //      to remove them, so the LRU strategy does not work.
108       configuration.set("someotherkey", String.valueOf(_randy.nextInt()));
109       last = connection;
110       LOG.info("Cache Size: " + getHConnectionManagerCacheSize());
111       Thread.sleep(100);
112     }
113     Assert.assertEquals(1,
114       getHConnectionManagerCacheSize());
115   }
116 
117   private static int getHConnectionManagerCacheSize()
118   throws SecurityException, NoSuchFieldException,
119   IllegalArgumentException, IllegalAccessException {
120     Field cacheField =
121       HConnectionManager.class.getDeclaredField("HBASE_INSTANCES");
122     cacheField.setAccessible(true);
123     Map<?, ?> cache = (Map<?, ?>) cacheField.get(null);
124     return cache.size();
125   }
126 
127   /**
128    * Test that when we delete a location using the first row of a region
129    * that we really delete it.
130    * @throws Exception
131    */
132   @Test
133   public void testRegionCaching() throws Exception{
134     HTable table = TEST_UTIL.createTable(TABLE_NAME, FAM_NAM);
135     TEST_UTIL.createMultiRegions(table, FAM_NAM);
136     Put put = new Put(ROW);
137     put.add(FAM_NAM, ROW, ROW);
138     table.put(put);
139     HConnectionManager.HConnectionImplementation conn =
140         (HConnectionManager.HConnectionImplementation)table.getConnection();
141     assertNotNull(conn.getCachedLocation(TABLE_NAME, ROW));
142     conn.deleteCachedLocation(TABLE_NAME, ROW);
143     HRegionLocation rl = conn.getCachedLocation(TABLE_NAME, ROW);
144     assertNull("What is this location?? " + rl, rl);
145   }
146 
147   /**
148    * Make sure that {@link HConfiguration} instances that are essentially the
149    * same map to the same {@link HConnection} instance.
150    */
151   @Test
152   public void testConnectionSameness() throws Exception {
153     HConnection previousConnection = null;
154     for (int i = 0; i < 2; i++) {
155       // set random key to differentiate the connection from previous ones
156       Configuration configuration = TEST_UTIL.getConfiguration();
157       configuration.set("some_key", String.valueOf(_randy.nextInt()));
158       LOG.info("The hash code of the current configuration is: "
159           + configuration.hashCode());
160       HConnection currentConnection = HConnectionManager
161           .getConnection(configuration);
162       if (previousConnection != null) {
163         assertTrue(
164             "Did not get the same connection even though its key didn't change",
165             previousConnection == currentConnection);
166       }
167       previousConnection = currentConnection;
168       // change the configuration, so that it is no longer reachable from the
169       // client's perspective. However, since its part of the LRU doubly linked
170       // list, it will eventually get thrown out, at which time it should also
171       // close the corresponding {@link HConnection}.
172       configuration.set("other_key", String.valueOf(_randy.nextInt()));
173     }
174   }
175 
176   /**
177    * Makes sure that there is no leaking of
178    * {@link HConnectionManager.TableServers} in the {@link HConnectionManager}
179    * class.
180    */
181   @Test
182   public void testConnectionUniqueness() throws Exception {
183     int zkmaxconnections = TEST_UTIL.getConfiguration().
184       getInt(HConstants.ZOOKEEPER_MAX_CLIENT_CNXNS,
185         HConstants.DEFAULT_ZOOKEPER_MAX_CLIENT_CNXNS);
186     // Test up to a max that is < the maximum number of zk connections.  If we
187     // go above zk connections, we just fall into cycle where we are failing
188     // to set up a session and test runs for a long time.
189     int maxConnections = Math.min(zkmaxconnections - 1, 20);
190     List<HConnection> connections = new ArrayList<HConnection>();
191     HConnection previousConnection = null;
192     try {
193       for (int i = 0; i < maxConnections; i++) {
194         // set random key to differentiate the connection from previous ones
195         Configuration configuration = TEST_UTIL.getConfiguration();
196         configuration.set("some_key", String.valueOf(_randy.nextInt()));
197         configuration.set(HConstants.HBASE_CLIENT_INSTANCE_ID,
198             String.valueOf(_randy.nextInt()));
199         LOG.info("The hash code of the current configuration is: "
200             + configuration.hashCode());
201         HConnection currentConnection =
202           HConnectionManager.getConnection(configuration);
203         if (previousConnection != null) {
204           assertTrue("Got the same connection even though its key changed!",
205               previousConnection != currentConnection);
206         }
207         // change the configuration, so that it is no longer reachable from the
208         // client's perspective. However, since its part of the LRU doubly linked
209         // list, it will eventually get thrown out, at which time it should also
210         // close the corresponding {@link HConnection}.
211         configuration.set("other_key", String.valueOf(_randy.nextInt()));
212 
213         previousConnection = currentConnection;
214         LOG.info("The current HConnectionManager#HBASE_INSTANCES cache size is: "
215             + getHConnectionManagerCacheSize());
216         Thread.sleep(50);
217       }
218     } finally {
219       for (HConnection c: connections) {
220         // Clean up connections made so we don't interfere w/ subsequent tests.
221         HConnectionManager.deleteConnection(c.getConfiguration(), true);
222       }
223     }
224   }
225 
226   @Test
227   public void testClosing() throws Exception {
228     Configuration configuration = TEST_UTIL.getConfiguration();
229     configuration.set(HConstants.HBASE_CLIENT_INSTANCE_ID,
230         String.valueOf(_randy.nextInt()));
231 
232     HConnection c1 = HConnectionManager.createConnection(configuration);
233     HConnection c2 = HConnectionManager.createConnection(configuration);
234 
235     HConnection c3 = HConnectionManager.getConnection(configuration);
236     HConnection c4 = HConnectionManager.getConnection(configuration);
237     assertTrue(c3 == c4);
238 
239     c1.close();
240     assertTrue(c1.isClosed());
241     assertFalse(c2.isClosed());
242     assertFalse(c3.isClosed());
243 
244     c3.close();
245     // still a reference left
246     assertFalse(c3.isClosed());
247     c3.close();
248     assertTrue(c3.isClosed());
249     // c3 was removed from the cache
250     assertTrue(HConnectionManager.getConnection(configuration) != c3);
251 
252     assertFalse(c2.isClosed());
253   }
254 
255   /**
256    * Trivial test to verify that nobody messes with
257    * {@link HConnectionManager#createConnection(Configuration)}
258    */
259   @Test
260   public void testCreateConnection() throws Exception {
261     Configuration configuration = TEST_UTIL.getConfiguration();
262     HConnection c1 = HConnectionManager.createConnection(configuration);
263     HConnection c2 = HConnectionManager.createConnection(configuration);
264     // created from the same configuration, yet they are different
265     assertTrue(c1 != c2);
266     assertTrue(c1.getConfiguration() == c2.getConfiguration());
267     // make sure these were not cached
268     HConnection c3 = HConnectionManager.getConnection(configuration);
269     assertTrue(c1 != c3);
270     assertTrue(c2 != c3);
271   }
272 }