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.Iterator; 021import java.util.Map; 022import java.util.Optional; 023import org.apache.commons.lang3.mutable.Mutable; 024import org.apache.commons.lang3.mutable.MutableBoolean; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.fs.Path; 027import org.apache.hadoop.hbase.io.HeapSize; 028import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache; 029import org.apache.hadoop.hbase.util.Pair; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034/** 035 * CombinedBlockCache is an abstraction layer that combines {@link FirstLevelBlockCache} and 036 * {@link BucketCache}. The smaller lruCache is used to cache bloom blocks and index blocks. The 037 * larger Cache is used to cache data blocks. 038 * {@link #getBlock(BlockCacheKey, boolean, boolean, boolean)} reads first from the smaller l1Cache 039 * before looking for the block in the l2Cache. Blocks evicted from l1Cache are put into the bucket 040 * cache. Metrics are the combined size and hits and misses of both caches. 041 */ 042@InterfaceAudience.Private 043public class CombinedBlockCache implements ResizableBlockCache, HeapSize { 044 protected final FirstLevelBlockCache l1Cache; 045 protected final BlockCache l2Cache; 046 protected final CombinedCacheStats combinedCacheStats; 047 048 private static final Logger LOG = LoggerFactory.getLogger(CombinedBlockCache.class); 049 050 public CombinedBlockCache(FirstLevelBlockCache l1Cache, BlockCache l2Cache) { 051 this.l1Cache = l1Cache; 052 this.l2Cache = l2Cache; 053 this.combinedCacheStats = new CombinedCacheStats(l1Cache.getStats(), l2Cache.getStats()); 054 } 055 056 @Override 057 public long heapSize() { 058 long l2size = 0; 059 if (l2Cache instanceof HeapSize) { 060 l2size = ((HeapSize) l2Cache).heapSize(); 061 } 062 return l1Cache.heapSize() + l2size; 063 } 064 065 @Override 066 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory) { 067 cacheBlock(cacheKey, buf, inMemory, false); 068 } 069 070 @Override 071 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory, 072 boolean waitWhenCache) { 073 boolean metaBlock = isMetaBlock(buf.getBlockType()); 074 if (metaBlock) { 075 l1Cache.cacheBlock(cacheKey, buf, inMemory); 076 } else { 077 l2Cache.cacheBlock(cacheKey, buf, inMemory, waitWhenCache); 078 } 079 } 080 081 @Override 082 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf) { 083 cacheBlock(cacheKey, buf, false); 084 } 085 086 @Override 087 public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat, 088 boolean updateCacheMetrics) { 089 Cacheable block = null; 090 // We don't know the block type. We should try to get it on one of the caches only, 091 // but not both otherwise we'll over compute on misses. Here we check if the key is on L1, 092 // if so, call getBlock on L1 and that will compute the hit. Otherwise, we'll try to get it from 093 // L2 and whatever happens, we'll update the stats there. 094 boolean existInL1 = l1Cache.containsBlock(cacheKey); 095 // if we know it's in L1, just delegate call to l1 and return it 096 if (existInL1) { 097 block = l1Cache.getBlock(cacheKey, caching, repeat, false); 098 } else { 099 block = l2Cache.getBlock(cacheKey, caching, repeat, false); 100 } 101 if (updateCacheMetrics) { 102 boolean metaBlock = isMetaBlock(cacheKey.getBlockType()); 103 if (metaBlock) { 104 if (!existInL1 && block != null) { 105 LOG.warn("Cache key {} had block type {}, but was found in L2 cache.", cacheKey, 106 cacheKey.getBlockType()); 107 updateBlockMetrics(block, cacheKey, l2Cache, caching); 108 } else { 109 updateBlockMetrics(block, cacheKey, l1Cache, caching); 110 } 111 } else { 112 if (existInL1) { 113 updateBlockMetrics(block, cacheKey, l1Cache, caching); 114 } else { 115 updateBlockMetrics(block, cacheKey, l2Cache, caching); 116 } 117 } 118 } 119 return block; 120 } 121 122 private void updateBlockMetrics(Cacheable block, BlockCacheKey key, BlockCache cache, 123 boolean caching) { 124 if (block == null) { 125 cache.getStats().miss(caching, key.isPrimary(), key.getBlockType()); 126 } else { 127 cache.getStats().hit(caching, key.isPrimary(), key.getBlockType()); 128 129 } 130 } 131 132 @Override 133 public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat, 134 boolean updateCacheMetrics, BlockType blockType) { 135 if (blockType == null) { 136 return getBlock(cacheKey, caching, repeat, updateCacheMetrics); 137 } 138 cacheKey.setBlockType(blockType); 139 return getBlockWithType(cacheKey, caching, repeat, updateCacheMetrics); 140 } 141 142 private Cacheable getBlockWithType(BlockCacheKey cacheKey, boolean caching, boolean repeat, 143 boolean updateCacheMetrics) { 144 boolean metaBlock = isMetaBlock(cacheKey.getBlockType()); 145 if (metaBlock) { 146 return l1Cache.getBlock(cacheKey, caching, repeat, updateCacheMetrics); 147 } else { 148 return l2Cache.getBlock(cacheKey, caching, repeat, updateCacheMetrics); 149 } 150 } 151 152 @Override 153 public boolean evictBlock(BlockCacheKey cacheKey) { 154 return l1Cache.evictBlock(cacheKey) || l2Cache.evictBlock(cacheKey); 155 } 156 157 @Override 158 public int evictBlocksByHfileName(String hfileName) { 159 return l1Cache.evictBlocksByHfileName(hfileName) + l2Cache.evictBlocksByHfileName(hfileName); 160 } 161 162 @Override 163 public int evictBlocksByHfilePath(Path hfilePath) { 164 return l1Cache.evictBlocksByHfilePath(hfilePath) + l2Cache.evictBlocksByHfilePath(hfilePath); 165 } 166 167 @Override 168 public CacheStats getStats() { 169 return this.combinedCacheStats; 170 } 171 172 @Override 173 public void shutdown() { 174 l1Cache.shutdown(); 175 l2Cache.shutdown(); 176 } 177 178 @Override 179 public long size() { 180 return l1Cache.size() + l2Cache.size(); 181 } 182 183 @Override 184 public long getMaxSize() { 185 return l1Cache.getMaxSize() + l2Cache.getMaxSize(); 186 } 187 188 @Override 189 public long getCurrentDataSize() { 190 return l1Cache.getCurrentDataSize() + l2Cache.getCurrentDataSize(); 191 } 192 193 @Override 194 public long getFreeSize() { 195 return l1Cache.getFreeSize() + l2Cache.getFreeSize(); 196 } 197 198 @Override 199 public long getCurrentSize() { 200 return l1Cache.getCurrentSize() + l2Cache.getCurrentSize(); 201 } 202 203 @Override 204 public long getBlockCount() { 205 return l1Cache.getBlockCount() + l2Cache.getBlockCount(); 206 } 207 208 @Override 209 public long getDataBlockCount() { 210 return l1Cache.getDataBlockCount() + l2Cache.getDataBlockCount(); 211 } 212 213 public static class CombinedCacheStats extends CacheStats { 214 private final CacheStats lruCacheStats; 215 private final CacheStats bucketCacheStats; 216 217 CombinedCacheStats(CacheStats lbcStats, CacheStats fcStats) { 218 super("CombinedBlockCache"); 219 this.lruCacheStats = lbcStats; 220 this.bucketCacheStats = fcStats; 221 } 222 223 public CacheStats getLruCacheStats() { 224 return this.lruCacheStats; 225 } 226 227 public CacheStats getBucketCacheStats() { 228 return this.bucketCacheStats; 229 } 230 231 @Override 232 public long getDataMissCount() { 233 return lruCacheStats.getDataMissCount() + bucketCacheStats.getDataMissCount(); 234 } 235 236 @Override 237 public long getLeafIndexMissCount() { 238 return lruCacheStats.getLeafIndexMissCount() + bucketCacheStats.getLeafIndexMissCount(); 239 } 240 241 @Override 242 public long getBloomChunkMissCount() { 243 return lruCacheStats.getBloomChunkMissCount() + bucketCacheStats.getBloomChunkMissCount(); 244 } 245 246 @Override 247 public long getMetaMissCount() { 248 return lruCacheStats.getMetaMissCount() + bucketCacheStats.getMetaMissCount(); 249 } 250 251 @Override 252 public long getRootIndexMissCount() { 253 return lruCacheStats.getRootIndexMissCount() + bucketCacheStats.getRootIndexMissCount(); 254 } 255 256 @Override 257 public long getIntermediateIndexMissCount() { 258 return lruCacheStats.getIntermediateIndexMissCount() 259 + bucketCacheStats.getIntermediateIndexMissCount(); 260 } 261 262 @Override 263 public long getFileInfoMissCount() { 264 return lruCacheStats.getFileInfoMissCount() + bucketCacheStats.getFileInfoMissCount(); 265 } 266 267 @Override 268 public long getGeneralBloomMetaMissCount() { 269 return lruCacheStats.getGeneralBloomMetaMissCount() 270 + bucketCacheStats.getGeneralBloomMetaMissCount(); 271 } 272 273 @Override 274 public long getDeleteFamilyBloomMissCount() { 275 return lruCacheStats.getDeleteFamilyBloomMissCount() 276 + bucketCacheStats.getDeleteFamilyBloomMissCount(); 277 } 278 279 @Override 280 public long getTrailerMissCount() { 281 return lruCacheStats.getTrailerMissCount() + bucketCacheStats.getTrailerMissCount(); 282 } 283 284 @Override 285 public long getDataHitCount() { 286 return lruCacheStats.getDataHitCount() + bucketCacheStats.getDataHitCount(); 287 } 288 289 @Override 290 public long getLeafIndexHitCount() { 291 return lruCacheStats.getLeafIndexHitCount() + bucketCacheStats.getLeafIndexHitCount(); 292 } 293 294 @Override 295 public long getBloomChunkHitCount() { 296 return lruCacheStats.getBloomChunkHitCount() + bucketCacheStats.getBloomChunkHitCount(); 297 } 298 299 @Override 300 public long getMetaHitCount() { 301 return lruCacheStats.getMetaHitCount() + bucketCacheStats.getMetaHitCount(); 302 } 303 304 @Override 305 public long getRootIndexHitCount() { 306 return lruCacheStats.getRootIndexHitCount() + bucketCacheStats.getRootIndexHitCount(); 307 } 308 309 @Override 310 public long getIntermediateIndexHitCount() { 311 return lruCacheStats.getIntermediateIndexHitCount() 312 + bucketCacheStats.getIntermediateIndexHitCount(); 313 } 314 315 @Override 316 public long getFileInfoHitCount() { 317 return lruCacheStats.getFileInfoHitCount() + bucketCacheStats.getFileInfoHitCount(); 318 } 319 320 @Override 321 public long getGeneralBloomMetaHitCount() { 322 return lruCacheStats.getGeneralBloomMetaHitCount() 323 + bucketCacheStats.getGeneralBloomMetaHitCount(); 324 } 325 326 @Override 327 public long getDeleteFamilyBloomHitCount() { 328 return lruCacheStats.getDeleteFamilyBloomHitCount() 329 + bucketCacheStats.getDeleteFamilyBloomHitCount(); 330 } 331 332 @Override 333 public long getTrailerHitCount() { 334 return lruCacheStats.getTrailerHitCount() + bucketCacheStats.getTrailerHitCount(); 335 } 336 337 @Override 338 public long getRequestCount() { 339 return lruCacheStats.getRequestCount() + bucketCacheStats.getRequestCount(); 340 } 341 342 @Override 343 public long getRequestCachingCount() { 344 return lruCacheStats.getRequestCachingCount() + bucketCacheStats.getRequestCachingCount(); 345 } 346 347 @Override 348 public long getMissCount() { 349 return lruCacheStats.getMissCount() + bucketCacheStats.getMissCount(); 350 } 351 352 @Override 353 public long getPrimaryMissCount() { 354 return lruCacheStats.getPrimaryMissCount() + bucketCacheStats.getPrimaryMissCount(); 355 } 356 357 @Override 358 public long getMissCachingCount() { 359 return lruCacheStats.getMissCachingCount() + bucketCacheStats.getMissCachingCount(); 360 } 361 362 @Override 363 public long getHitCount() { 364 return lruCacheStats.getHitCount() + bucketCacheStats.getHitCount(); 365 } 366 367 @Override 368 public long getPrimaryHitCount() { 369 return lruCacheStats.getPrimaryHitCount() + bucketCacheStats.getPrimaryHitCount(); 370 } 371 372 @Override 373 public long getHitCachingCount() { 374 return lruCacheStats.getHitCachingCount() + bucketCacheStats.getHitCachingCount(); 375 } 376 377 @Override 378 public long getEvictionCount() { 379 return lruCacheStats.getEvictionCount() + bucketCacheStats.getEvictionCount(); 380 } 381 382 @Override 383 public long getEvictedCount() { 384 return lruCacheStats.getEvictedCount() + bucketCacheStats.getEvictedCount(); 385 } 386 387 @Override 388 public long getPrimaryEvictedCount() { 389 return lruCacheStats.getPrimaryEvictedCount() + bucketCacheStats.getPrimaryEvictedCount(); 390 } 391 392 @Override 393 public void rollMetricsPeriod() { 394 lruCacheStats.rollMetricsPeriod(); 395 bucketCacheStats.rollMetricsPeriod(); 396 } 397 398 @Override 399 public long getFailedInserts() { 400 return lruCacheStats.getFailedInserts() + bucketCacheStats.getFailedInserts(); 401 } 402 403 @Override 404 public long getSumHitCountsPastNPeriods() { 405 return lruCacheStats.getSumHitCountsPastNPeriods() 406 + bucketCacheStats.getSumHitCountsPastNPeriods(); 407 } 408 409 @Override 410 public long getSumRequestCountsPastNPeriods() { 411 return lruCacheStats.getSumRequestCountsPastNPeriods() 412 + bucketCacheStats.getSumRequestCountsPastNPeriods(); 413 } 414 415 @Override 416 public long getSumHitCachingCountsPastNPeriods() { 417 return lruCacheStats.getSumHitCachingCountsPastNPeriods() 418 + bucketCacheStats.getSumHitCachingCountsPastNPeriods(); 419 } 420 421 @Override 422 public long getSumRequestCachingCountsPastNPeriods() { 423 return lruCacheStats.getSumRequestCachingCountsPastNPeriods() 424 + bucketCacheStats.getSumRequestCachingCountsPastNPeriods(); 425 } 426 } 427 428 @Override 429 public Iterator<CachedBlock> iterator() { 430 return new BlockCachesIterator(getBlockCaches()); 431 } 432 433 @Override 434 public BlockCache[] getBlockCaches() { 435 return new BlockCache[] { this.l1Cache, this.l2Cache }; 436 } 437 438 /** 439 * Returns the list of fully cached files 440 */ 441 @Override 442 public Optional<Map<String, Pair<String, Long>>> getFullyCachedFiles() { 443 return this.l2Cache.getFullyCachedFiles(); 444 } 445 446 @Override 447 public Optional<Map<String, Long>> getRegionCachedInfo() { 448 return l2Cache.getRegionCachedInfo(); 449 } 450 451 @Override 452 public void setMaxSize(long size) { 453 this.l1Cache.setMaxSize(size); 454 } 455 456 public int getRpcRefCount(BlockCacheKey cacheKey) { 457 return (this.l2Cache instanceof BucketCache) 458 ? ((BucketCache) this.l2Cache).getRpcRefCount(cacheKey) 459 : 0; 460 } 461 462 public FirstLevelBlockCache getFirstLevelCache() { 463 return l1Cache; 464 } 465 466 public BlockCache getSecondLevelCache() { 467 return l2Cache; 468 } 469 470 @Override 471 public void notifyFileCachingCompleted(Path fileName, int totalBlockCount, int dataBlockCount, 472 long size) { 473 l1Cache.getBlockCount(); 474 l1Cache.notifyFileCachingCompleted(fileName, totalBlockCount, dataBlockCount, size); 475 l2Cache.notifyFileCachingCompleted(fileName, totalBlockCount, dataBlockCount, size); 476 477 } 478 479 @Override 480 public void onConfigurationChange(Configuration config) { 481 l1Cache.onConfigurationChange(config); 482 l2Cache.onConfigurationChange(config); 483 } 484 485 @Override 486 public Optional<Boolean> blockFitsIntoTheCache(HFileBlock block) { 487 if (isMetaBlock(block.getBlockType())) { 488 return l1Cache.blockFitsIntoTheCache(block); 489 } else { 490 return l2Cache.blockFitsIntoTheCache(block); 491 } 492 } 493 494 @Override 495 public Optional<Boolean> shouldCacheFile(HFileInfo hFileInfo, Configuration conf) { 496 return combineCacheResults(l1Cache.shouldCacheFile(hFileInfo, conf), 497 l2Cache.shouldCacheFile(hFileInfo, conf)); 498 } 499 500 @Override 501 public Optional<Boolean> shouldCacheBlock(BlockCacheKey key, long maxTimeStamp, 502 Configuration conf) { 503 return combineCacheResults(l1Cache.shouldCacheBlock(key, maxTimeStamp, conf), 504 l2Cache.shouldCacheBlock(key, maxTimeStamp, conf)); 505 } 506 507 private Optional<Boolean> combineCacheResults(Optional<Boolean> result1, 508 Optional<Boolean> result2) { 509 final Mutable<Boolean> combinedResult = new MutableBoolean(true); 510 result1.ifPresent(b -> combinedResult.setValue(b && combinedResult.getValue())); 511 result2.ifPresent(b -> combinedResult.setValue(b && combinedResult.getValue())); 512 return Optional.of(combinedResult.getValue()); 513 } 514 515 @Override 516 public Optional<Boolean> isAlreadyCached(BlockCacheKey key) { 517 boolean result = 518 l1Cache.isAlreadyCached(key).orElseGet(() -> l2Cache.isAlreadyCached(key).orElse(false)); 519 return Optional.of(result); 520 } 521 522 @Override 523 public Optional<Integer> getBlockSize(BlockCacheKey key) { 524 Optional<Integer> l1Result = l1Cache.getBlockSize(key); 525 return l1Result.isPresent() ? l1Result : l2Cache.getBlockSize(key); 526 } 527 528 @Override 529 public int evictBlocksRangeByHfileName(String hfileName, long initOffset, long endOffset) { 530 return l1Cache.evictBlocksRangeByHfileName(hfileName, initOffset, endOffset) 531 + l2Cache.evictBlocksRangeByHfileName(hfileName, initOffset, endOffset); 532 } 533 534 @Override 535 public boolean waitForCacheInitialization(long timeout) { 536 return this.l1Cache.waitForCacheInitialization(timeout) 537 && this.l2Cache.waitForCacheInitialization(timeout); 538 } 539 540 @Override 541 public boolean isCacheEnabled() { 542 return l1Cache.isCacheEnabled() && l2Cache.isCacheEnabled(); 543 } 544}