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