View Javadoc

1   /**
2    * Copyright 2010 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.Closeable;
23  import java.io.IOException;
24  import java.util.Collection;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.hadoop.conf.Configuration;
29  import org.apache.hadoop.hbase.HBaseConfiguration;
30  import org.apache.hadoop.hbase.HTableDescriptor;
31  import org.apache.hadoop.hbase.client.coprocessor.Batch;
32  import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
33  import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.hbase.util.PoolMap;
36  import org.apache.hadoop.hbase.util.PoolMap.PoolType;
37  
38  /**
39   * A simple pool of HTable instances.
40   * 
41   * Each HTablePool acts as a pool for all tables. To use, instantiate an
42   * HTablePool and use {@link #getTable(String)} to get an HTable from the pool.
43   *
44     * This method is not needed anymore, clients should call
45     * HTableInterface.close() rather than returning the tables to the pool
46     *
47   * Once you are done with it, close your instance of {@link HTableInterface}
48   * by calling {@link HTableInterface#close()} rather than returning the tables
49   * to the pool with (deprecated) {@link #putTable(HTableInterface)}.
50   * 
51   * <p>
52   * A pool can be created with a <i>maxSize</i> which defines the most HTable
53   * references that will ever be retained for each table. Otherwise the default
54   * is {@link Integer#MAX_VALUE}.
55   * 
56   * <p>
57   * Pool will manage its own connections to the cluster. See
58   * {@link HConnectionManager}.
59   */
60  public class HTablePool implements Closeable {
61    private final PoolMap<String, HTableInterface> tables;
62    private final int maxSize;
63    private final PoolType poolType;
64    private final Configuration config;
65    private final HTableInterfaceFactory tableFactory;
66  
67    /**
68     * Default Constructor. Default HBaseConfiguration and no limit on pool size.
69     */
70    public HTablePool() {
71      this(HBaseConfiguration.create(), Integer.MAX_VALUE);
72    }
73  
74    /**
75     * Constructor to set maximum versions and use the specified configuration.
76     * 
77     * @param config
78     *          configuration
79     * @param maxSize
80     *          maximum number of references to keep for each table
81     */
82    public HTablePool(final Configuration config, final int maxSize) {
83      this(config, maxSize, null, null);
84    }
85  
86    /**
87     * Constructor to set maximum versions and use the specified configuration and
88     * table factory.
89     * 
90     * @param config
91     *          configuration
92     * @param maxSize
93     *          maximum number of references to keep for each table
94     * @param tableFactory
95     *          table factory
96     */
97    public HTablePool(final Configuration config, final int maxSize,
98        final HTableInterfaceFactory tableFactory) {
99      this(config, maxSize, tableFactory, PoolType.Reusable);
100   }
101 
102   /**
103    * Constructor to set maximum versions and use the specified configuration and
104    * pool type.
105    * 
106    * @param config
107    *          configuration
108    * @param maxSize
109    *          maximum number of references to keep for each table
110    * @param poolType
111    *          pool type which is one of {@link PoolType#Reusable} or
112    *          {@link PoolType#ThreadLocal}
113    */
114   public HTablePool(final Configuration config, final int maxSize,
115       final PoolType poolType) {
116     this(config, maxSize, null, poolType);
117   }
118 
119   /**
120    * Constructor to set maximum versions and use the specified configuration,
121    * table factory and pool type. The HTablePool supports the
122    * {@link PoolType#Reusable} and {@link PoolType#ThreadLocal}. If the pool
123    * type is null or not one of those two values, then it will default to
124    * {@link PoolType#Reusable}.
125    * 
126    * @param config
127    *          configuration
128    * @param maxSize
129    *          maximum number of references to keep for each table
130    * @param tableFactory
131    *          table factory
132    * @param poolType
133    *          pool type which is one of {@link PoolType#Reusable} or
134    *          {@link PoolType#ThreadLocal}
135    */
136   public HTablePool(final Configuration config, final int maxSize,
137       final HTableInterfaceFactory tableFactory, PoolType poolType) {
138     // Make a new configuration instance so I can safely cleanup when
139     // done with the pool.
140     this.config = config == null ? HBaseConfiguration.create() : config;
141     this.maxSize = maxSize;
142     this.tableFactory = tableFactory == null ? new HTableFactory()
143         : tableFactory;
144     if (poolType == null) {
145       this.poolType = PoolType.Reusable;
146     } else {
147       switch (poolType) {
148       case Reusable:
149       case ThreadLocal:
150         this.poolType = poolType;
151         break;
152       default:
153         this.poolType = PoolType.Reusable;
154         break;
155       }
156     }
157     this.tables = new PoolMap<String, HTableInterface>(this.poolType,
158         this.maxSize);
159   }
160 
161   /**
162    * Get a reference to the specified table from the pool.
163    * <p>
164    * <p/>
165    * 
166    * @param tableName
167    *          table name
168    * @return a reference to the specified table
169    * @throws RuntimeException
170    *           if there is a problem instantiating the HTable
171    */
172   public HTableInterface getTable(String tableName) {
173     // call the old getTable implementation renamed to findOrCreateTable
174     HTableInterface table = findOrCreateTable(tableName);
175     // return a proxy table so when user closes the proxy, the actual table
176     // will be returned to the pool
177     return new PooledHTable(table);
178   }
179 
180   /**
181    * Get a reference to the specified table from the pool.
182    * <p>
183    * 
184    * Create a new one if one is not available.
185    * 
186    * @param tableName
187    *          table name
188    * @return a reference to the specified table
189    * @throws RuntimeException
190    *           if there is a problem instantiating the HTable
191    */
192   private HTableInterface findOrCreateTable(String tableName) {
193     HTableInterface table = tables.get(tableName);
194     if (table == null) {
195       table = createHTable(tableName);
196     }
197     return table;
198   }
199 
200   /**
201    * Get a reference to the specified table from the pool.
202    * <p>
203    * 
204    * Create a new one if one is not available.
205    * 
206    * @param tableName
207    *          table name
208    * @return a reference to the specified table
209    * @throws RuntimeException
210    *           if there is a problem instantiating the HTable
211    */
212   public HTableInterface getTable(byte[] tableName) {
213     return getTable(Bytes.toString(tableName));
214   }
215 
216   /**
217    * This method is not needed anymore, clients should call
218    * HTableInterface.close() rather than returning the tables to the pool
219    * 
220    * @param table
221    *          the proxy table user got from pool
222    * @deprecated
223    */
224   public void putTable(HTableInterface table) throws IOException {
225     // we need to be sure nobody puts a proxy implementation in the pool
226     // but if the client code is not updated
227     // and it will continue to call putTable() instead of calling close()
228     // then we need to return the wrapped table to the pool instead of the
229     // proxy
230     // table
231     if (table instanceof PooledHTable) {
232       returnTable(((PooledHTable) table).getWrappedTable());
233     } else {
234       // normally this should not happen if clients pass back the same
235       // table
236       // object they got from the pool
237       // but if it happens then it's better to reject it
238       throw new IllegalArgumentException("not a pooled table: " + table);
239     }
240   }
241 
242   /**
243    * Puts the specified HTable back into the pool.
244    * <p>
245    * 
246    * If the pool already contains <i>maxSize</i> references to the table, then
247    * the table instance gets closed after flushing buffered edits.
248    * 
249    * @param table
250    *          table
251    */
252   private void returnTable(HTableInterface table) throws IOException {
253     // this is the old putTable method renamed and made private
254     String tableName = Bytes.toString(table.getTableName());
255     if (tables.size(tableName) >= maxSize) {
256       // release table instance since we're not reusing it
257       this.tables.remove(tableName, table);
258       this.tableFactory.releaseHTableInterface(table);
259       return;
260     }
261     tables.put(tableName, table);
262   }
263 
264   protected HTableInterface createHTable(String tableName) {
265     return this.tableFactory.createHTableInterface(config,
266         Bytes.toBytes(tableName));
267   }
268 
269   /**
270    * Closes all the HTable instances , belonging to the given table, in the
271    * table pool.
272    * <p>
273    * Note: this is a 'shutdown' of the given table pool and different from
274    * {@link #putTable(HTableInterface)}, that is used to return the table
275    * instance to the pool for future re-use.
276    * 
277    * @param tableName
278    */
279   public void closeTablePool(final String tableName) throws IOException {
280     Collection<HTableInterface> tables = this.tables.values(tableName);
281     if (tables != null) {
282       for (HTableInterface table : tables) {
283         this.tableFactory.releaseHTableInterface(table);
284       }
285     }
286     this.tables.remove(tableName);
287   }
288 
289   /**
290    * See {@link #closeTablePool(String)}.
291    * 
292    * @param tableName
293    */
294   public void closeTablePool(final byte[] tableName) throws IOException {
295     closeTablePool(Bytes.toString(tableName));
296   }
297 
298   /**
299    * Closes all the HTable instances , belonging to all tables in the table
300    * pool.
301    * <p>
302    * Note: this is a 'shutdown' of all the table pools.
303    */
304   public void close() throws IOException {
305     for (String tableName : tables.keySet()) {
306       closeTablePool(tableName);
307     }
308     this.tables.clear();
309   }
310 
311   public int getCurrentPoolSize(String tableName) {
312     return tables.size(tableName);
313   }
314 
315   /**
316    * A proxy class that implements HTableInterface.close method to return the
317    * wrapped table back to the table pool
318    *
319    */
320   class PooledHTable implements HTableInterface {
321 
322     private HTableInterface table; // actual table implementation
323 
324     public PooledHTable(HTableInterface table) {
325       this.table = table;
326     }
327 
328     @Override
329     public byte[] getTableName() {
330       return table.getTableName();
331     }
332 
333     @Override
334     public Configuration getConfiguration() {
335       return table.getConfiguration();
336     }
337 
338     @Override
339     public HTableDescriptor getTableDescriptor() throws IOException {
340       return table.getTableDescriptor();
341     }
342 
343     @Override
344     public boolean exists(Get get) throws IOException {
345       return table.exists(get);
346     }
347 
348     @Override
349     public void batch(List<? extends Row> actions, Object[] results) throws IOException,
350         InterruptedException {
351       table.batch(actions, results);
352     }
353 
354     @Override
355     public Object[] batch(List<? extends Row> actions) throws IOException,
356         InterruptedException {
357       return table.batch(actions);
358     }
359 
360     @Override
361     public Result get(Get get) throws IOException {
362       return table.get(get);
363     }
364 
365     @Override
366     public Result[] get(List<Get> gets) throws IOException {
367       return table.get(gets);
368     }
369 
370     @Override
371     @SuppressWarnings("deprecation")
372     public Result getRowOrBefore(byte[] row, byte[] family) throws IOException {
373       return table.getRowOrBefore(row, family);
374     }
375 
376     @Override
377     public ResultScanner getScanner(Scan scan) throws IOException {
378       return table.getScanner(scan);
379     }
380 
381     @Override
382     public ResultScanner getScanner(byte[] family) throws IOException {
383       return table.getScanner(family);
384     }
385 
386     @Override
387     public ResultScanner getScanner(byte[] family, byte[] qualifier)
388         throws IOException {
389       return table.getScanner(family, qualifier);
390     }
391 
392     @Override
393     public void put(Put put) throws IOException {
394       table.put(put);
395     }
396 
397     @Override
398     public void put(List<Put> puts) throws IOException {
399       table.put(puts);
400     }
401 
402     @Override
403     public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
404         byte[] value, Put put) throws IOException {
405       return table.checkAndPut(row, family, qualifier, value, put);
406     }
407 
408     @Override
409     public void delete(Delete delete) throws IOException {
410       table.delete(delete);
411     }
412 
413     @Override
414     public void delete(List<Delete> deletes) throws IOException {
415       table.delete(deletes);
416     }
417 
418     @Override
419     public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
420         byte[] value, Delete delete) throws IOException {
421       return table.checkAndDelete(row, family, qualifier, value, delete);
422     }
423 
424     @Override
425     public Result increment(Increment increment) throws IOException {
426       return table.increment(increment);
427     }
428 
429     @Override
430     public long incrementColumnValue(byte[] row, byte[] family,
431         byte[] qualifier, long amount) throws IOException {
432       return table.incrementColumnValue(row, family, qualifier, amount);
433     }
434 
435     @Override
436     public long incrementColumnValue(byte[] row, byte[] family,
437         byte[] qualifier, long amount, boolean writeToWAL) throws IOException {
438       return table.incrementColumnValue(row, family, qualifier, amount,
439           writeToWAL);
440     }
441 
442     @Override
443     public boolean isAutoFlush() {
444       return table.isAutoFlush();
445     }
446 
447     @Override
448     public void flushCommits() throws IOException {
449       table.flushCommits();
450     }
451 
452     /**
453      * Returns the actual table back to the pool
454      * 
455      * @throws IOException
456      */
457     public void close() throws IOException {
458       returnTable(table);
459     }
460 
461     /**
462      * @deprecated {@link RowLock} and associated operations are deprecated
463      */
464     @Override
465     public RowLock lockRow(byte[] row) throws IOException {
466       return table.lockRow(row);
467     }
468 
469     /**
470      * @deprecated {@link RowLock} and associated operations are deprecated
471      */
472     @Override
473     public void unlockRow(RowLock rl) throws IOException {
474       table.unlockRow(rl);
475     }
476 
477     @Override
478     public <T extends CoprocessorProtocol> T coprocessorProxy(
479         Class<T> protocol, byte[] row) {
480       return table.coprocessorProxy(protocol, row);
481     }
482 
483     @Override
484     public <T extends CoprocessorProtocol, R> Map<byte[], R> coprocessorExec(
485         Class<T> protocol, byte[] startKey, byte[] endKey,
486         Batch.Call<T, R> callable) throws IOException, Throwable {
487       return table.coprocessorExec(protocol, startKey, endKey, callable);
488     }
489 
490     @Override
491     public <T extends CoprocessorProtocol, R> void coprocessorExec(
492         Class<T> protocol, byte[] startKey, byte[] endKey,
493         Batch.Call<T, R> callable, Batch.Callback<R> callback)
494         throws IOException, Throwable {
495       table.coprocessorExec(protocol, startKey, endKey, callable, callback);
496     }
497 
498     @Override
499     public String toString() {
500       return "PooledHTable{" + ", table=" + table + '}';
501     }
502 
503     /**
504      * Expose the wrapped HTable to tests in the same package
505      * 
506      * @return wrapped htable
507      */
508     HTableInterface getWrappedTable() {
509       return table;
510     }
511 
512     @Override
513     public void mutateRow(RowMutations rm) throws IOException {
514       table.mutateRow(rm);
515     }
516 
517     @Override
518     public Result append(Append append) throws IOException {
519       return table.append(append);
520     }
521 
522     @Override
523     public void setAutoFlush(boolean autoFlush) {
524       table.setAutoFlush(autoFlush);
525     }
526 
527     @Override
528     public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) {
529       table.setAutoFlush(autoFlush, clearBufferOnFail);
530     }
531 
532     @Override
533     public long getWriteBufferSize() {
534       return table.getWriteBufferSize();
535     }
536 
537     @Override
538     public void setWriteBufferSize(long writeBufferSize) throws IOException {
539       table.setWriteBufferSize(writeBufferSize);
540     }
541   }
542 }