package org.apache.hadoop.hbase.client; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.util.Bytes; import org.apache.log4j.Logger; public class SolbaseHTablePool extends HTablePool { private final static Logger logger = Logger.getLogger(SolbaseHTablePool.class); private final ConcurrentMap<String, BlockingQueue<HTableInterface>> busyTables = new ConcurrentHashMap<String, BlockingQueue<HTableInterface>>(); private final ConcurrentMap<String, BlockingQueue<HTableInterface>> idleTables = new ConcurrentHashMap<String, BlockingQueue<HTableInterface>>(); private final Configuration config; private final int maxBusy; private final int maxIdle; private final HTableInterfaceFactory tableFactory; private static ConcurrentMap<String, AtomicInteger> htableCounters = new ConcurrentHashMap<String, AtomicInteger>(); private static int maxWait = 5000; // max wait for pool is 5 seconds public SolbaseHTablePool(final Configuration config, final int maxSize) { this(config, maxSize, null); } public SolbaseHTablePool(final Configuration config, final int maxSize, final HTableInterfaceFactory tableFactory) { // Make a new configuration instance so I can safely cleanup when // done with the pool. this.config = config == null ? new Configuration() : new Configuration(config); this.maxBusy = maxSize; this.maxIdle = maxSize; this.tableFactory = tableFactory == null ? new HTableFactory() : tableFactory; } public HTableInterface getTable(String tableName) { BlockingQueue<HTableInterface> idleQueue = idleTables.get(tableName); BlockingQueue<HTableInterface> busyQueue = busyTables.get(tableName); AtomicInteger htablecounter = htableCounters.get(tableName); if(busyQueue == null){ logger.info("creating new busy queue for table: " + tableName); // lazy initialization of this pool busyQueue = new ArrayBlockingQueue<HTableInterface>(this.maxBusy,false); busyTables.putIfAbsent(tableName, busyQueue); } if (idleQueue == null || htablecounter == null) { logger.info("creating new idle queue for table: " + tableName); // lazy initialization of this pool idleQueue = new ArrayBlockingQueue<HTableInterface>(this.maxIdle,true); idleTables.putIfAbsent(tableName, idleQueue); HTableInterface htable = createHTable(tableName); htablecounter = new AtomicInteger(1); htableCounters.putIfAbsent(tableName, htablecounter); logger.info("counter for total htable created for this table: " + tableName + " count: " + htablecounter.get()); try { busyQueue.put(htable); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } return htable; } HTableInterface table; synchronized (idleQueue) { table = idleQueue.poll(); } //get the current time stamp long now = System.currentTimeMillis(); while(true){ if(table != null){ try { busyQueue.put(table); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } return table; } //if we get here, see if we need to create one //this is not 100% accurate since it doesn't use a shared //atomic variable - a connection can become idle while we are creating //a new connection if (htablecounter.get() < this.maxBusy) { //atomic duplicate check if (htablecounter.addAndGet(1) > this.maxBusy) { //if we got here, two threads passed through the first if htablecounter.decrementAndGet(); logger.info("trying to acquire resource and lost battle because pool is full for time being: " + tableName); } else { table = createHTable(tableName); logger.info("counter for total htable created for this table: " + tableName + " count: " + htablecounter.get()); try { busyQueue.put(table); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } return table; } } //end if try { // TODO: pass in wait value from param or config table = idleQueue.poll(100,TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } //we didn't get a connection, lets see if we timed out if (table == null) { if ((System.currentTimeMillis() - now) >= maxWait) { throw new RuntimeException("[" + Thread.currentThread().getName()+"] " + "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) + " seconds, none available["+busyQueue.size()+" in use] for tableName: " + tableName); } else { //no timeout, lets try again continue; } } } } public void putTable(HTableInterface table) { String tableName = Bytes.toString(table.getTableName()); BlockingQueue<HTableInterface> idleQueue = idleTables.get(tableName); BlockingQueue<HTableInterface> busyQueue = busyTables.get(tableName); synchronized (idleQueue) { if (idleQueue.size() >= maxIdle) return; // remove from busy queue, and put it back to idle queue if(busyQueue.remove(table)){ idleQueue.add(table); } } } public void closeTablePool(final String tableName) { // clean up busy tables Queue<HTableInterface> busyQueue = busyTables.get(tableName); synchronized (busyQueue) { HTableInterface table = busyQueue.poll(); while (table != null) { this.tableFactory.releaseHTableInterface(table); table = busyQueue.poll(); } } // clean up idle tables Queue<HTableInterface> idleQueue = idleTables.get(tableName); synchronized (idleQueue) { HTableInterface table = idleQueue.poll(); while (table != null) { this.tableFactory.releaseHTableInterface(table); table = idleQueue.poll(); } } HConnectionManager.deleteConnection(this.config, true); } int getCurrentPoolSize(String tableName) { Queue<HTableInterface> queue = busyTables.get(tableName); synchronized (queue) { return queue.size(); } } }