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