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   * TODO: this config point is completely wrong, as it's used to determine the
135   * target block size of BlockCache instances. Rename.
136   */
137  public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.offheapcache.minblocksize";
138
139  private static final String EXTERNAL_BLOCKCACHE_KEY = "hbase.blockcache.use.external";
140  private static final boolean EXTERNAL_BLOCKCACHE_DEFAULT = false;
141
142  private static final String EXTERNAL_BLOCKCACHE_CLASS_KEY = "hbase.blockcache.external.class";
143  private static final String DROP_BEHIND_CACHE_COMPACTION_KEY =
144      "hbase.hfile.drop.behind.compaction";
145  private static final boolean DROP_BEHIND_CACHE_COMPACTION_DEFAULT = true;
146
147  /**
148   * Enum of all built in external block caches.
149   * This is used for config.
150   */
151  private static enum ExternalBlockCaches {
152    memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
153    // TODO(eclark): Consider more. Redis, etc.
154    Class<? extends BlockCache> clazz;
155    ExternalBlockCaches(String clazzName) {
156      try {
157        clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
158      } catch (ClassNotFoundException cnef) {
159        clazz = null;
160      }
161    }
162    ExternalBlockCaches(Class<? extends BlockCache> clazz) {
163      this.clazz = clazz;
164    }
165  }
166
167  // Defaults
168  public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
169  public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
170  public static final boolean DEFAULT_IN_MEMORY = false;
171  public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
172  public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
173  public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
174  public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
175  public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
176
177  /** Local reference to the block cache, null if completely disabled */
178  private final BlockCache blockCache;
179
180  /**
181   * Whether blocks should be cached on read (default is on if there is a
182   * cache but this can be turned off on a per-family or per-request basis).
183   * If off we will STILL cache meta blocks; i.e. INDEX and BLOOM types.
184   * This cannot be disabled.
185   */
186  private boolean cacheDataOnRead;
187
188  /** Whether blocks should be flagged as in-memory when being cached */
189  private final boolean inMemory;
190
191  /** Whether data blocks should be cached when new files are written */
192  private boolean cacheDataOnWrite;
193
194  /** Whether index blocks should be cached when new files are written */
195  private final boolean cacheIndexesOnWrite;
196
197  /** Whether compound bloom filter blocks should be cached on write */
198  private final boolean cacheBloomsOnWrite;
199
200  /** Whether blocks of a file should be evicted when the file is closed */
201  private boolean evictOnClose;
202
203  /** Whether data blocks should be stored in compressed and/or encrypted form in the cache */
204  private final boolean cacheDataCompressed;
205
206  /** Whether data blocks should be prefetched into the cache */
207  private final boolean prefetchOnOpen;
208
209  private final boolean dropBehindCompaction;
210
211  /**
212   * Create a cache configuration using the specified configuration object and
213   * family descriptor.
214   * @param conf hbase configuration
215   * @param family column family configuration
216   */
217  public CacheConfig(Configuration conf, ColumnFamilyDescriptor family) {
218    this(GLOBAL_BLOCK_CACHE_INSTANCE,
219        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ)
220           && family.isBlockCacheEnabled(),
221        family.isInMemory(),
222        // For the following flags we enable them regardless of per-schema settings
223        // if they are enabled in the global configuration.
224        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
225            DEFAULT_CACHE_DATA_ON_WRITE) || family.isCacheDataOnWrite(),
226        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
227            DEFAULT_CACHE_INDEXES_ON_WRITE) || family.isCacheIndexesOnWrite(),
228        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
229            DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.isCacheBloomsOnWrite(),
230        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
231            DEFAULT_EVICT_ON_CLOSE) || family.isEvictBlocksOnClose(),
232        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
233        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
234            DEFAULT_PREFETCH_ON_OPEN) || family.isPrefetchBlocksOnOpen(),
235        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
236     );
237    LOG.info("Created cacheConfig for " + family.getNameAsString() + ": " + this);
238  }
239
240  /**
241   * Create a cache configuration using the specified configuration object and
242   * defaults for family level settings. Only use if no column family context. Prefer
243   * {@link CacheConfig#CacheConfig(Configuration, ColumnFamilyDescriptor)}
244   * @see #CacheConfig(Configuration, ColumnFamilyDescriptor)
245   * @param conf hbase configuration
246   */
247  public CacheConfig(Configuration conf) {
248    this(GLOBAL_BLOCK_CACHE_INSTANCE,
249        conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ),
250        DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
251        // strictly from conf
252        conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
253        conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
254        conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
255        conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
256        conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
257        conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
258        conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT));
259    LOG.info("Created cacheConfig: " + this);
260  }
261
262  /**
263   * Create a block cache configuration with the specified cache and configuration parameters.
264   * @param blockCache reference to block cache, null if completely disabled
265   * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
266   *          blocks and BLOOM blocks; this cannot be disabled).
267   * @param inMemory whether blocks should be flagged as in-memory
268   * @param cacheDataOnWrite whether data blocks should be cached on write
269   * @param cacheIndexesOnWrite whether index blocks should be cached on write
270   * @param cacheBloomsOnWrite whether blooms should be cached on write
271   * @param evictOnClose whether blocks should be evicted when HFile is closed
272   * @param cacheDataCompressed whether to store blocks as compressed in the cache
273   * @param prefetchOnOpen whether to prefetch blocks upon open
274   * @param dropBehindCompaction indicate that we should set drop behind to true when open a store
275   *          file reader for compaction
276   */
277  @VisibleForTesting
278  CacheConfig(final BlockCache blockCache,
279      final boolean cacheDataOnRead, final boolean inMemory,
280      final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
281      final boolean cacheBloomsOnWrite, final boolean evictOnClose,
282      final boolean cacheDataCompressed, final boolean prefetchOnOpen,
283      final boolean dropBehindCompaction) {
284    this.blockCache = blockCache;
285    this.cacheDataOnRead = cacheDataOnRead;
286    this.inMemory = inMemory;
287    this.cacheDataOnWrite = cacheDataOnWrite;
288    this.cacheIndexesOnWrite = cacheIndexesOnWrite;
289    this.cacheBloomsOnWrite = cacheBloomsOnWrite;
290    this.evictOnClose = evictOnClose;
291    this.cacheDataCompressed = cacheDataCompressed;
292    this.prefetchOnOpen = prefetchOnOpen;
293    this.dropBehindCompaction = dropBehindCompaction;
294  }
295
296  /**
297   * Constructs a cache configuration copied from the specified configuration.
298   * @param cacheConf
299   */
300  public CacheConfig(CacheConfig cacheConf) {
301    this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
302        cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
303        cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
304        cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
305        cacheConf.dropBehindCompaction);
306  }
307
308  private CacheConfig() {
309    this(null, false, false, false, false, false, false, false, false, false);
310  }
311
312  /**
313   * Checks whether the block cache is enabled.
314   */
315  public boolean isBlockCacheEnabled() {
316    return this.blockCache != null;
317  }
318
319  /**
320   * Returns the block cache.
321   * @return the block cache, or null if caching is completely disabled
322   */
323  public BlockCache getBlockCache() {
324    return this.blockCache;
325  }
326
327  /**
328   * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
329   * cache the meta blocks, the INDEX and BLOOM blocks).
330   * @return true if blocks should be cached on read, false if not
331   */
332  public boolean shouldCacheDataOnRead() {
333    return isBlockCacheEnabled() && cacheDataOnRead;
334  }
335
336  public boolean shouldDropBehindCompaction() {
337    return dropBehindCompaction;
338  }
339
340  /**
341   * Should we cache a block of a particular category? We always cache
342   * important blocks such as index blocks, as long as the block cache is
343   * available.
344   */
345  public boolean shouldCacheBlockOnRead(BlockCategory category) {
346    return isBlockCacheEnabled()
347        && (cacheDataOnRead ||
348            category == BlockCategory.INDEX ||
349            category == BlockCategory.BLOOM ||
350            (prefetchOnOpen &&
351                (category != BlockCategory.META &&
352                 category != BlockCategory.UNKNOWN)));
353  }
354
355  /**
356   * @return true if blocks in this file should be flagged as in-memory
357   */
358  public boolean isInMemory() {
359    return isBlockCacheEnabled() && this.inMemory;
360  }
361
362  /**
363   * @return true if data blocks should be written to the cache when an HFile is
364   *         written, false if not
365   */
366  public boolean shouldCacheDataOnWrite() {
367    return isBlockCacheEnabled() && this.cacheDataOnWrite;
368  }
369
370  /**
371   * Only used for testing.
372   * @param cacheDataOnWrite whether data blocks should be written to the cache
373   *                         when an HFile is written
374   */
375  @VisibleForTesting
376  public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
377    this.cacheDataOnWrite = cacheDataOnWrite;
378  }
379
380  /**
381   * @return true if index blocks should be written to the cache when an HFile
382   *         is written, false if not
383   */
384  public boolean shouldCacheIndexesOnWrite() {
385    return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
386  }
387
388  /**
389   * @return true if bloom blocks should be written to the cache when an HFile
390   *         is written, false if not
391   */
392  public boolean shouldCacheBloomsOnWrite() {
393    return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
394  }
395
396  /**
397   * @return true if blocks should be evicted from the cache when an HFile
398   *         reader is closed, false if not
399   */
400  public boolean shouldEvictOnClose() {
401    return isBlockCacheEnabled() && this.evictOnClose;
402  }
403
404  /**
405   * Only used for testing.
406   * @param evictOnClose whether blocks should be evicted from the cache when an
407   *                     HFile reader is closed
408   */
409  public void setEvictOnClose(boolean evictOnClose) {
410    this.evictOnClose = evictOnClose;
411  }
412
413  /**
414   * @return true if data blocks should be compressed in the cache, false if not
415   */
416  public boolean shouldCacheDataCompressed() {
417    return isBlockCacheEnabled() && this.cacheDataOnRead && this.cacheDataCompressed;
418  }
419
420  /**
421   * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
422   */
423  public boolean shouldCacheCompressed(BlockCategory category) {
424    if (!isBlockCacheEnabled()) return false;
425    switch (category) {
426      case DATA:
427        return this.cacheDataOnRead && this.cacheDataCompressed;
428      default:
429        return false;
430    }
431  }
432
433  /**
434   * @return true if blocks should be prefetched into the cache on open, false if not
435   */
436  public boolean shouldPrefetchOnOpen() {
437    return isBlockCacheEnabled() && this.prefetchOnOpen;
438  }
439
440  /**
441   * Return true if we may find this type of block in block cache.
442   * <p>
443   * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
444   * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
445   * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
446   * configuration.
447   */
448  public boolean shouldReadBlockFromCache(BlockType blockType) {
449    if (!isBlockCacheEnabled()) {
450      return false;
451    }
452    if (cacheDataOnRead) {
453      return true;
454    }
455    if (prefetchOnOpen) {
456      return true;
457    }
458    if (cacheDataOnWrite) {
459      return true;
460    }
461    if (blockType == null) {
462      return true;
463    }
464    if (blockType.getCategory() == BlockCategory.BLOOM ||
465            blockType.getCategory() == BlockCategory.INDEX) {
466      return true;
467    }
468    return false;
469  }
470
471  /**
472   * If we make sure the block could not be cached, we will not acquire the lock
473   * otherwise we will acquire lock
474   */
475  public boolean shouldLockOnCacheMiss(BlockType blockType) {
476    if (blockType == null) {
477      return true;
478    }
479    return shouldCacheBlockOnRead(blockType.getCategory());
480  }
481
482  @Override
483  public String toString() {
484    if (!isBlockCacheEnabled()) {
485      return "CacheConfig:disabled";
486    }
487    return "blockCache=" + getBlockCache() +
488      ", cacheDataOnRead=" + shouldCacheDataOnRead() +
489      ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
490      ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
491      ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
492      ", cacheEvictOnClose=" + shouldEvictOnClose() +
493      ", cacheDataCompressed=" + shouldCacheDataCompressed() +
494      ", prefetchOnOpen=" + shouldPrefetchOnOpen();
495  }
496
497  // Static block cache reference and methods
498
499  /**
500   * Static reference to the block cache, or null if no caching should be used
501   * at all.
502   */
503  // Clear this if in tests you'd make more than one block cache instance.
504  @VisibleForTesting
505  static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
506  private static LruBlockCache ONHEAP_CACHE_INSTANCE = null;
507  private static BlockCache L2_CACHE_INSTANCE = null;// Can be BucketCache or External cache.
508
509  /** Boolean whether we have disabled the block cache entirely. */
510  @VisibleForTesting
511  static boolean blockCacheDisabled = false;
512
513  /**
514   * @param c Configuration to use.
515   * @return An L1 instance.  Currently an instance of LruBlockCache.
516   */
517  public static LruBlockCache getOnHeapCache(final Configuration c) {
518    return getOnHeapCacheInternal(c);
519  }
520
521  public CacheStats getOnHeapCacheStats() {
522    if (ONHEAP_CACHE_INSTANCE != null) {
523      return ONHEAP_CACHE_INSTANCE.getStats();
524    }
525    return null;
526  }
527
528  public CacheStats getL2CacheStats() {
529    if (L2_CACHE_INSTANCE != null) {
530      return L2_CACHE_INSTANCE.getStats();
531    }
532    return null;
533  }
534
535  /**
536   * @param c Configuration to use.
537   * @return An L1 instance.  Currently an instance of LruBlockCache.
538   */
539  private synchronized static LruBlockCache getOnHeapCacheInternal(final Configuration c) {
540    if (ONHEAP_CACHE_INSTANCE != null) {
541      return ONHEAP_CACHE_INSTANCE;
542    }
543    final long cacheSize = MemorySizeUtil.getOnHeapCacheSize(c);
544    if (cacheSize < 0) {
545      blockCacheDisabled = true;
546    }
547    if (blockCacheDisabled) return null;
548    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
549    LOG.info("Allocating onheap LruBlockCache size=" +
550      StringUtils.byteDesc(cacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
551    ONHEAP_CACHE_INSTANCE = new LruBlockCache(cacheSize, blockSize, true, c);
552    return ONHEAP_CACHE_INSTANCE;
553  }
554
555  private static BlockCache getExternalBlockcache(Configuration c) {
556    if (LOG.isDebugEnabled()) {
557      LOG.debug("Trying to use External l2 cache");
558    }
559    Class klass = null;
560
561    // Get the class, from the config. s
562    try {
563      klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
564    } catch (IllegalArgumentException exception) {
565      try {
566        klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
567            "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
568      } catch (ClassNotFoundException e) {
569        return null;
570      }
571    }
572
573    // Now try and create an instance of the block cache.
574    try {
575      LOG.info("Creating external block cache of type: " + klass);
576      return (BlockCache) ReflectionUtils.newInstance(klass, c);
577    } catch (Exception e) {
578      LOG.warn("Error creating external block cache", e);
579    }
580    return null;
581
582  }
583
584  @VisibleForTesting
585  static BucketCache getBucketCache(Configuration c) {
586    // Check for L2.  ioengine name must be non-null.
587    String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
588    if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
589
590    int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
591    final long bucketCacheSize = MemorySizeUtil.getBucketCacheSize(c);
592    if (bucketCacheSize <= 0) {
593      throw new IllegalStateException("bucketCacheSize <= 0; Check " +
594        BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
595    }
596    if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
597      LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
598          + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
599    }
600    int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
601      DEFAULT_BUCKET_CACHE_WRITER_THREADS);
602    int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
603      DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
604    String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
605    String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
606    int [] bucketSizes = null;
607    if (configuredBucketSizes != null) {
608      bucketSizes = new int[configuredBucketSizes.length];
609      for (int i = 0; i < configuredBucketSizes.length; i++) {
610        int bucketSize = Integer.parseInt(configuredBucketSizes[i].trim());
611        if (bucketSize % 256 != 0) {
612          // We need all the bucket sizes to be multiples of 256. Having all the configured bucket
613          // sizes to be multiples of 256 will ensure that the block offsets within buckets,
614          // that are calculated, will also be multiples of 256.
615          // See BucketEntry where offset to each block is represented using 5 bytes (instead of 8
616          // bytes long). We would like to save heap overhead as less as possible.
617          throw new IllegalArgumentException("Illegal value: " + bucketSize + " configured for '"
618              + BUCKET_CACHE_BUCKETS_KEY + "'. All bucket sizes to be multiples of 256");
619        }
620        bucketSizes[i] = bucketSize;
621      }
622    }
623    BucketCache bucketCache = null;
624    try {
625      int ioErrorsTolerationDuration = c.getInt(
626        "hbase.bucketcache.ioengine.errors.tolerated.duration",
627        BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
628      // Bucket cache logs its stats on creation internal to the constructor.
629      bucketCache = new BucketCache(bucketCacheIOEngineName,
630        bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
631        ioErrorsTolerationDuration, c);
632    } catch (IOException ioex) {
633      LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
634    }
635    return bucketCache;
636  }
637
638  /**
639   * Returns the block cache or <code>null</code> in case none should be used.
640   * Sets GLOBAL_BLOCK_CACHE_INSTANCE
641   *
642   * @param conf  The current configuration.
643   * @return The block cache or <code>null</code>.
644   */
645  public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
646    if (GLOBAL_BLOCK_CACHE_INSTANCE != null) {
647      return GLOBAL_BLOCK_CACHE_INSTANCE;
648    }
649    if (blockCacheDisabled) {
650      return null;
651    }
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) {
656      return null;
657    }
658    boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
659    if (useExternal) {
660      L2_CACHE_INSTANCE = getExternalBlockcache(conf);
661      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
662          : new InclusiveCombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
663    } else {
664      // otherwise use the bucket cache.
665      L2_CACHE_INSTANCE = getBucketCache(conf);
666      if (!conf.getBoolean("hbase.bucketcache.combinedcache.enabled", true)) {
667        // Non combined mode is off from 2.0
668        LOG.warn(
669            "From HBase 2.0 onwards only combined mode of LRU cache and bucket cache is available");
670      }
671      GLOBAL_BLOCK_CACHE_INSTANCE = L2_CACHE_INSTANCE == null ? onHeapCache
672          : new CombinedBlockCache(onHeapCache, L2_CACHE_INSTANCE);
673    }
674    return GLOBAL_BLOCK_CACHE_INSTANCE;
675  }
676
677  // Supposed to use only from tests. Some tests want to reinit the Global block cache instance
678  @VisibleForTesting
679  static synchronized void clearGlobalInstances() {
680    ONHEAP_CACHE_INSTANCE = null;
681    L2_CACHE_INSTANCE = null;
682    GLOBAL_BLOCK_CACHE_INSTANCE = null;
683  }
684}