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
052  public static final CacheConfig DISABLED = new CacheConfig();
053
054
055  /**
056   * Configuration key to cache data blocks on read. Bloom blocks and index blocks are always be
057   * cached if the block cache is enabled.
058   */
059  public static final String CACHE_DATA_ON_READ_KEY = "hbase.block.data.cacheonread";
060
061  /**
062   * Configuration key to cache data blocks on write. There are separate
063   * switches for bloom blocks and non-root index blocks.
064   */
065  public static final String CACHE_BLOCKS_ON_WRITE_KEY =
066      "hbase.rs.cacheblocksonwrite";
067
068  /**
069   * Configuration key to cache leaf and intermediate-level index blocks on
070   * write.
071   */
072  public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY =
073      "hfile.block.index.cacheonwrite";
074
075  /**
076   * Configuration key to cache compound bloom filter blocks on write.
077   */
078  public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY =
079      "hfile.block.bloom.cacheonwrite";
080
081  /**
082   * Configuration key to cache data blocks in compressed and/or encrypted format.
083   */
084  public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY =
085      "hbase.block.data.cachecompressed";
086
087  /**
088   * Configuration key to evict all blocks of a given file from the block cache
089   * when the file is closed.
090   */
091  public static final String EVICT_BLOCKS_ON_CLOSE_KEY =
092      "hbase.rs.evictblocksonclose";
093
094  /**
095   * Configuration keys for Bucket cache
096   */
097
098  /**
099   * If the chosen ioengine can persist its state across restarts, the path to the file to persist
100   * to. This file is NOT the data file. It is a file into which we will serialize the map of
101   * what is in the data file. For example, if you pass the following argument as
102   * BUCKET_CACHE_IOENGINE_KEY ("hbase.bucketcache.ioengine"),
103   * <code>file:/tmp/bucketcache.data </code>, then we will write the bucketcache data to the file
104   * <code>/tmp/bucketcache.data</code> but the metadata on where the data is in the supplied file
105   * is an in-memory map that needs to be persisted across restarts. Where to store this
106   * in-memory state is what you supply here: e.g. <code>/tmp/bucketcache.map</code>.
107   */
108  public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
109      "hbase.bucketcache.persistent.path";
110
111  public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
112  public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
113      "hbase.bucketcache.writer.queuelength";
114
115  /**
116   * A comma-delimited array of values for use as bucket sizes.
117   */
118  public static final String BUCKET_CACHE_BUCKETS_KEY = "hbase.bucketcache.bucket.sizes";
119
120  /**
121   * Defaults for Bucket cache
122   */
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  private final boolean dropBehindCompaction;
212
213  /**
214   * Create a cache configuration using the specified configuration object and
215   * family descriptor.
216   * @param conf hbase configuration
217   * @param family column family configuration
218   */
219  public CacheConfig(Configuration conf, ColumnFamilyDescriptor family) {
220    this(CacheConfig.instantiateBlockCache(conf),
221        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ)
222           && family.isBlockCacheEnabled(),
223        family.isInMemory(),
224        // For the following flags we enable them regardless of per-schema settings
225        // if they are enabled in the global configuration.
226        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
227            DEFAULT_CACHE_DATA_ON_WRITE) || family.isCacheDataOnWrite(),
228        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
229            DEFAULT_CACHE_INDEXES_ON_WRITE) || family.isCacheIndexesOnWrite(),
230        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
231            DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.isCacheBloomsOnWrite(),
232        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
233            DEFAULT_EVICT_ON_CLOSE) || family.isEvictBlocksOnClose(),
234        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
235        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
236            DEFAULT_PREFETCH_ON_OPEN) || family.isPrefetchBlocksOnOpen(),
237        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
238     );
239    LOG.info("Created cacheConfig for " + family.getNameAsString() + ": " + this);
240  }
241
242  /**
243   * Create a cache configuration using the specified configuration object and
244   * defaults for family level settings. Only use if no column family context. Prefer
245   * {@link CacheConfig#CacheConfig(Configuration, ColumnFamilyDescriptor)}
246   * @see #CacheConfig(Configuration, ColumnFamilyDescriptor)
247   * @param conf hbase configuration
248   */
249  public CacheConfig(Configuration conf) {
250    this(CacheConfig.instantiateBlockCache(conf),
251        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ),
252        DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
253                           // strictly from conf
254        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
255        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
256        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
257        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
258        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
259        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
260        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
261     );
262    LOG.info("Created cacheConfig: " + this);
263  }
264
265  /**
266   * Create a block cache configuration with the specified cache and configuration parameters.
267   * @param blockCache reference to block cache, null if completely disabled
268   * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
269   *          blocks and BLOOM blocks; this cannot be disabled).
270   * @param inMemory whether blocks should be flagged as in-memory
271   * @param cacheDataOnWrite whether data blocks should be cached on write
272   * @param cacheIndexesOnWrite whether index blocks should be cached on write
273   * @param cacheBloomsOnWrite whether blooms should be cached on write
274   * @param evictOnClose whether blocks should be evicted when HFile is closed
275   * @param cacheDataCompressed whether to store blocks as compressed in the cache
276   * @param prefetchOnOpen whether to prefetch blocks upon open
277   * @param cacheDataInL1 If more than one cache tier deployed, if true, cache this column families
278   *          data blocks up in the L1 tier.
279   * @param dropBehindCompaction indicate that we should set drop behind to true when open a store
280   *          file reader for compaction
281   */
282  CacheConfig(final BlockCache blockCache,
283      final boolean cacheDataOnRead, final boolean inMemory,
284      final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
285      final boolean cacheBloomsOnWrite, final boolean evictOnClose,
286      final boolean cacheDataCompressed, final boolean prefetchOnOpen,
287      final boolean dropBehindCompaction) {
288    this.blockCache = blockCache;
289    this.cacheDataOnRead = cacheDataOnRead;
290    this.inMemory = inMemory;
291    this.cacheDataOnWrite = cacheDataOnWrite;
292    this.cacheIndexesOnWrite = cacheIndexesOnWrite;
293    this.cacheBloomsOnWrite = cacheBloomsOnWrite;
294    this.evictOnClose = evictOnClose;
295    this.cacheDataCompressed = cacheDataCompressed;
296    this.prefetchOnOpen = prefetchOnOpen;
297    this.dropBehindCompaction = dropBehindCompaction;
298  }
299
300  /**
301   * Constructs a cache configuration copied from the specified configuration.
302   * @param cacheConf
303   */
304  public CacheConfig(CacheConfig cacheConf) {
305    this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
306        cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
307        cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
308        cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
309        cacheConf.dropBehindCompaction);
310  }
311
312  private CacheConfig() {
313    this(null, false, false, false, false, false, false, false, false, false);
314  }
315
316  /**
317   * Checks whether the block cache is enabled.
318   */
319  public boolean isBlockCacheEnabled() {
320    return this.blockCache != null;
321  }
322
323  /**
324   * Returns the block cache.
325   * @return the block cache, or null if caching is completely disabled
326   */
327  public BlockCache getBlockCache() {
328    return this.blockCache;
329  }
330
331  /**
332   * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
333   * cache the meta blocks, the INDEX and BLOOM blocks).
334   * @return true if blocks should be cached on read, false if not
335   */
336  public boolean shouldCacheDataOnRead() {
337    return isBlockCacheEnabled() && cacheDataOnRead;
338  }
339
340  public boolean shouldDropBehindCompaction() {
341    return dropBehindCompaction;
342  }
343
344  /**
345   * Should we cache a block of a particular category? We always cache
346   * important blocks such as index blocks, as long as the block cache is
347   * available.
348   */
349  public boolean shouldCacheBlockOnRead(BlockCategory category) {
350    return isBlockCacheEnabled()
351        && (cacheDataOnRead ||
352            category == BlockCategory.INDEX ||
353            category == BlockCategory.BLOOM ||
354            (prefetchOnOpen &&
355                (category != BlockCategory.META &&
356                 category != BlockCategory.UNKNOWN)));
357  }
358
359  /**
360   * @return true if blocks in this file should be flagged as in-memory
361   */
362  public boolean isInMemory() {
363    return isBlockCacheEnabled() && this.inMemory;
364  }
365
366  /**
367   * @return true if data blocks should be written to the cache when an HFile is
368   *         written, false if not
369   */
370  public boolean shouldCacheDataOnWrite() {
371    return isBlockCacheEnabled() && this.cacheDataOnWrite;
372  }
373
374  /**
375   * Only used for testing.
376   * @param cacheDataOnWrite whether data blocks should be written to the cache
377   *                         when an HFile is written
378   */
379  @VisibleForTesting
380  public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
381    this.cacheDataOnWrite = cacheDataOnWrite;
382  }
383
384  /**
385   * @return true if index blocks should be written to the cache when an HFile
386   *         is written, false if not
387   */
388  public boolean shouldCacheIndexesOnWrite() {
389    return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
390  }
391
392  /**
393   * @return true if bloom blocks should be written to the cache when an HFile
394   *         is written, false if not
395   */
396  public boolean shouldCacheBloomsOnWrite() {
397    return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
398  }
399
400  /**
401   * @return true if blocks should be evicted from the cache when an HFile
402   *         reader is closed, false if not
403   */
404  public boolean shouldEvictOnClose() {
405    return isBlockCacheEnabled() && this.evictOnClose;
406  }
407
408  /**
409   * Only used for testing.
410   * @param evictOnClose whether blocks should be evicted from the cache when an
411   *                     HFile reader is closed
412   */
413  public void setEvictOnClose(boolean evictOnClose) {
414    this.evictOnClose = evictOnClose;
415  }
416
417  /**
418   * @return true if data blocks should be compressed in the cache, false if not
419   */
420  public boolean shouldCacheDataCompressed() {
421    return isBlockCacheEnabled() && this.cacheDataOnRead && this.cacheDataCompressed;
422  }
423
424  /**
425   * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
426   */
427  public boolean shouldCacheCompressed(BlockCategory category) {
428    if (!isBlockCacheEnabled()) return false;
429    switch (category) {
430      case DATA:
431        return this.cacheDataOnRead && this.cacheDataCompressed;
432      default:
433        return false;
434    }
435  }
436
437  /**
438   * @return true if blocks should be prefetched into the cache on open, false if not
439   */
440  public boolean shouldPrefetchOnOpen() {
441    return isBlockCacheEnabled() && this.prefetchOnOpen;
442  }
443
444  /**
445   * Return true if we may find this type of block in block cache.
446   * <p>
447   * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
448   * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
449   * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
450   * configuration.
451   */
452  public boolean shouldReadBlockFromCache(BlockType blockType) {
453    if (!isBlockCacheEnabled()) {
454      return false;
455    }
456    if (cacheDataOnRead) {
457      return true;
458    }
459    if (prefetchOnOpen) {
460      return true;
461    }
462    if (cacheDataOnWrite) {
463      return true;
464    }
465    if (blockType == null) {
466      return true;
467    }
468    if (blockType.getCategory() == BlockCategory.BLOOM ||
469            blockType.getCategory() == BlockCategory.INDEX) {
470      return true;
471    }
472    return false;
473  }
474
475  /**
476   * If we make sure the block could not be cached, we will not acquire the lock
477   * otherwise we will acquire lock
478   */
479  public boolean shouldLockOnCacheMiss(BlockType blockType) {
480    if (blockType == null) {
481      return true;
482    }
483    return shouldCacheBlockOnRead(blockType.getCategory());
484  }
485
486  @Override
487  public String toString() {
488    if (!isBlockCacheEnabled()) {
489      return "CacheConfig:disabled";
490    }
491    return "blockCache=" + getBlockCache() +
492      ", cacheDataOnRead=" + shouldCacheDataOnRead() +
493      ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
494      ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
495      ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
496      ", cacheEvictOnClose=" + shouldEvictOnClose() +
497      ", cacheDataCompressed=" + shouldCacheDataCompressed() +
498      ", prefetchOnOpen=" + shouldPrefetchOnOpen();
499  }
500
501  // Static block cache reference and methods
502
503  /**
504   * Static reference to the block cache, or null if no caching should be used
505   * at all.
506   */
507  // Clear this if in tests you'd make more than one block cache instance.
508  @VisibleForTesting
509  static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
510  private static LruBlockCache ONHEAP_CACHE_INSTANCE = null;
511  private static BlockCache L2_CACHE_INSTANCE = null;// Can be BucketCache or External cache.
512
513  /** Boolean whether we have disabled the block cache entirely. */
514  @VisibleForTesting
515  static boolean blockCacheDisabled = false;
516
517  /**
518   * @param c Configuration to use.
519   * @return An L1 instance.  Currently an instance of LruBlockCache.
520   */
521  public static LruBlockCache getOnHeapCache(final Configuration c) {
522    return getOnHeapCacheInternal(c);
523  }
524
525  public CacheStats getOnHeapCacheStats() {
526    if (ONHEAP_CACHE_INSTANCE != null) {
527      return ONHEAP_CACHE_INSTANCE.getStats();
528    }
529    return null;
530  }
531
532  public CacheStats getL2CacheStats() {
533    if (L2_CACHE_INSTANCE != null) {
534      return L2_CACHE_INSTANCE.getStats();
535    }
536    return null;
537  }
538
539  /**
540   * @param c Configuration to use.
541   * @return An L1 instance.  Currently an instance of LruBlockCache.
542   */
543  private synchronized static LruBlockCache getOnHeapCacheInternal(final Configuration c) {
544    if (ONHEAP_CACHE_INSTANCE != null) {
545      return ONHEAP_CACHE_INSTANCE;
546    }
547    final long cacheSize = MemorySizeUtil.getOnHeapCacheSize(c);
548    if (cacheSize < 0) {
549      blockCacheDisabled = true;
550    }
551    if (blockCacheDisabled) return null;
552    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
553    LOG.info("Allocating onheap LruBlockCache size=" +
554      StringUtils.byteDesc(cacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
555    ONHEAP_CACHE_INSTANCE = new LruBlockCache(cacheSize, blockSize, true, c);
556    return ONHEAP_CACHE_INSTANCE;
557  }
558
559  private static BlockCache getExternalBlockcache(Configuration c) {
560    if (LOG.isDebugEnabled()) {
561      LOG.debug("Trying to use External l2 cache");
562    }
563    Class klass = null;
564
565    // Get the class, from the config. s
566    try {
567      klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
568    } catch (IllegalArgumentException exception) {
569      try {
570        klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
571            "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
572      } catch (ClassNotFoundException e) {
573        return null;
574      }
575    }
576
577    // Now try and create an instance of the block cache.
578    try {
579      LOG.info("Creating external block cache of type: " + klass);
580      return (BlockCache) ReflectionUtils.newInstance(klass, c);
581    } catch (Exception e) {
582      LOG.warn("Error creating external block cache", e);
583    }
584    return null;
585
586  }
587
588  @VisibleForTesting
589  static BucketCache getBucketCache(Configuration c) {
590    // Check for L2.  ioengine name must be non-null.
591    String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
592    if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
593
594    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
595    final long bucketCacheSize = MemorySizeUtil.getBucketCacheSize(c);
596    if (bucketCacheSize <= 0) {
597      throw new IllegalStateException("bucketCacheSize <= 0; Check " +
598        BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
599    }
600    if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
601      LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
602          + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
603    }
604    int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
605      DEFAULT_BUCKET_CACHE_WRITER_THREADS);
606    int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
607      DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
608    String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
609    String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
610    int [] bucketSizes = null;
611    if (configuredBucketSizes != null) {
612      bucketSizes = new int[configuredBucketSizes.length];
613      for (int i = 0; i < configuredBucketSizes.length; i++) {
614        int bucketSize = Integer.parseInt(configuredBucketSizes[i].trim());
615        if (bucketSize % 256 != 0) {
616          // We need all the bucket sizes to be multiples of 256. Having all the configured bucket
617          // sizes to be multiples of 256 will ensure that the block offsets within buckets,
618          // that are calculated, will also be multiples of 256.
619          // See BucketEntry where offset to each block is represented using 5 bytes (instead of 8
620          // bytes long). We would like to save heap overhead as less as possible.
621          throw new IllegalArgumentException("Illegal value: " + bucketSize + " configured for '"
622              + BUCKET_CACHE_BUCKETS_KEY + "'. All bucket sizes to be multiples of 256");
623        }
624        bucketSizes[i] = bucketSize;
625      }
626    }
627    BucketCache bucketCache = null;
628    try {
629      int ioErrorsTolerationDuration = c.getInt(
630        "hbase.bucketcache.ioengine.errors.tolerated.duration",
631        BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
632      // Bucket cache logs its stats on creation internal to the constructor.
633      bucketCache = new BucketCache(bucketCacheIOEngineName,
634        bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
635        ioErrorsTolerationDuration, c);
636    } catch (IOException ioex) {
637      LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
638    }
639    return bucketCache;
640  }
641
642  /**
643   * Returns the block cache or <code>null</code> in case none should be used.
644   * Sets GLOBAL_BLOCK_CACHE_INSTANCE
645   *
646   * @param conf  The current configuration.
647   * @return The block cache or <code>null</code>.
648   */
649  public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
650    if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
651    if (blockCacheDisabled) return null;
652    LruBlockCache onHeapCache = getOnHeapCacheInternal(conf);
653    // blockCacheDisabled is set as a side-effect of getL1Internal(), so check it again after the
654    // call.
655    if (blockCacheDisabled) return null;
656    boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
657    if (useExternal) {
658      L2_CACHE_INSTANCE = getExternalBlockcache(conf);
659      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
660          : new InclusiveCombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
661    } else {
662      // otherwise use the bucket cache.
663      L2_CACHE_INSTANCE = getBucketCache(conf);
664      if (!conf.getBoolean("hbase.bucketcache.combinedcache.enabled", true)) {
665        // Non combined mode is off from 2.0
666        LOG.warn(
667            "From HBase 2.0 onwards only combined mode of LRU cache and bucket cache is available");
668      }
669      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
670          : new CombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
671    }
672    return GLOBAL_BLOCK_CACHE_INSTANCE;
673  }
674
675  // Supposed to use only from tests. Some tests want to reinit the Global block cache instance
676  @VisibleForTesting
677  static synchronized void clearGlobalInstances() {
678    ONHEAP_CACHE_INSTANCE = null;
679    L2_CACHE_INSTANCE = null;
680    GLOBAL_BLOCK_CACHE_INSTANCE = null;
681  }
682}