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   }
236 
237   /**
238    * Create a cache configuration using the specified configuration object and
239    * defaults for family level settings.
240    * @param conf hbase configuration
241    */
242   public CacheConfig(Configuration conf) {
243     this(CacheConfig.instantiateBlockCache(conf),
244         DEFAULT_CACHE_DATA_ON_READ,
245         DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
246                            // strictly from conf
247         conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
248         conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
249         conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
250         conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
251         conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
252         conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
253         conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
254           HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1),
255         conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY,DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
256      );
257   }
258 
259   /**
260    * Create a block cache configuration with the specified cache and
261    * configuration parameters.
262    * @param blockCache reference to block cache, null if completely disabled
263    * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
264    * blocks and BLOOM blocks; this cannot be disabled).
265    * @param inMemory whether blocks should be flagged as in-memory
266    * @param cacheDataOnWrite whether data blocks should be cached on write
267    * @param cacheIndexesOnWrite whether index blocks should be cached on write
268    * @param cacheBloomsOnWrite whether blooms should be cached on write
269    * @param evictOnClose whether blocks should be evicted when HFile is closed
270    * @param cacheDataCompressed whether to store blocks as compressed in the cache
271    * @param prefetchOnOpen whether to prefetch blocks upon open
272    * @param cacheDataInL1 If more than one cache tier deployed, if true, cache this column families
273    * data blocks up in the L1 tier.
274    */
275   CacheConfig(final BlockCache blockCache,
276       final boolean cacheDataOnRead, final boolean inMemory,
277       final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
278       final boolean cacheBloomsOnWrite, final boolean evictOnClose,
279       final boolean cacheDataCompressed, final boolean prefetchOnOpen,
280       final boolean cacheDataInL1, final boolean dropBehindCompaction) {
281     this.blockCache = blockCache;
282     this.cacheDataOnRead = cacheDataOnRead;
283     this.inMemory = inMemory;
284     this.cacheDataOnWrite = cacheDataOnWrite;
285     this.cacheIndexesOnWrite = cacheIndexesOnWrite;
286     this.cacheBloomsOnWrite = cacheBloomsOnWrite;
287     this.evictOnClose = evictOnClose;
288     this.cacheDataCompressed = cacheDataCompressed;
289     this.prefetchOnOpen = prefetchOnOpen;
290     this.cacheDataInL1 = cacheDataInL1;
291     this.dropBehindCompaction = dropBehindCompaction;
292     LOG.info(this);
293   }
294 
295   /**
296    * Constructs a cache configuration copied from the specified configuration.
297    * @param cacheConf
298    */
299   public CacheConfig(CacheConfig cacheConf) {
300     this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
301         cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
302         cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
303         cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
304         cacheConf.cacheDataInL1, cacheConf.dropBehindCompaction);
305   }
306 
307   /**
308    * Checks whether the block cache is enabled.
309    */
310   public boolean isBlockCacheEnabled() {
311     return this.blockCache != null;
312   }
313 
314   /**
315    * Returns the block cache.
316    * @return the block cache, or null if caching is completely disabled
317    */
318   public BlockCache getBlockCache() {
319     return this.blockCache;
320   }
321 
322   /**
323    * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
324    * cache the meta blocks, the INDEX and BLOOM blocks).
325    * @return true if blocks should be cached on read, false if not
326    */
327   public boolean shouldCacheDataOnRead() {
328     return isBlockCacheEnabled() && cacheDataOnRead;
329   }
330 
331   public boolean shouldDropBehindCompaction() {
332     return dropBehindCompaction;
333   }
334 
335   /**
336    * Should we cache a block of a particular category? We always cache
337    * important blocks such as index blocks, as long as the block cache is
338    * available.
339    */
340   public boolean shouldCacheBlockOnRead(BlockCategory category) {
341     return isBlockCacheEnabled()
342         && (cacheDataOnRead ||
343             category == BlockCategory.INDEX ||
344             category == BlockCategory.BLOOM ||
345             (prefetchOnOpen &&
346                 (category != BlockCategory.META &&
347                  category != BlockCategory.UNKNOWN)));
348   }
349 
350   /**
351    * @return true if blocks in this file should be flagged as in-memory
352    */
353   public boolean isInMemory() {
354     return isBlockCacheEnabled() && this.inMemory;
355   }
356 
357   /**
358    * @return True if cache data blocks in L1 tier (if more than one tier in block cache deploy).
359    */
360   public boolean isCacheDataInL1() {
361     return isBlockCacheEnabled() && this.cacheDataInL1;
362   }
363 
364   /**
365    * @return true if data blocks should be written to the cache when an HFile is
366    *         written, false if not
367    */
368   public boolean shouldCacheDataOnWrite() {
369     return isBlockCacheEnabled() && this.cacheDataOnWrite;
370   }
371 
372   /**
373    * Only used for testing.
374    * @param cacheDataOnWrite whether data blocks should be written to the cache
375    *                         when an HFile is written
376    */
377   @VisibleForTesting
378   public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
379     this.cacheDataOnWrite = cacheDataOnWrite;
380   }
381 
382   /**
383    * Only used for testing.
384    * @param cacheDataInL1 Whether to cache data blocks up in l1 (if a multi-tier cache
385    * implementation).
386    */
387   @VisibleForTesting
388   public void setCacheDataInL1(boolean cacheDataInL1) {
389     this.cacheDataInL1 = cacheDataInL1;
390   }
391 
392   /**
393    * @return true if index blocks should be written to the cache when an HFile
394    *         is written, false if not
395    */
396   public boolean shouldCacheIndexesOnWrite() {
397     return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
398   }
399 
400   /**
401    * @return true if bloom blocks should be written to the cache when an HFile
402    *         is written, false if not
403    */
404   public boolean shouldCacheBloomsOnWrite() {
405     return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
406   }
407 
408   /**
409    * @return true if blocks should be evicted from the cache when an HFile
410    *         reader is closed, false if not
411    */
412   public boolean shouldEvictOnClose() {
413     return isBlockCacheEnabled() && this.evictOnClose;
414   }
415 
416   /**
417    * Only used for testing.
418    * @param evictOnClose whether blocks should be evicted from the cache when an
419    *                     HFile reader is closed
420    */
421   public void setEvictOnClose(boolean evictOnClose) {
422     this.evictOnClose = evictOnClose;
423   }
424 
425   /**
426    * @return true if data blocks should be compressed in the cache, false if not
427    */
428   public boolean shouldCacheDataCompressed() {
429     return isBlockCacheEnabled() && this.cacheDataCompressed;
430   }
431 
432   /**
433    * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
434    */
435   public boolean shouldCacheCompressed(BlockCategory category) {
436     if (!isBlockCacheEnabled()) return false;
437     switch (category) {
438       case DATA:
439         return this.cacheDataCompressed;
440       default:
441         return false;
442     }
443   }
444 
445   /**
446    * @return true if blocks should be prefetched into the cache on open, false if not
447    */
448   public boolean shouldPrefetchOnOpen() {
449     return isBlockCacheEnabled() && this.prefetchOnOpen;
450   }
451 
452   /**
453    * Return true if we may find this type of block in block cache.
454    * <p>
455    * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
456    * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
457    * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
458    * configuration.
459    */
460   public boolean shouldReadBlockFromCache(BlockType blockType) {
461     if (!isBlockCacheEnabled()) {
462       return false;
463     }
464     if (cacheDataOnRead) {
465       return true;
466     }
467     if (prefetchOnOpen) {
468       return true;
469     }
470     if (cacheDataOnWrite) {
471       return true;
472     }
473     if (blockType == null) {
474       return true;
475     }
476     if (blockType.getCategory() == BlockCategory.BLOOM ||
477             blockType.getCategory() == BlockCategory.INDEX) {
478       return true;
479     }
480     return false;
481   }
482 
483   /**
484    * If we make sure the block could not be cached, we will not acquire the lock
485    * otherwise we will acquire lock
486    */
487   public boolean shouldLockOnCacheMiss(BlockType blockType) {
488     if (blockType == null) {
489       return true;
490     }
491     return shouldCacheBlockOnRead(blockType.getCategory());
492   }
493 
494   @Override
495   public String toString() {
496     if (!isBlockCacheEnabled()) {
497       return "CacheConfig:disabled";
498     }
499     return "blockCache=" + getBlockCache() +
500       ", cacheDataOnRead=" + shouldCacheDataOnRead() +
501       ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
502       ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
503       ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
504       ", cacheEvictOnClose=" + shouldEvictOnClose() +
505       ", cacheDataCompressed=" + shouldCacheDataCompressed() +
506       ", prefetchOnOpen=" + shouldPrefetchOnOpen();
507   }
508 
509   // Static block cache reference and methods
510 
511   /**
512    * Static reference to the block cache, or null if no caching should be used
513    * at all.
514    */
515   // Clear this if in tests you'd make more than one block cache instance.
516   @VisibleForTesting
517   static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
518 
519   /** Boolean whether we have disabled the block cache entirely. */
520   @VisibleForTesting
521   static boolean blockCacheDisabled = false;
522 
523   static long getLruCacheSize(final Configuration conf, final MemoryUsage mu) {
524     float cachePercentage = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY,
525       HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT);
526     if (cachePercentage <= 0.0001f) {
527       blockCacheDisabled = true;
528       return -1;
529     }
530     if (cachePercentage > 1.0) {
531       throw new IllegalArgumentException(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY +
532         " must be between 0.0 and 1.0, and not > 1.0");
533     }
534 
535     // Calculate the amount of heap to give the heap.
536     return (long) (mu.getMax() * cachePercentage);
537   }
538 
539   /**
540    * @param c Configuration to use.
541    * @param mu JMX Memory Bean
542    * @return An L1 instance.  Currently an instance of LruBlockCache.
543    */
544   private static LruBlockCache getL1(final Configuration c, final MemoryUsage mu) {
545     long lruCacheSize = getLruCacheSize(c, mu);
546     if (lruCacheSize < 0) return null;
547     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
548     LOG.info("Allocating LruBlockCache size=" +
549       StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
550     return new LruBlockCache(lruCacheSize, blockSize, true, c);
551   }
552 
553   /**
554    * @param c Configuration to use.
555    * @param mu JMX Memory Bean
556    * @return Returns L2 block cache instance (for now it is BucketCache BlockCache all the time)
557    * or null if not supposed to be a L2.
558    */
559   private static BlockCache getL2(final Configuration c, final MemoryUsage mu) {
560     final boolean useExternal = c.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
561     if (LOG.isDebugEnabled()) {
562       LOG.debug("Trying to use " + (useExternal?" External":" Internal") + " l2 cache");
563     }
564 
565     // If we want to use an external block cache then create that.
566     if (useExternal) {
567       return getExternalBlockcache(c);
568     }
569 
570     // otherwise use the bucket cache.
571     return getBucketCache(c, mu);
572 
573   }
574 
575   private static BlockCache getExternalBlockcache(Configuration c) {
576     Class klass = null;
577 
578     // Get the class, from the config. s
579     try {
580       klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
581     } catch (IllegalArgumentException exception) {
582       try {
583         klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
584             "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
585       } catch (ClassNotFoundException e) {
586         return null;
587       }
588     }
589 
590     // Now try and create an instance of the block cache.
591     try {
592       LOG.info("Creating external block cache of type: " + klass);
593       return (BlockCache) ReflectionUtils.newInstance(klass, c);
594     } catch (Exception e) {
595       LOG.warn("Error creating external block cache", e);
596     }
597     return null;
598 
599   }
600 
601   private static BlockCache getBucketCache(Configuration c, MemoryUsage mu) {
602     // Check for L2.  ioengine name must be non-null.
603     String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
604     if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
605 
606     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
607     float bucketCachePercentage = c.getFloat(BUCKET_CACHE_SIZE_KEY, 0F);
608     long bucketCacheSize = (long) (bucketCachePercentage < 1? mu.getMax() * bucketCachePercentage:
609       bucketCachePercentage * 1024 * 1024);
610     if (bucketCacheSize <= 0) {
611       throw new IllegalStateException("bucketCacheSize <= 0; Check " +
612         BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
613     }
614     if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
615       LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
616           + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
617     }
618     int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
619       DEFAULT_BUCKET_CACHE_WRITER_THREADS);
620     int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
621       DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
622     String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
623     String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
624     int [] bucketSizes = null;
625     if (configuredBucketSizes != null) {
626       bucketSizes = new int[configuredBucketSizes.length];
627       for (int i = 0; i < configuredBucketSizes.length; i++) {
628         bucketSizes[i] = Integer.parseInt(configuredBucketSizes[i].trim());
629       }
630     }
631     BucketCache bucketCache = null;
632     try {
633       int ioErrorsTolerationDuration = c.getInt(
634         "hbase.bucketcache.ioengine.errors.tolerated.duration",
635         BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
636       // Bucket cache logs its stats on creation internal to the constructor.
637       bucketCache = new BucketCache(bucketCacheIOEngineName,
638         bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
639         ioErrorsTolerationDuration);
640     } catch (IOException ioex) {
641       LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
642     }
643     return bucketCache;
644   }
645 
646   /**
647    * Returns the block cache or <code>null</code> in case none should be used.
648    * Sets GLOBAL_BLOCK_CACHE_INSTANCE
649    *
650    * @param conf  The current configuration.
651    * @return The block cache or <code>null</code>.
652    */
653   public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
654     if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
655     if (blockCacheDisabled) return null;
656     MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
657     LruBlockCache l1 = getL1(conf, mu);
658     // blockCacheDisabled is set as a side-effect of getL1(), so check it again after the call.
659     if (blockCacheDisabled) return null;
660     BlockCache l2 = getL2(conf, mu);
661     if (l2 == null) {
662       GLOBAL_BLOCK_CACHE_INSTANCE = l1;
663     } else {
664       boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
665       boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
666         DEFAULT_BUCKET_CACHE_COMBINED);
667       if (useExternal) {
668         GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
669       } else {
670         if (combinedWithLru) {
671           GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
672         } else {
673           // L1 and L2 are not 'combined'.  They are connected via the LruBlockCache victimhandler
674           // mechanism.  It is a little ugly but works according to the following: when the
675           // background eviction thread runs, blocks evicted from L1 will go to L2 AND when we get
676           // a block from the L1 cache, if not in L1, we will search L2.
677           GLOBAL_BLOCK_CACHE_INSTANCE = l1;
678         }
679       }
680       l1.setVictimCache(l2);
681     }
682     return GLOBAL_BLOCK_CACHE_INSTANCE;
683   }
684 }