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