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