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