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 java.io.IOException;
22 
23 import org.apache.hadoop.hbase.*;
24 import org.apache.hadoop.hbase.testclassification.MediumTests;
25 import org.apache.hadoop.hbase.util.Bytes;
26 import org.apache.hadoop.hbase.util.PoolMap.PoolType;
27 import org.junit.*;
28 import org.junit.experimental.categories.Category;
29 import org.junit.runner.RunWith;
30 import org.junit.runners.Suite;
31 
32 /**
33  * Tests HTablePool.
34  */
35 @RunWith(Suite.class)
36 @Suite.SuiteClasses({TestHTablePool.TestHTableReusablePool.class, TestHTablePool.TestHTableThreadLocalPool.class})
37 @Category(MediumTests.class)
38 public class TestHTablePool {
39   private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
40   private final static String TABLENAME = "TestHTablePool";
41 
42   public abstract static class TestHTablePoolType {
43 
44     @BeforeClass
setUpBeforeClass()45     public static void setUpBeforeClass() throws Exception {
46       TEST_UTIL.startMiniCluster(1);
47       TEST_UTIL.createTable(TableName.valueOf(TABLENAME), HConstants.CATALOG_FAMILY);
48     }
49 
50     @AfterClass
tearDownAfterClass()51     public static void tearDownAfterClass() throws Exception {
52       TEST_UTIL.shutdownMiniCluster();
53     }
54 
getPoolType()55     protected abstract PoolType getPoolType();
56 
57     @Test
testTableWithStringName()58     public void testTableWithStringName() throws Exception {
59       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
60           Integer.MAX_VALUE, getPoolType());
61       String tableName = TABLENAME;
62 
63       // Request a table from an empty pool
64       Table table = pool.getTable(tableName);
65       Assert.assertNotNull(table);
66 
67       // Close table (returns table to the pool)
68       table.close();
69 
70       // Request a table of the same name
71       Table sameTable = pool.getTable(tableName);
72       Assert.assertSame(
73           ((HTablePool.PooledHTable) table).getWrappedTable(),
74           ((HTablePool.PooledHTable) sameTable).getWrappedTable());
75     }
76 
77     @Test
testTableWithByteArrayName()78     public void testTableWithByteArrayName() throws IOException {
79       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
80           Integer.MAX_VALUE, getPoolType());
81 
82       // Request a table from an empty pool
83       Table table = pool.getTable(TABLENAME);
84       Assert.assertNotNull(table);
85 
86       // Close table (returns table to the pool)
87       table.close();
88 
89       // Request a table of the same name
90       Table sameTable = pool.getTable(TABLENAME);
91       Assert.assertSame(
92           ((HTablePool.PooledHTable) table).getWrappedTable(),
93           ((HTablePool.PooledHTable) sameTable).getWrappedTable());
94     }
95 
96     @Test
testTablesWithDifferentNames()97     public void testTablesWithDifferentNames() throws IOException {
98       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
99           Integer.MAX_VALUE, getPoolType());
100       // We add the class to the table name as the HBase cluster is reused
101       //  during the tests: this gives naming unicity.
102       byte[] otherTable = Bytes.toBytes(
103         "OtherTable_" + getClass().getSimpleName()
104       );
105       TEST_UTIL.createTable(otherTable, HConstants.CATALOG_FAMILY);
106 
107       // Request a table from an empty pool
108       Table table1 = pool.getTable(TABLENAME);
109       Table table2 = pool.getTable(otherTable);
110       Assert.assertNotNull(table2);
111 
112       // Close tables (returns tables to the pool)
113       table1.close();
114       table2.close();
115 
116       // Request tables of the same names
117       Table sameTable1 = pool.getTable(TABLENAME);
118       Table sameTable2 = pool.getTable(otherTable);
119       Assert.assertSame(
120           ((HTablePool.PooledHTable) table1).getWrappedTable(),
121           ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
122       Assert.assertSame(
123           ((HTablePool.PooledHTable) table2).getWrappedTable(),
124           ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
125     }
126     @Test
testProxyImplementationReturned()127     public void testProxyImplementationReturned() {
128       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
129           Integer.MAX_VALUE);
130       String tableName = TABLENAME;// Request a table from
131                               // an
132                               // empty pool
133       Table table = pool.getTable(tableName);
134 
135       // Test if proxy implementation is returned
136       Assert.assertTrue(table instanceof HTablePool.PooledHTable);
137     }
138 
139     @Test
testDeprecatedUsagePattern()140     public void testDeprecatedUsagePattern() throws IOException {
141       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
142           Integer.MAX_VALUE);
143       String tableName = TABLENAME;// Request a table from
144                               // an
145                               // empty pool
146 
147       // get table will return proxy implementation
148       HTableInterface table = pool.getTable(tableName);
149 
150       // put back the proxy implementation instead of closing it
151       pool.putTable(table);
152 
153       // Request a table of the same name
154       Table sameTable = pool.getTable(tableName);
155 
156       // test no proxy over proxy created
157       Assert.assertSame(((HTablePool.PooledHTable) table).getWrappedTable(),
158           ((HTablePool.PooledHTable) sameTable).getWrappedTable());
159     }
160 
161     @Test
testReturnDifferentTable()162     public void testReturnDifferentTable() throws IOException {
163       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
164           Integer.MAX_VALUE);
165       String tableName = TABLENAME;// Request a table from
166                               // an
167                               // empty pool
168 
169       // get table will return proxy implementation
170       final Table table = pool.getTable(tableName);
171       HTableInterface alienTable = new HTable(TEST_UTIL.getConfiguration(),
172           TableName.valueOf(TABLENAME)) {
173         // implementation doesn't matter as long the table is not from
174         // pool
175       };
176       try {
177         // put the wrong table in pool
178         pool.putTable(alienTable);
179         Assert.fail("alien table accepted in pool");
180       } catch (IllegalArgumentException e) {
181         Assert.assertTrue("alien table rejected", true);
182       }
183     }
184 
185     @Test
testHTablePoolCloseTwice()186     public void testHTablePoolCloseTwice() throws Exception {
187       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
188           Integer.MAX_VALUE, getPoolType());
189       String tableName = TABLENAME;
190 
191       // Request a table from an empty pool
192       Table table = pool.getTable(tableName);
193       Assert.assertNotNull(table);
194       Assert.assertTrue(((HTablePool.PooledHTable) table).isOpen());
195       // Close table (returns table to the pool)
196       table.close();
197       // check if the table is closed
198       Assert.assertFalse(((HTablePool.PooledHTable) table).isOpen());
199       try {
200         table.close();
201         Assert.fail("Should not allow table to be closed twice");
202       } catch (IllegalStateException ex) {
203         Assert.assertTrue("table cannot be closed twice", true);
204       } finally {
205         pool.close();
206       }
207 
208     }
209 
210   }
211 
212   @Category(MediumTests.class)
213   public static class TestHTableReusablePool extends TestHTablePoolType {
214     @Override
getPoolType()215     protected PoolType getPoolType() {
216       return PoolType.Reusable;
217     }
218 
219     @Test
testTableWithMaxSize()220     public void testTableWithMaxSize() throws Exception {
221       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
222           getPoolType());
223 
224       // Request tables from an empty pool
225       Table table1 = pool.getTable(TABLENAME);
226       Table table2 = pool.getTable(TABLENAME);
227       Table table3 = pool.getTable(TABLENAME);
228 
229       // Close tables (returns tables to the pool)
230       table1.close();
231       table2.close();
232       // The pool should reject this one since it is already full
233       table3.close();
234 
235       // Request tables of the same name
236       Table sameTable1 = pool.getTable(TABLENAME);
237       Table sameTable2 = pool.getTable(TABLENAME);
238       Table sameTable3 = pool.getTable(TABLENAME);
239       Assert.assertSame(
240           ((HTablePool.PooledHTable) table1).getWrappedTable(),
241           ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
242       Assert.assertSame(
243           ((HTablePool.PooledHTable) table2).getWrappedTable(),
244           ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
245       Assert.assertNotSame(
246           ((HTablePool.PooledHTable) table3).getWrappedTable(),
247           ((HTablePool.PooledHTable) sameTable3).getWrappedTable());
248     }
249 
250     @Test
testCloseTablePool()251     public void testCloseTablePool() throws IOException {
252       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
253           getPoolType());
254       HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
255 
256       if (admin.tableExists(TABLENAME)) {
257         admin.disableTable(TABLENAME);
258         admin.deleteTable(TABLENAME);
259       }
260 
261       HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(TABLENAME));
262       tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
263       admin.createTable(tableDescriptor);
264 
265       // Request tables from an empty pool
266       Table[] tables = new Table[4];
267       for (int i = 0; i < 4; ++i) {
268         tables[i] = pool.getTable(TABLENAME);
269       }
270 
271       pool.closeTablePool(TABLENAME);
272 
273       for (int i = 0; i < 4; ++i) {
274         tables[i].close();
275       }
276 
277       Assert.assertEquals(4,
278           pool.getCurrentPoolSize(TABLENAME));
279 
280       pool.closeTablePool(TABLENAME);
281 
282       Assert.assertEquals(0,
283           pool.getCurrentPoolSize(TABLENAME));
284     }
285   }
286 
287   @Category(MediumTests.class)
288   public static class TestHTableThreadLocalPool extends TestHTablePoolType {
289     @Override
getPoolType()290     protected PoolType getPoolType() {
291       return PoolType.ThreadLocal;
292     }
293 
294     @Test
testTableWithMaxSize()295     public void testTableWithMaxSize() throws Exception {
296       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
297           getPoolType());
298 
299       // Request tables from an empty pool
300       Table table1 = pool.getTable(TABLENAME);
301       Table table2 = pool.getTable(TABLENAME);
302       Table table3 = pool.getTable(TABLENAME);
303 
304       // Close tables (returns tables to the pool)
305       table1.close();
306       table2.close();
307       // The pool should not reject this one since the number of threads
308       // <= 2
309       table3.close();
310 
311       // Request tables of the same name
312       Table sameTable1 = pool.getTable(TABLENAME);
313       Table sameTable2 = pool.getTable(TABLENAME);
314       Table sameTable3 = pool.getTable(TABLENAME);
315       Assert.assertSame(
316           ((HTablePool.PooledHTable) table3).getWrappedTable(),
317           ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
318       Assert.assertSame(
319           ((HTablePool.PooledHTable) table3).getWrappedTable(),
320           ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
321       Assert.assertSame(
322           ((HTablePool.PooledHTable) table3).getWrappedTable(),
323           ((HTablePool.PooledHTable) sameTable3).getWrappedTable());
324     }
325 
326     @Test
testCloseTablePool()327     public void testCloseTablePool() throws IOException {
328       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
329           getPoolType());
330       HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
331 
332       if (admin.tableExists(TABLENAME)) {
333         admin.disableTable(TABLENAME);
334         admin.deleteTable(TABLENAME);
335       }
336 
337       HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(TABLENAME));
338       tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
339       admin.createTable(tableDescriptor);
340 
341       // Request tables from an empty pool
342       Table[] tables = new Table[4];
343       for (int i = 0; i < 4; ++i) {
344         tables[i] = pool.getTable(TABLENAME);
345       }
346 
347       pool.closeTablePool(TABLENAME);
348 
349       for (int i = 0; i < 4; ++i) {
350         tables[i].close();
351       }
352 
353       Assert.assertEquals(1,
354           pool.getCurrentPoolSize(TABLENAME));
355 
356       pool.closeTablePool(TABLENAME);
357 
358       Assert.assertEquals(0,
359           pool.getCurrentPoolSize(TABLENAME));
360     }
361   }
362 
363 }
364