You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2011/07/07 23:58:35 UTC
svn commit: r1144068 - in /hbase/trunk: CHANGES.txt
src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java
Author: stack
Date: Thu Jul 7 21:58:35 2011
New Revision: 1144068
URL: http://svn.apache.org/viewvc?rev=1144068&view=rev
Log:
HBASE-4054 Usability improvement to HTablePool
Modified:
hbase/trunk/CHANGES.txt
hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java
Modified: hbase/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/trunk/CHANGES.txt?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/CHANGES.txt (original)
+++ hbase/trunk/CHANGES.txt Thu Jul 7 21:58:35 2011
@@ -311,6 +311,7 @@ Release 0.91.0 - Unreleased
HBASE-4048 [Coprocessors] Support configuration of coprocessor at load time
HBASE-3240 Improve documentation of importtsv and bulk loads.
(Aaron T. Myers via todd)
+ HBASE-4054 Usability improvement to HTablePool (Daniel Iancu)
TASKS
HBASE-3559 Move report of split to master OFF the heartbeat channel
Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HTablePool.java Thu Jul 7 21:58:35 2011
@@ -22,25 +22,39 @@ package org.apache.hadoop.hbase.client;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.coprocessor.Batch;
+import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.PoolMap;
import org.apache.hadoop.hbase.util.PoolMap.PoolType;
/**
- * A simple pool of HTable instances.<p>
- *
- * Each HTablePool acts as a pool for all tables. To use, instantiate an
+ * A simple pool of HTable instances.
+ *
+ * Each HTablePool acts as a pool for all tables. To use, instantiate an
* HTablePool and use {@link #getTable(String)} to get an HTable from the pool.
- * Once you are done with it, return it to the pool with {@link #putTable(HTableInterface)}.
+ *
+ * This method is not needed anymore, clients should call
+ * HTableInterface.close() rather than returning the tables to the pool
+ *
+ * Once you are done with it, close your instance of {@link HTableInterface}
+ * by calling {@link HTableInterface#close()} rather than returning the tables
+ * to the pool with (deprecated) {@link #putTable(HTableInterface)}.
*
- * <p>A pool can be created with a <i>maxSize</i> which defines the most HTable
- * references that will ever be retained for each table. Otherwise the default
+ * <p>
+ * A pool can be created with a <i>maxSize</i> which defines the most HTable
+ * references that will ever be retained for each table. Otherwise the default
* is {@link Integer#MAX_VALUE}.
- *
- * <p>Pool will manage its own cluster to the cluster. See {@link HConnectionManager}.
+ *
+ * <p>
+ * Pool will manage its own connections to the cluster. See
+ * {@link HConnectionManager}.
*/
public class HTablePool implements Closeable {
private final PoolMap<String, HTableInterface> tables;
@@ -50,7 +64,7 @@ public class HTablePool implements Close
private final HTableInterfaceFactory tableFactory;
/**
- * Default Constructor. Default HBaseConfiguration and no limit on pool size.
+ * Default Constructor. Default HBaseConfiguration and no limit on pool size.
*/
public HTablePool() {
this(HBaseConfiguration.create(), Integer.MAX_VALUE);
@@ -58,8 +72,11 @@ public class HTablePool implements Close
/**
* Constructor to set maximum versions and use the specified configuration.
- * @param config configuration
- * @param maxSize maximum number of references to keep for each table
+ *
+ * @param config
+ * configuration
+ * @param maxSize
+ * maximum number of references to keep for each table
*/
public HTablePool(final Configuration config, final int maxSize) {
this(config, maxSize, null, null);
@@ -68,7 +85,7 @@ public class HTablePool implements Close
/**
* Constructor to set maximum versions and use the specified configuration and
* table factory.
- *
+ *
* @param config
* configuration
* @param maxSize
@@ -84,7 +101,7 @@ public class HTablePool implements Close
/**
* Constructor to set maximum versions and use the specified configuration and
* pool type.
- *
+ *
* @param config
* configuration
* @param maxSize
@@ -106,7 +123,7 @@ public class HTablePool implements Close
* {@link PoolType#Reusable} and {@link PoolType#ThreadLocal}. If the pool
* type is null or not one of those two values, then it will default to
* {@link PoolType#Reusable}.
- *
+ *
* @param config
* configuration
* @param maxSize
@@ -121,9 +138,10 @@ public class HTablePool implements Close
final HTableInterfaceFactory tableFactory, PoolType poolType) {
// Make a new configuration instance so I can safely cleanup when
// done with the pool.
- this.config = config == null? new Configuration(): config;
+ this.config = config == null ? new Configuration() : config;
this.maxSize = maxSize;
- this.tableFactory = tableFactory == null? new HTableFactory(): tableFactory;
+ this.tableFactory = tableFactory == null ? new HTableFactory()
+ : tableFactory;
if (poolType == null) {
this.poolType = PoolType.Reusable;
} else {
@@ -137,47 +155,106 @@ public class HTablePool implements Close
break;
}
}
- this.tables = new PoolMap<String, HTableInterface>(this.poolType, this.maxSize);
+ this.tables = new PoolMap<String, HTableInterface>(this.poolType,
+ this.maxSize);
}
/**
- * Get a reference to the specified table from the pool.<p>
- *
- * Create a new one if one is not available.
- * @param tableName table name
+ * Get a reference to the specified table from the pool.
+ * <p>
+ * <p/>
+ *
+ * @param tableName
+ * table name
* @return a reference to the specified table
- * @throws RuntimeException if there is a problem instantiating the HTable
+ * @throws RuntimeException
+ * if there is a problem instantiating the HTable
*/
public HTableInterface getTable(String tableName) {
+ // call the old getTable implementation renamed to findOrCreateTable
+ HTableInterface table = findOrCreateTable(tableName);
+ // return a proxy table so when user closes the proxy, the actual table
+ // will be returned to the pool
+ return new PooledHTable(table);
+
+ }
+
+ /**
+ * Get a reference to the specified table from the pool.
+ * <p>
+ *
+ * Create a new one if one is not available.
+ *
+ * @param tableName
+ * table name
+ * @return a reference to the specified table
+ * @throws RuntimeException
+ * if there is a problem instantiating the HTable
+ */
+ private HTableInterface findOrCreateTable(String tableName) {
HTableInterface table = tables.get(tableName);
- if(table == null) {
+ if (table == null) {
table = createHTable(tableName);
}
return table;
}
/**
- * Get a reference to the specified table from the pool.<p>
- *
+ * Get a reference to the specified table from the pool.
+ * <p>
+ *
* Create a new one if one is not available.
- * @param tableName table name
+ *
+ * @param tableName
+ * table name
* @return a reference to the specified table
- * @throws RuntimeException if there is a problem instantiating the HTable
+ * @throws RuntimeException
+ * if there is a problem instantiating the HTable
*/
- public HTableInterface getTable(byte [] tableName) {
+ public HTableInterface getTable(byte[] tableName) {
return getTable(Bytes.toString(tableName));
}
/**
- * Puts the specified HTable back into the pool.<p>
- *
- * If the pool already contains <i>maxSize</i> references to the table,
- * then the table instance gets closed after flushing buffered edits.
- * @param table table
+ * This method is not needed anymore, clients should call
+ * HTableInterface.close() rather than returning the tables to the pool
+ *
+ * @param table
+ * the proxy table user got from pool
+ * @deprecated
*/
public void putTable(HTableInterface table) throws IOException {
+ // we need to be sure nobody puts a proxy implementation in the pool
+ // but if the client code is not updated
+ // and it will continue to call putTable() instead of calling close()
+ // then we need to return the wrapped table to the pool instead of the
+ // proxy
+ // table
+ if (table instanceof PooledHTable) {
+ returnTable(((PooledHTable) table).getWrappedTable());
+ } else {
+ // normally this should not happen if clients pass back the same
+ // table
+ // object they got from the pool
+ // but if it happens then it's better to reject it
+ throw new IllegalArgumentException("not a pooled table: " + table);
+ }
+ }
+
+ /**
+ * Puts the specified HTable back into the pool.
+ * <p>
+ *
+ * If the pool already contains <i>maxSize</i> references to the table, then
+ * the table instance gets closed after flushing buffered edits.
+ *
+ * @param table
+ * table
+ */
+ private void returnTable(HTableInterface table) throws IOException {
+ // this is the old putTable method renamed and made private
String tableName = Bytes.toString(table.getTableName());
- if(tables.size(tableName) >= maxSize) {
+ if (tables.size(tableName) >= maxSize) {
// release table instance since we're not reusing it
this.tableFactory.releaseHTableInterface(table);
return;
@@ -186,16 +263,18 @@ public class HTablePool implements Close
}
protected HTableInterface createHTable(String tableName) {
- return this.tableFactory.createHTableInterface(config, Bytes.toBytes(tableName));
+ return this.tableFactory.createHTableInterface(config,
+ Bytes.toBytes(tableName));
}
/**
- * Closes all the HTable instances , belonging to the given table, in the table pool.
+ * Closes all the HTable instances , belonging to the given table, in the
+ * table pool.
* <p>
* Note: this is a 'shutdown' of the given table pool and different from
* {@link #putTable(HTableInterface)}, that is used to return the table
* instance to the pool for future re-use.
- *
+ *
* @param tableName
*/
public void closeTablePool(final String tableName) throws IOException {
@@ -210,7 +289,7 @@ public class HTablePool implements Close
/**
* See {@link #closeTablePool(String)}.
- *
+ *
* @param tableName
*/
public void closeTablePool(final byte[] tableName) throws IOException {
@@ -218,7 +297,8 @@ public class HTablePool implements Close
}
/**
- * Closes all the HTable instances , belonging to all tables in the table pool.
+ * Closes all the HTable instances , belonging to all tables in the table
+ * pool.
* <p>
* Note: this is a 'shutdown' of all the table pools.
*/
@@ -231,4 +311,195 @@ public class HTablePool implements Close
int getCurrentPoolSize(String tableName) {
return tables.size(tableName);
}
+
+ /**
+ * A proxy class that implements HTableInterface.close method to return the
+ * wrapped table back to the table pool
+ *
+ */
+ class PooledHTable implements HTableInterface {
+
+ private HTableInterface table; // actual table implementation
+
+ public PooledHTable(HTableInterface table) {
+ this.table = table;
+ }
+
+ @Override
+ public byte[] getTableName() {
+ return table.getTableName();
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ return table.getConfiguration();
+ }
+
+ @Override
+ public HTableDescriptor getTableDescriptor() throws IOException {
+ return table.getTableDescriptor();
+ }
+
+ @Override
+ public boolean exists(Get get) throws IOException {
+ return table.exists(get);
+ }
+
+ @Override
+ public void batch(List<Row> actions, Object[] results) throws IOException,
+ InterruptedException {
+ table.batch(actions, results);
+ }
+
+ @Override
+ public Object[] batch(List<Row> actions) throws IOException,
+ InterruptedException {
+ return table.batch(actions);
+ }
+
+ @Override
+ public Result get(Get get) throws IOException {
+ return table.get(get);
+ }
+
+ @Override
+ public Result[] get(List<Get> gets) throws IOException {
+ return table.get(gets);
+ }
+
+ @Override
+ public Result getRowOrBefore(byte[] row, byte[] family) throws IOException {
+ return table.getRowOrBefore(row, family);
+ }
+
+ @Override
+ public ResultScanner getScanner(Scan scan) throws IOException {
+ return table.getScanner(scan);
+ }
+
+ @Override
+ public ResultScanner getScanner(byte[] family) throws IOException {
+ return table.getScanner(family);
+ }
+
+ @Override
+ public ResultScanner getScanner(byte[] family, byte[] qualifier)
+ throws IOException {
+ return table.getScanner(family, qualifier);
+ }
+
+ @Override
+ public void put(Put put) throws IOException {
+ table.put(put);
+ }
+
+ @Override
+ public void put(List<Put> puts) throws IOException {
+ table.put(puts);
+ }
+
+ @Override
+ public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
+ byte[] value, Put put) throws IOException {
+ return table.checkAndPut(row, family, qualifier, value, put);
+ }
+
+ @Override
+ public void delete(Delete delete) throws IOException {
+ table.delete(delete);
+ }
+
+ @Override
+ public void delete(List<Delete> deletes) throws IOException {
+ table.delete(deletes);
+ }
+
+ @Override
+ public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
+ byte[] value, Delete delete) throws IOException {
+ return table.checkAndDelete(row, family, qualifier, value, delete);
+ }
+
+ @Override
+ public Result increment(Increment increment) throws IOException {
+ return table.increment(increment);
+ }
+
+ @Override
+ public long incrementColumnValue(byte[] row, byte[] family,
+ byte[] qualifier, long amount) throws IOException {
+ return table.incrementColumnValue(row, family, qualifier, amount);
+ }
+
+ @Override
+ public long incrementColumnValue(byte[] row, byte[] family,
+ byte[] qualifier, long amount, boolean writeToWAL) throws IOException {
+ return table.incrementColumnValue(row, family, qualifier, amount,
+ writeToWAL);
+ }
+
+ @Override
+ public boolean isAutoFlush() {
+ return table.isAutoFlush();
+ }
+
+ @Override
+ public void flushCommits() throws IOException {
+ table.flushCommits();
+ }
+
+ /**
+ * Returns the actual table back to the pool
+ *
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ returnTable(table);
+ }
+
+ @Override
+ public RowLock lockRow(byte[] row) throws IOException {
+ return table.lockRow(row);
+ }
+
+ @Override
+ public void unlockRow(RowLock rl) throws IOException {
+ table.unlockRow(rl);
+ }
+
+ @Override
+ public <T extends CoprocessorProtocol> T coprocessorProxy(
+ Class<T> protocol, byte[] row) {
+ return table.coprocessorProxy(protocol, row);
+ }
+
+ @Override
+ public <T extends CoprocessorProtocol, R> Map<byte[], R> coprocessorExec(
+ Class<T> protocol, byte[] startKey, byte[] endKey,
+ Batch.Call<T, R> callable) throws IOException, Throwable {
+ return table.coprocessorExec(protocol, startKey, endKey, callable);
+ }
+
+ @Override
+ public <T extends CoprocessorProtocol, R> void coprocessorExec(
+ Class<T> protocol, byte[] startKey, byte[] endKey,
+ Batch.Call<T, R> callable, Batch.Callback<R> callback)
+ throws IOException, Throwable {
+ table.coprocessorExec(protocol, startKey, endKey, callable, callback);
+ }
+
+ @Override
+ public String toString() {
+ return "PooledHTable{" + ", table=" + table + '}';
+ }
+
+ /**
+ * Expose the wrapped HTable to tests in the same package
+ *
+ * @return wrapped htable
+ */
+ HTableInterface getWrappedTable() {
+ return table;
+ }
+ }
}
Modified: hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java?rev=1144068&r1=1144067&r2=1144068&view=diff
==============================================================================
--- hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java (original)
+++ hbase/trunk/src/test/java/org/apache/hadoop/hbase/client/TestHTablePool.java Thu Jul 7 21:58:35 2011
@@ -37,221 +37,300 @@ import org.junit.Test;
* Tests HTablePool.
*/
public class TestHTablePool {
- private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
- private final static byte[] TABLENAME = Bytes.toBytes("TestHTablePool");
+ private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+ private final static byte[] TABLENAME = Bytes.toBytes("TestHTablePool");
- public abstract static class TestHTablePoolType extends TestCase {
- protected void setUp() throws Exception {
- TEST_UTIL.startMiniCluster(1);
- TEST_UTIL.createTable(TABLENAME, HConstants.CATALOG_FAMILY);
- }
-
- protected void tearDown() throws IOException {
- TEST_UTIL.shutdownMiniCluster();
- }
-
- protected abstract PoolType getPoolType();
-
- @Test
- public void testTableWithStringName() throws Exception {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
- Integer.MAX_VALUE, getPoolType());
- String tableName = Bytes.toString(TABLENAME);
-
- // Request a table from an empty pool
- HTableInterface table = pool.getTable(tableName);
- Assert.assertNotNull(table);
-
- // Return the table to the pool
- pool.putTable(table);
-
- // Request a table of the same name
- HTableInterface sameTable = pool.getTable(tableName);
- Assert.assertSame(table, sameTable);
- }
-
- @Test
- public void testTableWithByteArrayName() throws IOException {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
- Integer.MAX_VALUE, getPoolType());
-
- // Request a table from an empty pool
- HTableInterface table = pool.getTable(TABLENAME);
- Assert.assertNotNull(table);
-
- // Return the table to the pool
- pool.putTable(table);
-
- // Request a table of the same name
- HTableInterface sameTable = pool.getTable(TABLENAME);
- Assert.assertSame(table, sameTable);
- }
-
- @Test
- public void testTablesWithDifferentNames() throws IOException {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
- Integer.MAX_VALUE, getPoolType());
- byte[] otherTable = Bytes.toBytes("OtherTable");
- TEST_UTIL.createTable(otherTable, HConstants.CATALOG_FAMILY);
-
- // Request a table from an empty pool
- HTableInterface table1 = pool.getTable(TABLENAME);
- HTableInterface table2 = pool.getTable(otherTable);
- Assert.assertNotNull(table2);
-
- // Return the tables to the pool
- pool.putTable(table1);
- pool.putTable(table2);
-
- // Request tables of the same names
- HTableInterface sameTable1 = pool.getTable(TABLENAME);
- HTableInterface sameTable2 = pool.getTable(otherTable);
- Assert.assertSame(table1, sameTable1);
- Assert.assertSame(table2, sameTable2);
- }
-
- }
-
- public static class TestHTableReusablePool extends TestHTablePoolType {
- @Override
- protected PoolType getPoolType() {
- return PoolType.Reusable;
- }
-
- @Test
- public void testTableWithMaxSize() throws Exception {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
- getPoolType());
-
- // Request tables from an empty pool
- HTableInterface table1 = pool.getTable(TABLENAME);
- HTableInterface table2 = pool.getTable(TABLENAME);
- HTableInterface table3 = pool.getTable(TABLENAME);
-
- // Return the tables to the pool
- pool.putTable(table1);
- pool.putTable(table2);
- // The pool should reject this one since it is already full
- pool.putTable(table3);
-
- // Request tables of the same name
- HTableInterface sameTable1 = pool.getTable(TABLENAME);
- HTableInterface sameTable2 = pool.getTable(TABLENAME);
- HTableInterface sameTable3 = pool.getTable(TABLENAME);
- Assert.assertSame(table1, sameTable1);
- Assert.assertSame(table2, sameTable2);
- Assert.assertNotSame(table3, sameTable3);
- }
-
- @Test
- public void testCloseTablePool() throws IOException {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
- getPoolType());
- HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
-
- if (admin.tableExists(TABLENAME)) {
- admin.disableTable(TABLENAME);
- admin.deleteTable(TABLENAME);
- }
-
- HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
- tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
- admin.createTable(tableDescriptor);
-
- // Request tables from an empty pool
- HTableInterface[] tables = new HTableInterface[4];
- for (int i = 0; i < 4; ++i) {
- tables[i] = pool.getTable(TABLENAME);
- }
-
- pool.closeTablePool(TABLENAME);
-
- for (int i = 0; i < 4; ++i) {
- pool.putTable(tables[i]);
- }
-
- Assert
- .assertEquals(4, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-
- pool.closeTablePool(TABLENAME);
-
- Assert
- .assertEquals(0, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
- }
- }
-
- public static class TestHTableThreadLocalPool extends TestHTablePoolType {
- @Override
- protected PoolType getPoolType() {
- return PoolType.ThreadLocal;
- }
-
- @Test
- public void testTableWithMaxSize() throws Exception {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
- getPoolType());
-
- // Request tables from an empty pool
- HTableInterface table1 = pool.getTable(TABLENAME);
- HTableInterface table2 = pool.getTable(TABLENAME);
- HTableInterface table3 = pool.getTable(TABLENAME);
-
- // Return the tables to the pool
- pool.putTable(table1);
- pool.putTable(table2);
- // The pool should not reject this one since the number of threads <= 2
- pool.putTable(table3);
-
- // Request tables of the same name
- HTableInterface sameTable1 = pool.getTable(TABLENAME);
- HTableInterface sameTable2 = pool.getTable(TABLENAME);
- HTableInterface sameTable3 = pool.getTable(TABLENAME);
- Assert.assertSame(table3, sameTable1);
- Assert.assertSame(table3, sameTable2);
- Assert.assertSame(table3, sameTable3);
- }
-
- @Test
- public void testCloseTablePool() throws IOException {
- HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
- getPoolType());
- HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
-
- if (admin.tableExists(TABLENAME)) {
- admin.disableTable(TABLENAME);
- admin.deleteTable(TABLENAME);
- }
-
- HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
- tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
- admin.createTable(tableDescriptor);
-
- // Request tables from an empty pool
- HTableInterface[] tables = new HTableInterface[4];
- for (int i = 0; i < 4; ++i) {
- tables[i] = pool.getTable(TABLENAME);
- }
-
- pool.closeTablePool(TABLENAME);
-
- for (int i = 0; i < 4; ++i) {
- pool.putTable(tables[i]);
- }
-
- Assert
- .assertEquals(1, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
-
- pool.closeTablePool(TABLENAME);
-
- Assert
- .assertEquals(0, pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
- }
- }
-
- public static junit.framework.Test suite() {
- TestSuite suite = new TestSuite();
- suite.addTestSuite(TestHTableReusablePool.class);
- suite.addTestSuite(TestHTableThreadLocalPool.class);
- return suite;
- }
+ public abstract static class TestHTablePoolType extends TestCase {
+ protected void setUp() throws Exception {
+ TEST_UTIL.startMiniCluster(1);
+ TEST_UTIL.createTable(TABLENAME, HConstants.CATALOG_FAMILY);
+ }
+
+ protected void tearDown() throws IOException {
+ TEST_UTIL.shutdownMiniCluster();
+ }
+
+ protected abstract PoolType getPoolType();
+
+ @Test
+ public void testTableWithStringName() throws Exception {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE, getPoolType());
+ String tableName = Bytes.toString(TABLENAME);
+
+ // Request a table from an empty pool
+ HTableInterface table = pool.getTable(tableName);
+ Assert.assertNotNull(table);
+
+ // Close table (returns table to the pool)
+ table.close();
+
+ // Request a table of the same name
+ HTableInterface sameTable = pool.getTable(tableName);
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable).getWrappedTable());
+ }
+
+ @Test
+ public void testTableWithByteArrayName() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE, getPoolType());
+
+ // Request a table from an empty pool
+ HTableInterface table = pool.getTable(TABLENAME);
+ Assert.assertNotNull(table);
+
+ // Close table (returns table to the pool)
+ table.close();
+
+ // Request a table of the same name
+ HTableInterface sameTable = pool.getTable(TABLENAME);
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable).getWrappedTable());
+ }
+
+ @Test
+ public void testTablesWithDifferentNames() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE, getPoolType());
+ byte[] otherTable = Bytes.toBytes("OtherTable");
+ TEST_UTIL.createTable(otherTable, HConstants.CATALOG_FAMILY);
+
+ // Request a table from an empty pool
+ HTableInterface table1 = pool.getTable(TABLENAME);
+ HTableInterface table2 = pool.getTable(otherTable);
+ Assert.assertNotNull(table2);
+
+ // Close tables (returns tables to the pool)
+ table1.close();
+ table2.close();
+
+ // Request tables of the same names
+ HTableInterface sameTable1 = pool.getTable(TABLENAME);
+ HTableInterface sameTable2 = pool.getTable(otherTable);
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table1).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table2).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+ }
+
+ }
+
+ public static class TestHTableReusablePool extends TestHTablePoolType {
+ @Override
+ protected PoolType getPoolType() {
+ return PoolType.Reusable;
+ }
+
+ @Test
+ public void testTableWithMaxSize() throws Exception {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
+ getPoolType());
+
+ // Request tables from an empty pool
+ HTableInterface table1 = pool.getTable(TABLENAME);
+ HTableInterface table2 = pool.getTable(TABLENAME);
+ HTableInterface table3 = pool.getTable(TABLENAME);
+
+ // Close tables (returns tables to the pool)
+ table1.close();
+ table2.close();
+ // The pool should reject this one since it is already full
+ table3.close();
+
+ // Request tables of the same name
+ HTableInterface sameTable1 = pool.getTable(TABLENAME);
+ HTableInterface sameTable2 = pool.getTable(TABLENAME);
+ HTableInterface sameTable3 = pool.getTable(TABLENAME);
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table1).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table2).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+ Assert.assertNotSame(
+ ((HTablePool.PooledHTable) table3).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable3).getWrappedTable());
+ }
+
+ @Test
+ public void testCloseTablePool() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
+ getPoolType());
+ HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+
+ if (admin.tableExists(TABLENAME)) {
+ admin.disableTable(TABLENAME);
+ admin.deleteTable(TABLENAME);
+ }
+
+ HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
+ tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
+ admin.createTable(tableDescriptor);
+
+ // Request tables from an empty pool
+ HTableInterface[] tables = new HTableInterface[4];
+ for (int i = 0; i < 4; ++i) {
+ tables[i] = pool.getTable(TABLENAME);
+ }
+
+ pool.closeTablePool(TABLENAME);
+
+ for (int i = 0; i < 4; ++i) {
+ tables[i].close();
+ }
+
+ Assert.assertEquals(4,
+ pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+
+ pool.closeTablePool(TABLENAME);
+
+ Assert.assertEquals(0,
+ pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+ }
+ }
+
+ public static class TestHTableThreadLocalPool extends TestHTablePoolType {
+ @Override
+ protected PoolType getPoolType() {
+ return PoolType.ThreadLocal;
+ }
+
+ @Test
+ public void testTableWithMaxSize() throws Exception {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 2,
+ getPoolType());
+
+ // Request tables from an empty pool
+ HTableInterface table1 = pool.getTable(TABLENAME);
+ HTableInterface table2 = pool.getTable(TABLENAME);
+ HTableInterface table3 = pool.getTable(TABLENAME);
+
+ // Close tables (returns tables to the pool)
+ table1.close();
+ table2.close();
+ // The pool should not reject this one since the number of threads
+ // <= 2
+ table3.close();
+
+ // Request tables of the same name
+ HTableInterface sameTable1 = pool.getTable(TABLENAME);
+ HTableInterface sameTable2 = pool.getTable(TABLENAME);
+ HTableInterface sameTable3 = pool.getTable(TABLENAME);
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table3).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable1).getWrappedTable());
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table3).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable2).getWrappedTable());
+ Assert.assertSame(
+ ((HTablePool.PooledHTable) table3).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable3).getWrappedTable());
+ }
+
+ @Test
+ public void testCloseTablePool() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(), 4,
+ getPoolType());
+ HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
+
+ if (admin.tableExists(TABLENAME)) {
+ admin.disableTable(TABLENAME);
+ admin.deleteTable(TABLENAME);
+ }
+
+ HTableDescriptor tableDescriptor = new HTableDescriptor(TABLENAME);
+ tableDescriptor.addFamily(new HColumnDescriptor("randomFamily"));
+ admin.createTable(tableDescriptor);
+
+ // Request tables from an empty pool
+ HTableInterface[] tables = new HTableInterface[4];
+ for (int i = 0; i < 4; ++i) {
+ tables[i] = pool.getTable(TABLENAME);
+ }
+
+ pool.closeTablePool(TABLENAME);
+
+ for (int i = 0; i < 4; ++i) {
+ tables[i].close();
+ }
+
+ Assert.assertEquals(1,
+ pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+
+ pool.closeTablePool(TABLENAME);
+
+ Assert.assertEquals(0,
+ pool.getCurrentPoolSize(Bytes.toString(TABLENAME)));
+ }
+ }
+
+ public static junit.framework.Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTestSuite(TestHTableReusablePool.class);
+ suite.addTestSuite(TestHTableThreadLocalPool.class);
+ return suite;
+ }
+ @Test
+ public void testProxyImplementationReturned() {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE);
+ String tableName = Bytes.toString(TABLENAME);// Request a table from
+ // an
+ // empty pool
+ HTableInterface table = pool.getTable(tableName);
+
+ // Test if proxy implementation is returned
+ Assert.assertTrue(table instanceof HTablePool.PooledHTable);
+ }
+
+ @Test
+ public void testDeprecatedUsagePattern() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE);
+ String tableName = Bytes.toString(TABLENAME);// Request a table from
+ // an
+ // empty pool
+
+ // get table will return proxy implementation
+ HTableInterface table = pool.getTable(tableName);
+
+ // put back the proxy implementation instead of closing it
+ pool.putTable(table);
+
+ // Request a table of the same name
+ HTableInterface sameTable = pool.getTable(tableName);
+
+ // test no proxy over proxy created
+ Assert.assertSame(((HTablePool.PooledHTable) table).getWrappedTable(),
+ ((HTablePool.PooledHTable) sameTable).getWrappedTable());
+ }
+
+ @Test
+ public void testReturnDifferentTable() throws IOException {
+ HTablePool pool = new HTablePool(TEST_UTIL.getConfiguration(),
+ Integer.MAX_VALUE);
+ String tableName = Bytes.toString(TABLENAME);// Request a table from
+ // an
+ // empty pool
+
+ // get table will return proxy implementation
+ final HTableInterface table = pool.getTable(tableName);
+ HTableInterface alienTable = new HTable(TABLENAME) {
+ // implementation doesn't matter as long the table is not from
+ // pool
+ };
+ try {
+ // put the wrong table in pool
+ pool.putTable(alienTable);
+ Assert.fail("alien table accepted in pool");
+ } catch (IllegalArgumentException e) {
+ Assert.assertTrue("alien table rejected", true);
+ }
+
+ }
}
\ No newline at end of file