View Javadoc

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