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