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