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 java.util.Optional;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
023import org.apache.hadoop.hbase.io.ByteBuffAllocator;
024import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
025import org.apache.yetus.audience.InterfaceAudience;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * Stores all of the cache objects and configuration for a single HFile.
031 */
032@InterfaceAudience.Private
033public class CacheConfig {
034  private static final Logger LOG = LoggerFactory.getLogger(CacheConfig.class.getName());
035
036  /**
037   * Disabled cache configuration
038   */
039  public static final CacheConfig DISABLED = new CacheConfig();
040
041  /**
042   * Configuration key to cache data blocks on read. Bloom blocks and index blocks are always be
043   * cached if the block cache is enabled.
044   */
045  public static final String CACHE_DATA_ON_READ_KEY = "hbase.block.data.cacheonread";
046
047  /**
048   * Configuration key to cache data blocks on write. There are separate switches for bloom blocks
049   * and non-root index blocks.
050   */
051  public static final String CACHE_BLOCKS_ON_WRITE_KEY = "hbase.rs.cacheblocksonwrite";
052
053  /**
054   * Configuration key to cache leaf and intermediate-level index blocks on write.
055   */
056  public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY = "hfile.block.index.cacheonwrite";
057
058  /**
059   * Configuration key to cache compound bloom filter blocks on write.
060   */
061  public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY = "hfile.block.bloom.cacheonwrite";
062
063  /**
064   * Configuration key to cache data blocks in compressed and/or encrypted format.
065   */
066  public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY = "hbase.block.data.cachecompressed";
067
068  /**
069   * Configuration key to evict all blocks of a given file from the block cache when the file is
070   * closed.
071   */
072  public static final String EVICT_BLOCKS_ON_CLOSE_KEY = "hbase.rs.evictblocksonclose";
073
074  /**
075   * Configuration key to prefetch all blocks of a given file into the block cache when the file is
076   * opened.
077   */
078  public static final String PREFETCH_BLOCKS_ON_OPEN_KEY = "hbase.rs.prefetchblocksonopen";
079
080  /**
081   * Configuration key to cache blocks when a compacted file is written
082   */
083  public static final String CACHE_COMPACTED_BLOCKS_ON_WRITE_KEY =
084    "hbase.rs.cachecompactedblocksonwrite";
085
086  /**
087   * Configuration key to determine total size in bytes of compacted files beyond which we do not
088   * cache blocks on compaction
089   */
090  public static final String CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD_KEY =
091    "hbase.rs.cachecompactedblocksonwrite.threshold";
092
093  public static final String DROP_BEHIND_CACHE_COMPACTION_KEY =
094    "hbase.hfile.drop.behind.compaction";
095
096  /**
097   * Configuration key to set interval for persisting bucket cache to disk.
098   */
099  public static final String BUCKETCACHE_PERSIST_INTERVAL_KEY =
100    "hbase.bucketcache.persist.intervalinmillis";
101
102  /**
103   * Configuration key to set the heap usage threshold limit once prefetch threads should be
104   * interrupted.
105   */
106  public static final String PREFETCH_HEAP_USAGE_THRESHOLD = "hbase.rs.prefetchheapusage";
107
108  // Defaults
109  public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
110  public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
111  public static final boolean DEFAULT_IN_MEMORY = false;
112  public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
113  public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
114  public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
115  public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
116  public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
117  public static final boolean DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE = false;
118  public static final boolean DROP_BEHIND_CACHE_COMPACTION_DEFAULT = true;
119  public static final long DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD = Long.MAX_VALUE;
120  public static final double DEFAULT_PREFETCH_HEAP_USAGE_THRESHOLD = 1d;
121
122  /**
123   * Whether blocks should be cached on read (default is on if there is a cache but this can be
124   * turned off on a per-family or per-request basis). If off we will STILL cache meta blocks; i.e.
125   * INDEX and BLOOM types. This cannot be disabled.
126   */
127  private final boolean cacheDataOnRead;
128
129  /** Whether blocks should be flagged as in-memory when being cached */
130  private final boolean inMemory;
131
132  /** Whether data blocks should be cached when new files are written */
133  private boolean cacheDataOnWrite;
134
135  /** Whether index blocks should be cached when new files are written */
136  private boolean cacheIndexesOnWrite;
137
138  /** Whether compound bloom filter blocks should be cached on write */
139  private boolean cacheBloomsOnWrite;
140
141  /** Whether blocks of a file should be evicted when the file is closed */
142  private boolean evictOnClose;
143
144  /** Whether data blocks should be stored in compressed and/or encrypted form in the cache */
145  private final boolean cacheDataCompressed;
146
147  /** Whether data blocks should be prefetched into the cache */
148  private final boolean prefetchOnOpen;
149
150  /**
151   * Whether data blocks should be cached when compacted file is written
152   */
153  private final boolean cacheCompactedDataOnWrite;
154
155  /**
156   * Determine threshold beyond which we do not cache blocks on compaction
157   */
158  private long cacheCompactedDataOnWriteThreshold;
159
160  private final boolean dropBehindCompaction;
161
162  // Local reference to the block cache
163  private final BlockCache blockCache;
164
165  private final ByteBuffAllocator byteBuffAllocator;
166
167  private final double heapUsageThreshold;
168
169  /**
170   * Create a cache configuration using the specified configuration object and defaults for family
171   * level settings. Only use if no column family context.
172   * @param conf hbase configuration
173   */
174  public CacheConfig(Configuration conf) {
175    this(conf, null);
176  }
177
178  public CacheConfig(Configuration conf, BlockCache blockCache) {
179    this(conf, null, blockCache, ByteBuffAllocator.HEAP);
180  }
181
182  /**
183   * Create a cache configuration using the specified configuration object and family descriptor.
184   * @param conf   hbase configuration
185   * @param family column family configuration
186   */
187  public CacheConfig(Configuration conf, ColumnFamilyDescriptor family, BlockCache blockCache,
188    ByteBuffAllocator byteBuffAllocator) {
189    this.cacheDataOnRead = conf.getBoolean(CACHE_DATA_ON_READ_KEY, DEFAULT_CACHE_DATA_ON_READ)
190      && (family == null ? true : family.isBlockCacheEnabled());
191    this.inMemory = family == null ? DEFAULT_IN_MEMORY : family.isInMemory();
192    this.cacheDataCompressed =
193      conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED);
194    this.dropBehindCompaction =
195      conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY, DROP_BEHIND_CACHE_COMPACTION_DEFAULT);
196    // For the following flags we enable them regardless of per-schema settings
197    // if they are enabled in the global configuration.
198    this.cacheDataOnWrite = conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE)
199      || (family == null ? false : family.isCacheDataOnWrite());
200    this.cacheIndexesOnWrite =
201      conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE)
202        || (family == null ? false : family.isCacheIndexesOnWrite());
203    this.cacheBloomsOnWrite =
204      conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE)
205        || (family == null ? false : family.isCacheBloomsOnWrite());
206    this.evictOnClose = conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE)
207      || (family == null ? false : family.isEvictBlocksOnClose());
208    this.prefetchOnOpen = conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN)
209      || (family == null ? false : family.isPrefetchBlocksOnOpen());
210    this.cacheCompactedDataOnWrite =
211      conf.getBoolean(CACHE_COMPACTED_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE);
212    this.cacheCompactedDataOnWriteThreshold = getCacheCompactedBlocksOnWriteThreshold(conf);
213    this.heapUsageThreshold =
214      conf.getDouble(PREFETCH_HEAP_USAGE_THRESHOLD, DEFAULT_PREFETCH_HEAP_USAGE_THRESHOLD);
215    this.blockCache = blockCache;
216    this.byteBuffAllocator = byteBuffAllocator;
217  }
218
219  /**
220   * Constructs a cache configuration copied from the specified configuration.
221   */
222  public CacheConfig(CacheConfig cacheConf) {
223    this.cacheDataOnRead = cacheConf.cacheDataOnRead;
224    this.inMemory = cacheConf.inMemory;
225    this.cacheDataOnWrite = cacheConf.cacheDataOnWrite;
226    this.cacheIndexesOnWrite = cacheConf.cacheIndexesOnWrite;
227    this.cacheBloomsOnWrite = cacheConf.cacheBloomsOnWrite;
228    this.evictOnClose = cacheConf.evictOnClose;
229    this.cacheDataCompressed = cacheConf.cacheDataCompressed;
230    this.prefetchOnOpen = cacheConf.prefetchOnOpen;
231    this.cacheCompactedDataOnWrite = cacheConf.cacheCompactedDataOnWrite;
232    this.cacheCompactedDataOnWriteThreshold = cacheConf.cacheCompactedDataOnWriteThreshold;
233    this.dropBehindCompaction = cacheConf.dropBehindCompaction;
234    this.blockCache = cacheConf.blockCache;
235    this.byteBuffAllocator = cacheConf.byteBuffAllocator;
236    this.heapUsageThreshold = cacheConf.heapUsageThreshold;
237  }
238
239  private CacheConfig() {
240    this.cacheDataOnRead = false;
241    this.inMemory = false;
242    this.cacheDataOnWrite = false;
243    this.cacheIndexesOnWrite = false;
244    this.cacheBloomsOnWrite = false;
245    this.evictOnClose = false;
246    this.cacheDataCompressed = false;
247    this.prefetchOnOpen = false;
248    this.cacheCompactedDataOnWrite = false;
249    this.dropBehindCompaction = false;
250    this.blockCache = null;
251    this.byteBuffAllocator = ByteBuffAllocator.HEAP;
252    this.heapUsageThreshold = DEFAULT_PREFETCH_HEAP_USAGE_THRESHOLD;
253  }
254
255  /**
256   * Returns whether the DATA blocks of this HFile should be cached on read or not (we always cache
257   * the meta blocks, the INDEX and BLOOM blocks).
258   * @return true if blocks should be cached on read, false if not
259   */
260  public boolean shouldCacheDataOnRead() {
261    return cacheDataOnRead;
262  }
263
264  public boolean shouldDropBehindCompaction() {
265    return dropBehindCompaction;
266  }
267
268  /**
269   * Should we cache a block of a particular category? We always cache important blocks such as
270   * index blocks, as long as the block cache is available.
271   */
272  public boolean shouldCacheBlockOnRead(BlockCategory category) {
273    return cacheDataOnRead || category == BlockCategory.INDEX || category == BlockCategory.BLOOM
274      || (prefetchOnOpen && (category != BlockCategory.META && category != BlockCategory.UNKNOWN));
275  }
276
277  /** Returns true if blocks in this file should be flagged as in-memory */
278  public boolean isInMemory() {
279    return this.inMemory;
280  }
281
282  /**
283   * @return true if data blocks should be written to the cache when an HFile is written, false if
284   *         not
285   */
286  public boolean shouldCacheDataOnWrite() {
287    return this.cacheDataOnWrite;
288  }
289
290  /**
291   * @param cacheDataOnWrite whether data blocks should be written to the cache when an HFile is
292   *                         written
293   */
294  public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
295    this.cacheDataOnWrite = cacheDataOnWrite;
296  }
297
298  /**
299   * Enable cache on write including: cacheDataOnWrite cacheIndexesOnWrite cacheBloomsOnWrite
300   */
301  public void enableCacheOnWrite() {
302    this.cacheDataOnWrite = true;
303    this.cacheIndexesOnWrite = true;
304    this.cacheBloomsOnWrite = true;
305  }
306
307  /**
308   * @return true if index blocks should be written to the cache when an HFile is written, false if
309   *         not
310   */
311  public boolean shouldCacheIndexesOnWrite() {
312    return this.cacheIndexesOnWrite;
313  }
314
315  /**
316   * @return true if bloom blocks should be written to the cache when an HFile is written, false if
317   *         not
318   */
319  public boolean shouldCacheBloomsOnWrite() {
320    return this.cacheBloomsOnWrite;
321  }
322
323  /**
324   * @return true if blocks should be evicted from the cache when an HFile reader is closed, false
325   *         if not
326   */
327  public boolean shouldEvictOnClose() {
328    return this.evictOnClose;
329  }
330
331  /**
332   * Only used for testing.
333   * @param evictOnClose whether blocks should be evicted from the cache when an HFile reader is
334   *                     closed
335   */
336  public void setEvictOnClose(boolean evictOnClose) {
337    this.evictOnClose = evictOnClose;
338  }
339
340  /** Returns true if data blocks should be compressed in the cache, false if not */
341  public boolean shouldCacheDataCompressed() {
342    return this.cacheDataOnRead && this.cacheDataCompressed;
343  }
344
345  /**
346   * Returns true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
347   */
348  public boolean shouldCacheCompressed(BlockCategory category) {
349    switch (category) {
350      case DATA:
351        return this.cacheDataOnRead && this.cacheDataCompressed;
352      default:
353        return false;
354    }
355  }
356
357  /** Returns true if blocks should be prefetched into the cache on open, false if not */
358  public boolean shouldPrefetchOnOpen() {
359    return this.prefetchOnOpen && this.cacheDataOnRead;
360  }
361
362  /** Returns true if blocks should be cached while writing during compaction, false if not */
363  public boolean shouldCacheCompactedBlocksOnWrite() {
364    return this.cacheCompactedDataOnWrite;
365  }
366
367  /** Returns total file size in bytes threshold for caching while writing during compaction */
368  public long getCacheCompactedBlocksOnWriteThreshold() {
369    return this.cacheCompactedDataOnWriteThreshold;
370  }
371
372  /**
373   * Return true if we may find this type of block in block cache.
374   * <p>
375   * TODO: today {@code family.isBlockCacheEnabled()} only means {@code cacheDataOnRead}, so here we
376   * consider lots of other configurations such as {@code cacheDataOnWrite}. We should fix this in
377   * the future, {@code cacheDataOnWrite} should honor the CF level {@code isBlockCacheEnabled}
378   * configuration.
379   */
380  public boolean shouldReadBlockFromCache(BlockType blockType) {
381    if (cacheDataOnRead) {
382      return true;
383    }
384    if (prefetchOnOpen) {
385      return true;
386    }
387    if (cacheDataOnWrite) {
388      return true;
389    }
390    if (blockType == null) {
391      return true;
392    }
393    if (
394      blockType.getCategory() == BlockCategory.BLOOM
395        || blockType.getCategory() == BlockCategory.INDEX
396    ) {
397      return true;
398    }
399    return false;
400  }
401
402  /**
403   * Checks if the current heap usage is below the threshold configured by
404   * "hbase.rs.prefetchheapusage" (0.8 by default).
405   */
406  public boolean isHeapUsageBelowThreshold() {
407    double total = Runtime.getRuntime().maxMemory();
408    double available = Runtime.getRuntime().freeMemory();
409    double usedRatio = 1d - (available / total);
410    return heapUsageThreshold > usedRatio;
411  }
412
413  /**
414   * If we make sure the block could not be cached, we will not acquire the lock otherwise we will
415   * acquire lock
416   */
417  public boolean shouldLockOnCacheMiss(BlockType blockType) {
418    if (blockType == null) {
419      return true;
420    }
421    return shouldCacheBlockOnRead(blockType.getCategory());
422  }
423
424  /**
425   * Returns the block cache.
426   * @return the block cache, or null if caching is completely disabled
427   */
428  public Optional<BlockCache> getBlockCache() {
429    return Optional.ofNullable(this.blockCache);
430  }
431
432  public boolean isCombinedBlockCache() {
433    return blockCache instanceof CombinedBlockCache;
434  }
435
436  public ByteBuffAllocator getByteBuffAllocator() {
437    return this.byteBuffAllocator;
438  }
439
440  public double getHeapUsageThreshold() {
441    return heapUsageThreshold;
442  }
443
444  private long getCacheCompactedBlocksOnWriteThreshold(Configuration conf) {
445    long cacheCompactedBlocksOnWriteThreshold =
446      conf.getLong(CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD_KEY,
447        DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD);
448
449    if (cacheCompactedBlocksOnWriteThreshold < 0) {
450      LOG.warn(
451        "cacheCompactedBlocksOnWriteThreshold value : {} is less than 0, resetting it to: {}",
452        cacheCompactedBlocksOnWriteThreshold, DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD);
453      cacheCompactedBlocksOnWriteThreshold = DEFAULT_CACHE_COMPACTED_BLOCKS_ON_WRITE_THRESHOLD;
454    }
455
456    return cacheCompactedBlocksOnWriteThreshold;
457  }
458
459  @Override
460  public String toString() {
461    return "cacheDataOnRead=" + shouldCacheDataOnRead() + ", cacheDataOnWrite="
462      + shouldCacheDataOnWrite() + ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite()
463      + ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() + ", cacheEvictOnClose="
464      + shouldEvictOnClose() + ", cacheDataCompressed=" + shouldCacheDataCompressed()
465      + ", prefetchOnOpen=" + shouldPrefetchOnOpen();
466  }
467}