001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io.hfile;
019
020import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_IOENGINE_KEY;
021import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_SIZE_KEY;
022
023import java.io.IOException;
024
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HConstants;
027import org.apache.yetus.audience.InterfaceAudience;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
031import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
032import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
033import org.apache.hadoop.hbase.io.util.MemorySizeUtil;
034import org.apache.hadoop.hbase.util.ReflectionUtils;
035import org.apache.hadoop.util.StringUtils;
036
037import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
038
039
040/**
041 * Stores all of the cache objects and configuration for a single HFile.
042 */
043@InterfaceAudience.Private
044public class CacheConfig {
045  private static final Logger LOG = LoggerFactory.getLogger(CacheConfig.class.getName());
046
047
048  /**
049   * Disabled cache configuration
050   */
051  public static final CacheConfig DISABLED = new CacheConfig();
052
053  /**
054   * Configuration key to cache data blocks on read. Bloom blocks and index blocks are always be
055   * cached if the block cache is enabled.
056   */
057  public static final String CACHE_DATA_ON_READ_KEY = "hbase.block.data.cacheonread";
058
059  /**
060   * Configuration key to cache data blocks on write. There are separate
061   * switches for bloom blocks and non-root index blocks.
062   */
063  public static final String CACHE_BLOCKS_ON_WRITE_KEY =
064      "hbase.rs.cacheblocksonwrite";
065
066  /**
067   * Configuration key to cache leaf and intermediate-level index blocks on
068   * write.
069   */
070  public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY =
071      "hfile.block.index.cacheonwrite";
072
073  /**
074   * Configuration key to cache compound bloom filter blocks on write.
075   */
076  public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY =
077      "hfile.block.bloom.cacheonwrite";
078
079  /**
080   * Configuration key to cache data blocks in compressed and/or encrypted format.
081   */
082  public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY =
083      "hbase.block.data.cachecompressed";
084
085  /**
086   * Configuration key to evict all blocks of a given file from the block cache
087   * when the file is closed.
088   */
089  public static final String EVICT_BLOCKS_ON_CLOSE_KEY =
090      "hbase.rs.evictblocksonclose";
091
092  /**
093   * Configuration keys for Bucket cache
094   */
095
096  /**
097   * If the chosen ioengine can persist its state across restarts, the path to the file to persist
098   * to. This file is NOT the data file. It is a file into which we will serialize the map of
099   * what is in the data file. For example, if you pass the following argument as
100   * BUCKET_CACHE_IOENGINE_KEY ("hbase.bucketcache.ioengine"),
101   * <code>file:/tmp/bucketcache.data </code>, then we will write the bucketcache data to the file
102   * <code>/tmp/bucketcache.data</code> but the metadata on where the data is in the supplied file
103   * is an in-memory map that needs to be persisted across restarts. Where to store this
104   * in-memory state is what you supply here: e.g. <code>/tmp/bucketcache.map</code>.
105   */
106  public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
107      "hbase.bucketcache.persistent.path";
108
109  public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
110  public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
111      "hbase.bucketcache.writer.queuelength";
112
113  /**
114   * A comma-delimited array of values for use as bucket sizes.
115   */
116  public static final String BUCKET_CACHE_BUCKETS_KEY = "hbase.bucketcache.bucket.sizes";
117
118  /**
119   * Defaults for Bucket cache
120   */
121  public static final int DEFAULT_BUCKET_CACHE_WRITER_THREADS = 3;
122  public static final int DEFAULT_BUCKET_CACHE_WRITER_QUEUE = 64;
123
124 /**
125   * Configuration key to prefetch all blocks of a given file into the block cache
126   * when the file is opened.
127   */
128  public static final String PREFETCH_BLOCKS_ON_OPEN_KEY =
129      "hbase.rs.prefetchblocksonopen";
130
131  /**
132   * The target block size used by blockcache instances. Defaults to
133   * {@link HConstants#DEFAULT_BLOCKSIZE}.
134   */
135  public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.blockcache.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 =
142      "hbase.hfile.drop.behind.compaction";
143  private static final boolean DROP_BEHIND_CACHE_COMPACTION_DEFAULT = true;
144
145  /**
146   * @deprecated use {@link CacheConfig#BLOCKCACHE_BLOCKSIZE_KEY} instead.
147   */
148  @Deprecated
149  static final String DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY = "hbase.offheapcache.minblocksize";
150
151  /**
152   * The config point hbase.offheapcache.minblocksize is completely wrong, which is replaced by
153   * {@link BlockCacheFactory#BLOCKCACHE_BLOCKSIZE_KEY}. Keep the old config key here for backward
154   * compatibility.
155   */
156  static {
157    Configuration.addDeprecation(DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY, BLOCKCACHE_BLOCKSIZE_KEY);
158  }
159
160  /**
161   * Enum of all built in external block caches.
162   * This is used for config.
163   */
164  private static enum ExternalBlockCaches {
165    memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
166    // TODO(eclark): Consider more. Redis, etc.
167    Class<? extends BlockCache> clazz;
168    ExternalBlockCaches(String clazzName) {
169      try {
170        clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
171      } catch (ClassNotFoundException cnef) {
172        clazz = null;
173      }
174    }
175    ExternalBlockCaches(Class<? extends BlockCache> clazz) {
176      this.clazz = clazz;
177    }
178  }
179
180  // Defaults
181  public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
182  public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
183  public static final boolean DEFAULT_IN_MEMORY = false;
184  public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
185  public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
186  public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
187  public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
188  public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
189
190  /** Local reference to the block cache, null if completely disabled */
191  private final BlockCache blockCache;
192
193  /**
194   * Whether blocks should be cached on read (default is on if there is a
195   * cache but this can be turned off on a per-family or per-request basis).
196   * If off we will STILL cache meta blocks; i.e. INDEX and BLOOM types.
197   * This cannot be disabled.
198   */
199  private boolean cacheDataOnRead;
200
201  /** Whether blocks should be flagged as in-memory when being cached */
202  private final boolean inMemory;
203
204  /** Whether data blocks should be cached when new files are written */
205  private boolean cacheDataOnWrite;
206
207  /** Whether index blocks should be cached when new files are written */
208  private final boolean cacheIndexesOnWrite;
209
210  /** Whether compound bloom filter blocks should be cached on write */
211  private final boolean cacheBloomsOnWrite;
212
213  /** Whether blocks of a file should be evicted when the file is closed */
214  private boolean evictOnClose;
215
216  /** Whether data blocks should be stored in compressed and/or encrypted form in the cache */
217  private final boolean cacheDataCompressed;
218
219  /** Whether data blocks should be prefetched into the cache */
220  private final boolean prefetchOnOpen;
221
222  private final boolean dropBehindCompaction;
223
224  /**
225   * Create a cache configuration using the specified configuration object and
226   * family descriptor.
227   * @param conf hbase configuration
228   * @param family column family configuration
229   */
230  public CacheConfig(Configuration conf, ColumnFamilyDescriptor family) {
231    this(GLOBAL_BLOCK_CACHE_INSTANCE,
232        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ)
233           && family.isBlockCacheEnabled(),
234        family.isInMemory(),
235        // For the following flags we enable them regardless of per-schema settings
236        // if they are enabled in the global configuration.
237        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
238            DEFAULT_CACHE_DATA_ON_WRITE) || family.isCacheDataOnWrite(),
239        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
240            DEFAULT_CACHE_INDEXES_ON_WRITE) || family.isCacheIndexesOnWrite(),
241        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
242            DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.isCacheBloomsOnWrite(),
243        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
244            DEFAULT_EVICT_ON_CLOSE) || family.isEvictBlocksOnClose(),
245        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
246        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
247            DEFAULT_PREFETCH_ON_OPEN) || family.isPrefetchBlocksOnOpen(),
248        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
249     );
250    LOG.info("Created cacheConfig for " + family.getNameAsString() + ": " + this);
251  }
252
253  /**
254   * Create a cache configuration using the specified configuration object and
255   * defaults for family level settings. Only use if no column family context. Prefer
256   * {@link CacheConfig#CacheConfig(Configuration, ColumnFamilyDescriptor)}
257   * @see #CacheConfig(Configuration, ColumnFamilyDescriptor)
258   * @param conf hbase configuration
259   */
260  public CacheConfig(Configuration conf) {
261    this(GLOBAL_BLOCK_CACHE_INSTANCE,
262        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ),
263        DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
264        // strictly from conf
265        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
266        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
267        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
268        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
269        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
270        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
271        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT));
272    LOG.info("Created cacheConfig: " + this);
273  }
274
275  /**
276   * Create a block cache configuration with the specified cache and configuration parameters.
277   * @param blockCache reference to block cache, null if completely disabled
278   * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
279   *          blocks and BLOOM blocks; this cannot be disabled).
280   * @param inMemory whether blocks should be flagged as in-memory
281   * @param cacheDataOnWrite whether data blocks should be cached on write
282   * @param cacheIndexesOnWrite whether index blocks should be cached on write
283   * @param cacheBloomsOnWrite whether blooms should be cached on write
284   * @param evictOnClose whether blocks should be evicted when HFile is closed
285   * @param cacheDataCompressed whether to store blocks as compressed in the cache
286   * @param prefetchOnOpen whether to prefetch blocks upon open
287   * @param dropBehindCompaction indicate that we should set drop behind to true when open a store
288   *          file reader for compaction
289   */
290  @VisibleForTesting
291  CacheConfig(final BlockCache blockCache,
292      final boolean cacheDataOnRead, final boolean inMemory,
293      final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
294      final boolean cacheBloomsOnWrite, final boolean evictOnClose,
295      final boolean cacheDataCompressed, final boolean prefetchOnOpen,
296      final boolean dropBehindCompaction) {
297    this.blockCache = blockCache;
298    this.cacheDataOnRead = cacheDataOnRead;
299    this.inMemory = inMemory;
300    this.cacheDataOnWrite = cacheDataOnWrite;
301    this.cacheIndexesOnWrite = cacheIndexesOnWrite;
302    this.cacheBloomsOnWrite = cacheBloomsOnWrite;
303    this.evictOnClose = evictOnClose;
304    this.cacheDataCompressed = cacheDataCompressed;
305    this.prefetchOnOpen = prefetchOnOpen;
306    this.dropBehindCompaction = dropBehindCompaction;
307  }
308
309  /**
310   * Constructs a cache configuration copied from the specified configuration.
311   * @param cacheConf
312   */
313  public CacheConfig(CacheConfig cacheConf) {
314    this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
315        cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
316        cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
317        cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
318        cacheConf.dropBehindCompaction);
319  }
320
321  private CacheConfig() {
322    this(null, false, false, false, false, false, false, false, false, false);
323  }
324
325  /**
326   * Checks whether the block cache is enabled.
327   */
328  public boolean isBlockCacheEnabled() {
329    return this.blockCache != null;
330  }
331
332  /**
333   * Returns the block cache.
334   * @return the block cache, or null if caching is completely disabled
335   */
336  public BlockCache getBlockCache() {
337    return this.blockCache;
338  }
339
340  /**
341   * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
342   * cache the meta blocks, the INDEX and BLOOM blocks).
343   * @return true if blocks should be cached on read, false if not
344   */
345  public boolean shouldCacheDataOnRead() {
346    return isBlockCacheEnabled() && cacheDataOnRead;
347  }
348
349  public boolean shouldDropBehindCompaction() {
350    return dropBehindCompaction;
351  }
352
353  /**
354   * Should we cache a block of a particular category? We always cache
355   * important blocks such as index blocks, as long as the block cache is
356   * available.
357   */
358  public boolean shouldCacheBlockOnRead(BlockCategory category) {
359    return isBlockCacheEnabled()
360        && (cacheDataOnRead ||
361            category == BlockCategory.INDEX ||
362            category == BlockCategory.BLOOM ||
363            (prefetchOnOpen &&
364                (category != BlockCategory.META &&
365                 category != BlockCategory.UNKNOWN)));
366  }
367
368  /**
369   * @return true if blocks in this file should be flagged as in-memory
370   */
371  public boolean isInMemory() {
372    return isBlockCacheEnabled() && this.inMemory;
373  }
374
375  /**
376   * @return true if data blocks should be written to the cache when an HFile is
377   *         written, false if not
378   */
379  public boolean shouldCacheDataOnWrite() {
380    return isBlockCacheEnabled() && this.cacheDataOnWrite;
381  }
382
383  /**
384   * Only used for testing.
385   * @param cacheDataOnWrite whether data blocks should be written to the cache
386   *                         when an HFile is written
387   */
388  @VisibleForTesting
389  public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
390    this.cacheDataOnWrite = cacheDataOnWrite;
391  }
392
393  /**
394   * @return true if index blocks should be written to the cache when an HFile
395   *         is written, false if not
396   */
397  public boolean shouldCacheIndexesOnWrite() {
398    return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
399  }
400
401  /**
402   * @return true if bloom blocks should be written to the cache when an HFile
403   *         is written, false if not
404   */
405  public boolean shouldCacheBloomsOnWrite() {
406    return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
407  }
408
409  /**
410   * @return true if blocks should be evicted from the cache when an HFile
411   *         reader is closed, false if not
412   */
413  public boolean shouldEvictOnClose() {
414    return isBlockCacheEnabled() && this.evictOnClose;
415  }
416
417  /**
418   * Only used for testing.
419   * @param evictOnClose whether blocks should be evicted from the cache when an
420   *                     HFile reader is closed
421   */
422  public void setEvictOnClose(boolean evictOnClose) {
423    this.evictOnClose = evictOnClose;
424  }
425
426  /**
427   * @return true if data blocks should be compressed in the cache, false if not
428   */
429  public boolean shouldCacheDataCompressed() {
430    return isBlockCacheEnabled() && this.cacheDataOnRead && this.cacheDataCompressed;
431  }
432
433  /**
434   * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
435   */
436  public boolean shouldCacheCompressed(BlockCategory category) {
437    if (!isBlockCacheEnabled()) return false;
438    switch (category) {
439      case DATA:
440        return this.cacheDataOnRead && this.cacheDataCompressed;
441      default:
442        return false;
443    }
444  }
445
446  /**
447   * @return true if blocks should be prefetched into the cache on open, false if not
448   */
449  public boolean shouldPrefetchOnOpen() {
450    return isBlockCacheEnabled() && this.prefetchOnOpen;
451  }
452
453  /**
454   * Return true if we may find this type of block in block cache.
455   * <p>
456   * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
457   * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
458   * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
459   * configuration.
460   */
461  public boolean shouldReadBlockFromCache(BlockType blockType) {
462    if (!isBlockCacheEnabled()) {
463      return false;
464    }
465    if (cacheDataOnRead) {
466      return true;
467    }
468    if (prefetchOnOpen) {
469      return true;
470    }
471    if (cacheDataOnWrite) {
472      return true;
473    }
474    if (blockType == null) {
475      return true;
476    }
477    if (blockType.getCategory() == BlockCategory.BLOOM ||
478            blockType.getCategory() == BlockCategory.INDEX) {
479      return true;
480    }
481    return false;
482  }
483
484  /**
485   * If we make sure the block could not be cached, we will not acquire the lock
486   * otherwise we will acquire lock
487   */
488  public boolean shouldLockOnCacheMiss(BlockType blockType) {
489    if (blockType == null) {
490      return true;
491    }
492    return shouldCacheBlockOnRead(blockType.getCategory());
493  }
494
495  @Override
496  public String toString() {
497    if (!isBlockCacheEnabled()) {
498      return "CacheConfig:disabled";
499    }
500    return "blockCache=" + getBlockCache() +
501      ", cacheDataOnRead=" + shouldCacheDataOnRead() +
502      ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
503      ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
504      ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
505      ", cacheEvictOnClose=" + shouldEvictOnClose() +
506      ", cacheDataCompressed=" + shouldCacheDataCompressed() +
507      ", prefetchOnOpen=" + shouldPrefetchOnOpen();
508  }
509
510  // Static block cache reference and methods
511
512  /**
513   * Static reference to the block cache, or null if no caching should be used
514   * at all.
515   */
516  // Clear this if in tests you'd make more than one block cache instance.
517  @VisibleForTesting
518  static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
519  private static LruBlockCache ONHEAP_CACHE_INSTANCE = null;
520  private static BlockCache L2_CACHE_INSTANCE = null;// Can be BucketCache or External cache.
521
522  /** Boolean whether we have disabled the block cache entirely. */
523  @VisibleForTesting
524  static boolean blockCacheDisabled = false;
525
526  /**
527   * @param c Configuration to use.
528   * @return An L1 instance.  Currently an instance of LruBlockCache.
529   */
530  public static LruBlockCache getOnHeapCache(final Configuration c) {
531    return getOnHeapCacheInternal(c);
532  }
533
534  public CacheStats getOnHeapCacheStats() {
535    if (ONHEAP_CACHE_INSTANCE != null) {
536      return ONHEAP_CACHE_INSTANCE.getStats();
537    }
538    return null;
539  }
540
541  public CacheStats getL2CacheStats() {
542    if (L2_CACHE_INSTANCE != null) {
543      return L2_CACHE_INSTANCE.getStats();
544    }
545    return null;
546  }
547
548  /**
549   * @param c Configuration to use.
550   * @return An L1 instance.  Currently an instance of LruBlockCache.
551   */
552  private synchronized static LruBlockCache getOnHeapCacheInternal(final Configuration c) {
553    if (ONHEAP_CACHE_INSTANCE != null) {
554      return ONHEAP_CACHE_INSTANCE;
555    }
556    final long cacheSize = MemorySizeUtil.getOnHeapCacheSize(c);
557    if (cacheSize < 0) {
558      blockCacheDisabled = true;
559    }
560    if (blockCacheDisabled) return null;
561    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
562    LOG.info("Allocating onheap LruBlockCache size=" +
563      StringUtils.byteDesc(cacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
564    ONHEAP_CACHE_INSTANCE = new LruBlockCache(cacheSize, blockSize, true, c);
565    return ONHEAP_CACHE_INSTANCE;
566  }
567
568  private static BlockCache getExternalBlockcache(Configuration c) {
569    if (LOG.isDebugEnabled()) {
570      LOG.debug("Trying to use External l2 cache");
571    }
572    Class klass = null;
573
574    // Get the class, from the config. s
575    try {
576      klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
577    } catch (IllegalArgumentException exception) {
578      try {
579        klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
580            "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
581      } catch (ClassNotFoundException e) {
582        return null;
583      }
584    }
585
586    // Now try and create an instance of the block cache.
587    try {
588      LOG.info("Creating external block cache of type: " + klass);
589      return (BlockCache) ReflectionUtils.newInstance(klass, c);
590    } catch (Exception e) {
591      LOG.warn("Error creating external block cache", e);
592    }
593    return null;
594
595  }
596
597  @VisibleForTesting
598  static BucketCache getBucketCache(Configuration c) {
599    // Check for L2.  ioengine name must be non-null.
600    String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
601    if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
602
603    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
604    final long bucketCacheSize = MemorySizeUtil.getBucketCacheSize(c);
605    if (bucketCacheSize <= 0) {
606      throw new IllegalStateException("bucketCacheSize <= 0; Check " +
607        BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
608    }
609    if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
610      LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
611          + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
612    }
613    int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
614      DEFAULT_BUCKET_CACHE_WRITER_THREADS);
615    int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
616      DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
617    String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
618    String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
619    int [] bucketSizes = null;
620    if (configuredBucketSizes != null) {
621      bucketSizes = new int[configuredBucketSizes.length];
622      for (int i = 0; i < configuredBucketSizes.length; i++) {
623        int bucketSize = Integer.parseInt(configuredBucketSizes[i].trim());
624        if (bucketSize % 256 != 0) {
625          // We need all the bucket sizes to be multiples of 256. Having all the configured bucket
626          // sizes to be multiples of 256 will ensure that the block offsets within buckets,
627          // that are calculated, will also be multiples of 256.
628          // See BucketEntry where offset to each block is represented using 5 bytes (instead of 8
629          // bytes long). We would like to save heap overhead as less as possible.
630          throw new IllegalArgumentException("Illegal value: " + bucketSize + " configured for '"
631              + BUCKET_CACHE_BUCKETS_KEY + "'. All bucket sizes to be multiples of 256");
632        }
633        bucketSizes[i] = bucketSize;
634      }
635    }
636    BucketCache bucketCache = null;
637    try {
638      int ioErrorsTolerationDuration = c.getInt(
639        "hbase.bucketcache.ioengine.errors.tolerated.duration",
640        BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
641      // Bucket cache logs its stats on creation internal to the constructor.
642      bucketCache = new BucketCache(bucketCacheIOEngineName,
643        bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
644        ioErrorsTolerationDuration, c);
645    } catch (IOException ioex) {
646      LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
647    }
648    return bucketCache;
649  }
650
651  /**
652   * Returns the block cache or <code>null</code> in case none should be used.
653   * Sets GLOBAL_BLOCK_CACHE_INSTANCE
654   *
655   * @param conf  The current configuration.
656   * @return The block cache or <code>null</code>.
657   */
658  public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
659    if (GLOBAL_BLOCK_CACHE_INSTANCE != null) {
660      return GLOBAL_BLOCK_CACHE_INSTANCE;
661    }
662    if (conf.get(DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY) != null) {
663      LOG.warn("The config key {} is deprecated now, instead please use {}. In future release "
664          + "we will remove the deprecated config.", DEPRECATED_BLOCKCACHE_BLOCKSIZE_KEY,
665        BLOCKCACHE_BLOCKSIZE_KEY);
666    }
667    if (blockCacheDisabled) {
668      return null;
669    }
670    LruBlockCache onHeapCache = getOnHeapCacheInternal(conf);
671    // blockCacheDisabled is set as a side-effect of getL1Internal(), so check it again after the
672    // call.
673    if (blockCacheDisabled) {
674      return null;
675    }
676    boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
677    if (useExternal) {
678      L2_CACHE_INSTANCE = getExternalBlockcache(conf);
679      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
680          : new InclusiveCombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
681    } else {
682      // otherwise use the bucket cache.
683      L2_CACHE_INSTANCE = getBucketCache(conf);
684      if (!conf.getBoolean("hbase.bucketcache.combinedcache.enabled", true)) {
685        // Non combined mode is off from 2.0
686        LOG.warn(
687            "From HBase 2.0 onwards only combined mode of LRU cache and bucket cache is available");
688      }
689      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
690          : new CombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
691    }
692    return GLOBAL_BLOCK_CACHE_INSTANCE;
693  }
694
695  // Supposed to use only from tests. Some tests want to reinit the Global block cache instance
696  @VisibleForTesting
697  static synchronized void clearGlobalInstances() {
698    ONHEAP_CACHE_INSTANCE = null;
699    L2_CACHE_INSTANCE = null;
700    GLOBAL_BLOCK_CACHE_INSTANCE = null;
701  }
702}