View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.io.hfile;
19  
20  import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_IOENGINE_KEY;
21  import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_SIZE_KEY;
22  
23  import java.io.IOException;
24  import java.lang.management.ManagementFactory;
25  import java.lang.management.MemoryUsage;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.classification.InterfaceAudience;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.HColumnDescriptor;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
34  import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
35  import org.apache.hadoop.hbase.util.ReflectionUtils;
36  import org.apache.hadoop.util.StringUtils;
37  
38  import com.google.common.annotations.VisibleForTesting;
39  
40  /**
41   * Stores all of the cache objects and configuration for a single HFile.
42   */
43  @InterfaceAudience.Private
44  public class CacheConfig {
45    private static final Log LOG = LogFactory.getLog(CacheConfig.class.getName());
46  
47    /**
48     * Configuration key to cache data blocks on write. There are separate
49     * switches for bloom blocks and non-root index blocks.
50     */
51    public static final String CACHE_BLOCKS_ON_WRITE_KEY =
52        "hbase.rs.cacheblocksonwrite";
53  
54    /**
55     * Configuration key to cache leaf and intermediate-level index blocks on
56     * write.
57     */
58    public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY =
59        "hfile.block.index.cacheonwrite";
60  
61    /**
62     * Configuration key to cache compound bloom filter blocks on write.
63     */
64    public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY =
65        "hfile.block.bloom.cacheonwrite";
66  
67    /**
68     * Configuration key to cache data blocks in compressed and/or encrypted format.
69     */
70    public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY =
71        "hbase.block.data.cachecompressed";
72  
73    /**
74     * Configuration key to evict all blocks of a given file from the block cache
75     * when the file is closed.
76     */
77    public static final String EVICT_BLOCKS_ON_CLOSE_KEY =
78        "hbase.rs.evictblocksonclose";
79  
80    /**
81     * Configuration keys for Bucket cache
82     */
83  
84    /**
85     * If the chosen ioengine can persist its state across restarts, the path to the file to
86     * persist to.
87     */
88    public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY = 
89        "hbase.bucketcache.persistent.path";
90  
91    /**
92     * If the bucket cache is used in league with the lru on-heap block cache (meta blocks such
93     * as indices and blooms are kept in the lru blockcache and the data blocks in the
94     * bucket cache).
95     */
96    public static final String BUCKET_CACHE_COMBINED_KEY = 
97        "hbase.bucketcache.combinedcache.enabled";
98  
99    public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
100   public static final String BUCKET_CACHE_WRITER_QUEUE_KEY = 
101       "hbase.bucketcache.writer.queuelength";
102 
103   /**
104    * A comma-delimited array of values for use as bucket sizes.
105    */
106   public static final String BUCKET_CACHE_BUCKETS_KEY = "hbase.bucketcache.bucket.sizes";
107 
108   /**
109    * Defaults for Bucket cache
110    */
111   public static final boolean DEFAULT_BUCKET_CACHE_COMBINED = true;
112   public static final int DEFAULT_BUCKET_CACHE_WRITER_THREADS = 3;
113   public static final int DEFAULT_BUCKET_CACHE_WRITER_QUEUE = 64;
114 
115  /**
116    * Configuration key to prefetch all blocks of a given file into the block cache
117    * when the file is opened.
118    */
119   public static final String PREFETCH_BLOCKS_ON_OPEN_KEY =
120       "hbase.rs.prefetchblocksonopen";
121 
122   /**
123    * The target block size used by blockcache instances. Defaults to
124    * {@link HConstants#DEFAULT_BLOCKSIZE}.
125    * TODO: this config point is completely wrong, as it's used to determine the
126    * target block size of BlockCache instances. Rename.
127    */
128   public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.offheapcache.minblocksize";
129 
130   private static final String EXTERNAL_BLOCKCACHE_KEY = "hbase.blockcache.use.external";
131   private static final boolean EXTERNAL_BLOCKCACHE_DEFAULT = false;
132 
133   private static final String EXTERNAL_BLOCKCACHE_CLASS_KEY="hbase.blockcache.external.class";
134   private static final String DROP_BEHIND_CACHE_COMPACTION_KEY="hbase.hfile.drop.behind.compaction";
135   private static final boolean DROP_BEHIND_CACHE_COMPACTION_DEFAULT = true;
136 
137   /**
138    * Enum of all built in external block caches.
139    * This is used for config.
140    */
141   private static enum ExternalBlockCaches {
142     memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
143     // TODO(eclark): Consider more. Redis, etc.
144     Class<? extends BlockCache> clazz;
145     ExternalBlockCaches(String clazzName) {
146       try {
147         clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
148       } catch (ClassNotFoundException cnef) {
149         clazz = null;
150       }
151     }
152     ExternalBlockCaches(Class<? extends BlockCache> clazz) {
153       this.clazz = clazz;
154     }
155   }
156 
157   // Defaults
158   public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
159   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
160   public static final boolean DEFAULT_IN_MEMORY = false;
161   public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
162   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
163   public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
164   public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
165   public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
166 
167   /** Local reference to the block cache, null if completely disabled */
168   private final BlockCache blockCache;
169 
170   /**
171    * Whether blocks should be cached on read (default is on if there is a
172    * cache but this can be turned off on a per-family or per-request basis).
173    * If off we will STILL cache meta blocks; i.e. INDEX and BLOOM types.
174    * This cannot be disabled.
175    */
176   private boolean cacheDataOnRead;
177 
178   /** Whether blocks should be flagged as in-memory when being cached */
179   private final boolean inMemory;
180 
181   /** Whether data blocks should be cached when new files are written */
182   private boolean cacheDataOnWrite;
183 
184   /** Whether index blocks should be cached when new files are written */
185   private final boolean cacheIndexesOnWrite;
186 
187   /** Whether compound bloom filter blocks should be cached on write */
188   private final boolean cacheBloomsOnWrite;
189 
190   /** Whether blocks of a file should be evicted when the file is closed */
191   private boolean evictOnClose;
192 
193   /** Whether data blocks should be stored in compressed and/or encrypted form in the cache */
194   private final boolean cacheDataCompressed;
195 
196   /** Whether data blocks should be prefetched into the cache */
197   private final boolean prefetchOnOpen;
198 
199   /**
200    * If true and if more than one tier in this cache deploy -- e.g. CombinedBlockCache has an L1
201    * and an L2 tier -- then cache data blocks up in the L1 tier (The meta blocks are likely being
202    * cached up in L1 already.  At least this is the case if CombinedBlockCache).
203    */
204   private boolean cacheDataInL1;
205 
206   private final boolean dropBehindCompaction;
207 
208   /**
209    * Create a cache configuration using the specified configuration object and
210    * family descriptor.
211    * @param conf hbase configuration
212    * @param family column family configuration
213    */
214   public CacheConfig(Configuration conf, HColumnDescriptor family) {
215     this(CacheConfig.instantiateBlockCache(conf),
216         family.isBlockCacheEnabled(),
217         family.isInMemory(),
218         // For the following flags we enable them regardless of per-schema settings
219         // if they are enabled in the global configuration.
220         conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
221             DEFAULT_CACHE_DATA_ON_WRITE) || family.isCacheDataOnWrite(),
222         conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
223             DEFAULT_CACHE_INDEXES_ON_WRITE) || family.isCacheIndexesOnWrite(),
224         conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
225             DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.isCacheBloomsOnWrite(),
226         conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
227             DEFAULT_EVICT_ON_CLOSE) || family.isEvictBlocksOnClose(),
228         conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
229         conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
230             DEFAULT_PREFETCH_ON_OPEN) || family.isPrefetchBlocksOnOpen(),
231         conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
232             HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1) || family.isCacheDataInL1(),
233         conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY,DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
234      );
235     LOG.info("Created cacheConfig for " + family.getNameAsString() + ": " + this);
236   }
237 
238   /**
239    * Create a cache configuration using the specified configuration object and
240    * defaults for family level settings. Only use if no column family context. Prefer
241    * {@link CacheConfig#CacheConfig(Configuration, HColumnDescriptor)}
242    * @see #CacheConfig(Configuration, HColumnDescriptor)
243    * @param conf hbase configuration
244    */
245   public CacheConfig(Configuration conf) {
246     this(CacheConfig.instantiateBlockCache(conf),
247         DEFAULT_CACHE_DATA_ON_READ,
248         DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
249                            // strictly from conf
250         conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
251         conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
252         conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
253         conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
254         conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
255         conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
256         conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
257           HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1),
258         conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY,DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
259      );
260     LOG.info("Created cacheConfig: " + this);
261   }
262 
263   /**
264    * Create a block cache configuration with the specified cache and
265    * configuration parameters.
266    * @param blockCache reference to block cache, null if completely disabled
267    * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
268    * blocks and BLOOM blocks; this cannot be disabled).
269    * @param inMemory whether blocks should be flagged as in-memory
270    * @param cacheDataOnWrite whether data blocks should be cached on write
271    * @param cacheIndexesOnWrite whether index blocks should be cached on write
272    * @param cacheBloomsOnWrite whether blooms should be cached on write
273    * @param evictOnClose whether blocks should be evicted when HFile is closed
274    * @param cacheDataCompressed whether to store blocks as compressed in the cache
275    * @param prefetchOnOpen whether to prefetch blocks upon open
276    * @param cacheDataInL1 If more than one cache tier deployed, if true, cache this column families
277    * data blocks up in the L1 tier.
278    */
279   CacheConfig(final BlockCache blockCache,
280       final boolean cacheDataOnRead, final boolean inMemory,
281       final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
282       final boolean cacheBloomsOnWrite, final boolean evictOnClose,
283       final boolean cacheDataCompressed, final boolean prefetchOnOpen,
284       final boolean cacheDataInL1, final boolean dropBehindCompaction) {
285     this.blockCache = blockCache;
286     this.cacheDataOnRead = cacheDataOnRead;
287     this.inMemory = inMemory;
288     this.cacheDataOnWrite = cacheDataOnWrite;
289     this.cacheIndexesOnWrite = cacheIndexesOnWrite;
290     this.cacheBloomsOnWrite = cacheBloomsOnWrite;
291     this.evictOnClose = evictOnClose;
292     this.cacheDataCompressed = cacheDataCompressed;
293     this.prefetchOnOpen = prefetchOnOpen;
294     this.cacheDataInL1 = cacheDataInL1;
295     this.dropBehindCompaction = dropBehindCompaction;
296   }
297 
298   /**
299    * Constructs a cache configuration copied from the specified configuration.
300    * @param cacheConf
301    */
302   public CacheConfig(CacheConfig cacheConf) {
303     this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
304         cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
305         cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
306         cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
307         cacheConf.cacheDataInL1, cacheConf.dropBehindCompaction);
308   }
309 
310   /**
311    * Checks whether the block cache is enabled.
312    */
313   public boolean isBlockCacheEnabled() {
314     return this.blockCache != null;
315   }
316 
317   /**
318    * Returns the block cache.
319    * @return the block cache, or null if caching is completely disabled
320    */
321   public BlockCache getBlockCache() {
322     return this.blockCache;
323   }
324 
325   /**
326    * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
327    * cache the meta blocks, the INDEX and BLOOM blocks).
328    * @return true if blocks should be cached on read, false if not
329    */
330   public boolean shouldCacheDataOnRead() {
331     return isBlockCacheEnabled() && cacheDataOnRead;
332   }
333 
334   public boolean shouldDropBehindCompaction() {
335     return dropBehindCompaction;
336   }
337 
338   /**
339    * Should we cache a block of a particular category? We always cache
340    * important blocks such as index blocks, as long as the block cache is
341    * available.
342    */
343   public boolean shouldCacheBlockOnRead(BlockCategory category) {
344     return isBlockCacheEnabled()
345         && (cacheDataOnRead ||
346             category == BlockCategory.INDEX ||
347             category == BlockCategory.BLOOM ||
348             (prefetchOnOpen &&
349                 (category != BlockCategory.META &&
350                  category != BlockCategory.UNKNOWN)));
351   }
352 
353   /**
354    * @return true if blocks in this file should be flagged as in-memory
355    */
356   public boolean isInMemory() {
357     return isBlockCacheEnabled() && this.inMemory;
358   }
359 
360   /**
361    * @return True if cache data blocks in L1 tier (if more than one tier in block cache deploy).
362    */
363   public boolean isCacheDataInL1() {
364     return isBlockCacheEnabled() && this.cacheDataInL1;
365   }
366 
367   /**
368    * @return true if data blocks should be written to the cache when an HFile is
369    *         written, false if not
370    */
371   public boolean shouldCacheDataOnWrite() {
372     return isBlockCacheEnabled() && this.cacheDataOnWrite;
373   }
374 
375   /**
376    * Only used for testing.
377    * @param cacheDataOnWrite whether data blocks should be written to the cache
378    *                         when an HFile is written
379    */
380   @VisibleForTesting
381   public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
382     this.cacheDataOnWrite = cacheDataOnWrite;
383   }
384 
385   /**
386    * Only used for testing.
387    * @param cacheDataInL1 Whether to cache data blocks up in l1 (if a multi-tier cache
388    * implementation).
389    */
390   @VisibleForTesting
391   public void setCacheDataInL1(boolean cacheDataInL1) {
392     this.cacheDataInL1 = cacheDataInL1;
393   }
394 
395   /**
396    * @return true if index blocks should be written to the cache when an HFile
397    *         is written, false if not
398    */
399   public boolean shouldCacheIndexesOnWrite() {
400     return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
401   }
402 
403   /**
404    * @return true if bloom blocks should be written to the cache when an HFile
405    *         is written, false if not
406    */
407   public boolean shouldCacheBloomsOnWrite() {
408     return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
409   }
410 
411   /**
412    * @return true if blocks should be evicted from the cache when an HFile
413    *         reader is closed, false if not
414    */
415   public boolean shouldEvictOnClose() {
416     return isBlockCacheEnabled() && this.evictOnClose;
417   }
418 
419   /**
420    * Only used for testing.
421    * @param evictOnClose whether blocks should be evicted from the cache when an
422    *                     HFile reader is closed
423    */
424   public void setEvictOnClose(boolean evictOnClose) {
425     this.evictOnClose = evictOnClose;
426   }
427 
428   /**
429    * @return true if data blocks should be compressed in the cache, false if not
430    */
431   public boolean shouldCacheDataCompressed() {
432     return isBlockCacheEnabled() && this.cacheDataCompressed;
433   }
434 
435   /**
436    * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
437    */
438   public boolean shouldCacheCompressed(BlockCategory category) {
439     if (!isBlockCacheEnabled()) return false;
440     switch (category) {
441       case DATA:
442         return this.cacheDataCompressed;
443       default:
444         return false;
445     }
446   }
447 
448   /**
449    * @return true if blocks should be prefetched into the cache on open, false if not
450    */
451   public boolean shouldPrefetchOnOpen() {
452     return isBlockCacheEnabled() && this.prefetchOnOpen;
453   }
454 
455   /**
456    * Return true if we may find this type of block in block cache.
457    * <p/>
458    * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
459    * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
460    * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
461    * configuration.
462    */
463   public boolean shouldReadBlockFromCache(BlockType blockType) {
464     if (!isBlockCacheEnabled()) {
465       return false;
466     }
467     if (cacheDataOnRead) {
468       return true;
469     }
470     if (prefetchOnOpen) {
471       return true;
472     }
473     if (cacheDataOnWrite) {
474       return true;
475     }
476     if (blockType == null) {
477       return true;
478     }
479     if (blockType.getCategory() == BlockCategory.BLOOM ||
480             blockType.getCategory() == BlockCategory.INDEX) {
481       return true;
482     }
483     return false;
484   }
485 
486   /**
487    * If we make sure the block could not be cached, we will not acquire the lock
488    * otherwise we will acquire lock
489    */
490   public boolean shouldLockOnCacheMiss(BlockType blockType) {
491     if (blockType == null) {
492       return true;
493     }
494     return shouldCacheBlockOnRead(blockType.getCategory());
495   }
496 
497   @Override
498   public String toString() {
499     if (!isBlockCacheEnabled()) {
500       return "CacheConfig:disabled";
501     }
502     return "blockCache=" + getBlockCache() +
503       ", cacheDataOnRead=" + shouldCacheDataOnRead() +
504       ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
505       ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
506       ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
507       ", cacheEvictOnClose=" + shouldEvictOnClose() +
508       ", cacheDataCompressed=" + shouldCacheDataCompressed() +
509       ", prefetchOnOpen=" + shouldPrefetchOnOpen();
510   }
511 
512   // Static block cache reference and methods
513 
514   /**
515    * Static reference to the block cache, or null if no caching should be used
516    * at all.
517    */
518   // Clear this if in tests you'd make more than one block cache instance.
519   @VisibleForTesting
520   static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
521 
522   /** Boolean whether we have disabled the block cache entirely. */
523   @VisibleForTesting
524   static boolean blockCacheDisabled = false;
525 
526   static long getLruCacheSize(final Configuration conf, final MemoryUsage mu) {
527     float cachePercentage = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY,
528       HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT);
529     if (cachePercentage <= 0.0001f) {
530       blockCacheDisabled = true;
531       return -1;
532     }
533     if (cachePercentage > 1.0) {
534       throw new IllegalArgumentException(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY +
535         " must be between 0.0 and 1.0, and not > 1.0");
536     }
537 
538     // Calculate the amount of heap to give the heap.
539     return (long) (mu.getMax() * cachePercentage);
540   }
541 
542   /**
543    * @param c Configuration to use.
544    * @param mu JMX Memory Bean
545    * @return An L1 instance.  Currently an instance of LruBlockCache.
546    */
547   private static LruBlockCache getL1(final Configuration c, final MemoryUsage mu) {
548     long lruCacheSize = getLruCacheSize(c, mu);
549     if (lruCacheSize < 0) return null;
550     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
551     LOG.info("Allocating LruBlockCache size=" +
552       StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
553     return new LruBlockCache(lruCacheSize, blockSize, true, c);
554   }
555 
556   /**
557    * @param c Configuration to use.
558    * @param mu JMX Memory Bean
559    * @return Returns L2 block cache instance (for now it is BucketCache BlockCache all the time)
560    * or null if not supposed to be a L2.
561    */
562   private static BlockCache getL2(final Configuration c, final MemoryUsage mu) {
563     final boolean useExternal = c.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
564     if (LOG.isDebugEnabled()) {
565       LOG.debug("Trying to use " + (useExternal?" External":" Internal") + " l2 cache");
566     }
567 
568     // If we want to use an external block cache then create that.
569     if (useExternal) {
570       return getExternalBlockcache(c);
571     }
572 
573     // otherwise use the bucket cache.
574     return getBucketCache(c, mu);
575 
576   }
577 
578   private static BlockCache getExternalBlockcache(Configuration c) {
579     Class klass = null;
580 
581     // Get the class, from the config. s
582     try {
583       klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
584     } catch (IllegalArgumentException exception) {
585       try {
586         klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
587             "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
588       } catch (ClassNotFoundException e) {
589         return null;
590       }
591     }
592 
593     // Now try and create an instance of the block cache.
594     try {
595       LOG.info("Creating external block cache of type: " + klass);
596       return (BlockCache) ReflectionUtils.newInstance(klass, c);
597     } catch (Exception e) {
598       LOG.warn("Error creating external block cache", e);
599     }
600     return null;
601 
602   }
603 
604   private static BlockCache getBucketCache(Configuration c, MemoryUsage mu) {
605     // Check for L2.  ioengine name must be non-null.
606     String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
607     if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
608 
609     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
610     float bucketCachePercentage = c.getFloat(BUCKET_CACHE_SIZE_KEY, 0F);
611     long bucketCacheSize = (long) (bucketCachePercentage < 1? mu.getMax() * bucketCachePercentage:
612       bucketCachePercentage * 1024 * 1024);
613     if (bucketCacheSize <= 0) {
614       throw new IllegalStateException("bucketCacheSize <= 0; Check " +
615         BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
616     }
617     if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
618       LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
619           + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
620     }
621     int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
622       DEFAULT_BUCKET_CACHE_WRITER_THREADS);
623     int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
624       DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
625     String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
626     String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
627     int [] bucketSizes = null;
628     if (configuredBucketSizes != null) {
629       bucketSizes = new int[configuredBucketSizes.length];
630       for (int i = 0; i < configuredBucketSizes.length; i++) {
631         bucketSizes[i] = Integer.parseInt(configuredBucketSizes[i].trim());
632       }
633     }
634     BucketCache bucketCache = null;
635     try {
636       int ioErrorsTolerationDuration = c.getInt(
637         "hbase.bucketcache.ioengine.errors.tolerated.duration",
638         BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
639       // Bucket cache logs its stats on creation internal to the constructor.
640       bucketCache = new BucketCache(bucketCacheIOEngineName,
641         bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
642         ioErrorsTolerationDuration);
643     } catch (IOException ioex) {
644       LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
645     }
646     return bucketCache;
647   }
648 
649   /**
650    * Returns the block cache or <code>null</code> in case none should be used.
651    * Sets GLOBAL_BLOCK_CACHE_INSTANCE
652    *
653    * @param conf  The current configuration.
654    * @return The block cache or <code>null</code>.
655    */
656   public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
657     if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
658     if (blockCacheDisabled) return null;
659     MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
660     LruBlockCache l1 = getL1(conf, mu);
661     // blockCacheDisabled is set as a side-effect of getL1(), so check it again after the call.
662     if (blockCacheDisabled) return null;
663     BlockCache l2 = getL2(conf, mu);
664     if (l2 == null) {
665       GLOBAL_BLOCK_CACHE_INSTANCE = l1;
666     } else {
667       boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
668       boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
669         DEFAULT_BUCKET_CACHE_COMBINED);
670       if (useExternal) {
671         GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
672       } else {
673         if (combinedWithLru) {
674           GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
675         } else {
676           // L1 and L2 are not 'combined'.  They are connected via the LruBlockCache victimhandler
677           // mechanism.  It is a little ugly but works according to the following: when the
678           // background eviction thread runs, blocks evicted from L1 will go to L2 AND when we get
679           // a block from the L1 cache, if not in L1, we will search L2.
680           GLOBAL_BLOCK_CACHE_INSTANCE = l1;
681         }
682       }
683       l1.setVictimCache(l2);
684     }
685     return GLOBAL_BLOCK_CACHE_INSTANCE;
686   }
687 }