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 org.apache.hadoop.hbase.*;
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 byte[] TABLENAME = Bytes.toBytes("TestHTablePool");
41  
42    public abstract static class TestHTablePoolType {
43  
44      @BeforeClass
45      public static void setUpBeforeClass() throws Exception {
46        TEST_UTIL.startMiniCluster(1);
47        TEST_UTIL.createTable(TABLENAME, HConstants.CATALOG_FAMILY);
48      }
49  
50      @AfterClass
51      public static void tearDownAfterClass() throws Exception {
52        TEST_UTIL.shutdownMiniCluster();
53      }
54  
55      protected abstract PoolType getPoolType();
56  
57  		@Test
58  		public void testTableWithStringName() throws Exception {
59  			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
60  					Integer.MAX_VALUE, getPoolType());
61  			String tableName = Bytes.toString(TABLENAME);
62  
63  			// Request a table from an empty pool
64  			HTableInterface 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  			HTableInterface sameTable = pool.getTable(tableName);
72  			Assert.assertSame(
73  					((HTablePool.PooledHTable) table).getWrappedTable(),
74  					((HTablePool.PooledHTable) sameTable).getWrappedTable());
75  		}
76  
77  		@Test
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  			HTableInterface 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  			HTableInterface sameTable = pool.getTable(TABLENAME);
91  			Assert.assertSame(
92  					((HTablePool.PooledHTable) table).getWrappedTable(),
93  					((HTablePool.PooledHTable) sameTable).getWrappedTable());
94  		}
95  
96  		@Test
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 			HTableInterface table1 = pool.getTable(TABLENAME);
109 			HTableInterface 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 			HTableInterface sameTable1 = pool.getTable(TABLENAME);
118 			HTableInterface 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
127     public void testProxyImplementationReturned() {
128       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
129           Integer.MAX_VALUE);
130       String tableName = Bytes.toString(TABLENAME);// Request a table from
131                               // an
132                               // empty pool
133       HTableInterface table = pool.getTable(tableName);
134 
135       // Test if proxy implementation is returned
136       Assert.assertTrue(table instanceof HTablePool.PooledHTable);
137     }
138 
139     @Test
140     public void testDeprecatedUsagePattern() throws IOException {
141       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
142           Integer.MAX_VALUE);
143       String tableName = Bytes.toString(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       HTableInterface 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
162     public void testReturnDifferentTable() throws IOException {
163       HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
164           Integer.MAX_VALUE);
165       String tableName = Bytes.toString(TABLENAME);// Request a table from
166                               // an
167                               // empty pool
168 
169       // get table will return proxy implementation
170       final HTableInterface table = pool.getTable(tableName);
171       HTableInterface alienTable = new HTable(TEST_UTIL.getConfiguration(),
172           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 
186   @Category(MediumTests.class)
187 	public static class TestHTableReusablePool extends TestHTablePoolType {
188 		@Override
189 		protected PoolType getPoolType() {
190 			return PoolType.Reusable;
191 		}
192 
193 		@Test
194 		public void testTableWithMaxSize() throws Exception {
195 			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
196 					getPoolType());
197 
198 			// Request tables from an empty pool
199 			HTableInterface table1 = pool.getTable(TABLENAME);
200 			HTableInterface table2 = pool.getTable(TABLENAME);
201 			HTableInterface table3 = pool.getTable(TABLENAME);
202 
203 			// Close tables (returns tables to the pool)
204 			table1.close();
205 			table2.close();
206 			// The pool should reject this one since it is already full
207 			table3.close();
208 
209 			// Request tables of the same name
210 			HTableInterface sameTable1 = pool.getTable(TABLENAME);
211 			HTableInterface sameTable2 = pool.getTable(TABLENAME);
212 			HTableInterface sameTable3 = pool.getTable(TABLENAME);
213 			Assert.assertSame(
214 					((HTablePool.PooledHTable) table1).getWrappedTable(),
215 					((HTablePool.PooledHTable) sameTable1).getWrappedTable());
216 			Assert.assertSame(
217 					((HTablePool.PooledHTable) table2).getWrappedTable(),
218 					((HTablePool.PooledHTable) sameTable2).getWrappedTable());
219 			Assert.assertNotSame(
220 					((HTablePool.PooledHTable) table3).getWrappedTable(),
221 					((HTablePool.PooledHTable) sameTable3).getWrappedTable());
222 		}
223 
224 		@Test
225 		public void testCloseTablePool() throws IOException {
226 			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
227 					getPoolType());
228 			HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
229 
230 			if (admin.tableExists(TABLENAME)) {
231 				admin.disableTable(TABLENAME);
232 				admin.deleteTable(TABLENAME);
233 			}
234 
235 			HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
236 			tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
237 			admin.createTable(tableDescriptor);
238 
239 			// Request tables from an empty pool
240 			HTableInterface[] tables = new HTableInterface[4];
241 			for (int i = 0; i < 4; ++i) {
242 				tables[i] = pool.getTable(TABLENAME);
243 			}
244 
245 			pool.closeTablePool(TABLENAME);
246 
247 			for (int i = 0; i < 4; ++i) {
248 				tables[i].close();
249 			}
250 
251 			Assert.assertEquals(4,
252 					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
253 
254 			pool.closeTablePool(TABLENAME);
255 
256 			Assert.assertEquals(0,
257 					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
258 		}
259 	}
260 
261   @Category(MediumTests.class)
262 	public static class TestHTableThreadLocalPool extends TestHTablePoolType {
263 		@Override
264 		protected PoolType getPoolType() {
265 			return PoolType.ThreadLocal;
266 		}
267 
268 		@Test
269 		public void testTableWithMaxSize() throws Exception {
270 			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
271 					getPoolType());
272 
273 			// Request tables from an empty pool
274 			HTableInterface table1 = pool.getTable(TABLENAME);
275 			HTableInterface table2 = pool.getTable(TABLENAME);
276 			HTableInterface table3 = pool.getTable(TABLENAME);
277 
278 			// Close tables (returns tables to the pool)
279 			table1.close();
280 			table2.close();
281 			// The pool should not reject this one since the number of threads
282 			// <= 2
283 			table3.close();
284 
285 			// Request tables of the same name
286 			HTableInterface sameTable1 = pool.getTable(TABLENAME);
287 			HTableInterface sameTable2 = pool.getTable(TABLENAME);
288 			HTableInterface sameTable3 = pool.getTable(TABLENAME);
289 			Assert.assertSame(
290 					((HTablePool.PooledHTable) table3).getWrappedTable(),
291 					((HTablePool.PooledHTable) sameTable1).getWrappedTable());
292 			Assert.assertSame(
293 					((HTablePool.PooledHTable) table3).getWrappedTable(),
294 					((HTablePool.PooledHTable) sameTable2).getWrappedTable());
295 			Assert.assertSame(
296 					((HTablePool.PooledHTable) table3).getWrappedTable(),
297 					((HTablePool.PooledHTable) sameTable3).getWrappedTable());
298 		}
299 
300 		@Test
301 		public void testCloseTablePool() throws IOException {
302 			HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
303 					getPoolType());
304 			HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
305 
306 			if (admin.tableExists(TABLENAME)) {
307 				admin.disableTable(TABLENAME);
308 				admin.deleteTable(TABLENAME);
309 			}
310 
311 			HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
312 			tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
313 			admin.createTable(tableDescriptor);
314 
315 			// Request tables from an empty pool
316 			HTableInterface[] tables = new HTableInterface[4];
317 			for (int i = 0; i < 4; ++i) {
318 				tables[i] = pool.getTable(TABLENAME);
319 			}
320 
321 			pool.closeTablePool(TABLENAME);
322 
323 			for (int i = 0; i < 4; ++i) {
324 				tables[i].close();
325 			}
326 
327 			Assert.assertEquals(1,
328 					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
329 
330 			pool.closeTablePool(TABLENAME);
331 
332 			Assert.assertEquals(0,
333 					pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
334 		}
335 	}
336 
337   @org.junit.Rule
338   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
339     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
340 }