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