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