View Javadoc

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