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 CacheStats getStats() { 164 return this.combinedCacheStats; 165 } 166 167 @Override 168 public void shutdown() { 169 l1Cache.shutdown(); 170 l2Cache.shutdown(); 171 } 172 173 @Override 174 public long size() { 175 return l1Cache.size() + l2Cache.size(); 176 } 177 178 @Override 179 public long getMaxSize() { 180 return l1Cache.getMaxSize() + l2Cache.getMaxSize(); 181 } 182 183 @Override 184 public long getCurrentDataSize() { 185 return l1Cache.getCurrentDataSize() + l2Cache.getCurrentDataSize(); 186 } 187 188 @Override 189 public long getFreeSize() { 190 return l1Cache.getFreeSize() + l2Cache.getFreeSize(); 191 } 192 193 @Override 194 public long getCurrentSize() { 195 return l1Cache.getCurrentSize() + l2Cache.getCurrentSize(); 196 } 197 198 @Override 199 public long getBlockCount() { 200 return l1Cache.getBlockCount() + l2Cache.getBlockCount(); 201 } 202 203 @Override 204 public long getDataBlockCount() { 205 return l1Cache.getDataBlockCount() + l2Cache.getDataBlockCount(); 206 } 207 208 public static class CombinedCacheStats extends CacheStats { 209 private final CacheStats lruCacheStats; 210 private final CacheStats bucketCacheStats; 211 212 CombinedCacheStats(CacheStats lbcStats, CacheStats fcStats) { 213 super("CombinedBlockCache"); 214 this.lruCacheStats = lbcStats; 215 this.bucketCacheStats = fcStats; 216 } 217 218 public CacheStats getLruCacheStats() { 219 return this.lruCacheStats; 220 } 221 222 public CacheStats getBucketCacheStats() { 223 return this.bucketCacheStats; 224 } 225 226 @Override 227 public long getDataMissCount() { 228 return lruCacheStats.getDataMissCount() + bucketCacheStats.getDataMissCount(); 229 } 230 231 @Override 232 public long getLeafIndexMissCount() { 233 return lruCacheStats.getLeafIndexMissCount() + bucketCacheStats.getLeafIndexMissCount(); 234 } 235 236 @Override 237 public long getBloomChunkMissCount() { 238 return lruCacheStats.getBloomChunkMissCount() + bucketCacheStats.getBloomChunkMissCount(); 239 } 240 241 @Override 242 public long getMetaMissCount() { 243 return lruCacheStats.getMetaMissCount() + bucketCacheStats.getMetaMissCount(); 244 } 245 246 @Override 247 public long getRootIndexMissCount() { 248 return lruCacheStats.getRootIndexMissCount() + bucketCacheStats.getRootIndexMissCount(); 249 } 250 251 @Override 252 public long getIntermediateIndexMissCount() { 253 return lruCacheStats.getIntermediateIndexMissCount() 254 + bucketCacheStats.getIntermediateIndexMissCount(); 255 } 256 257 @Override 258 public long getFileInfoMissCount() { 259 return lruCacheStats.getFileInfoMissCount() + bucketCacheStats.getFileInfoMissCount(); 260 } 261 262 @Override 263 public long getGeneralBloomMetaMissCount() { 264 return lruCacheStats.getGeneralBloomMetaMissCount() 265 + bucketCacheStats.getGeneralBloomMetaMissCount(); 266 } 267 268 @Override 269 public long getDeleteFamilyBloomMissCount() { 270 return lruCacheStats.getDeleteFamilyBloomMissCount() 271 + bucketCacheStats.getDeleteFamilyBloomMissCount(); 272 } 273 274 @Override 275 public long getTrailerMissCount() { 276 return lruCacheStats.getTrailerMissCount() + bucketCacheStats.getTrailerMissCount(); 277 } 278 279 @Override 280 public long getDataHitCount() { 281 return lruCacheStats.getDataHitCount() + bucketCacheStats.getDataHitCount(); 282 } 283 284 @Override 285 public long getLeafIndexHitCount() { 286 return lruCacheStats.getLeafIndexHitCount() + bucketCacheStats.getLeafIndexHitCount(); 287 } 288 289 @Override 290 public long getBloomChunkHitCount() { 291 return lruCacheStats.getBloomChunkHitCount() + bucketCacheStats.getBloomChunkHitCount(); 292 } 293 294 @Override 295 public long getMetaHitCount() { 296 return lruCacheStats.getMetaHitCount() + bucketCacheStats.getMetaHitCount(); 297 } 298 299 @Override 300 public long getRootIndexHitCount() { 301 return lruCacheStats.getRootIndexHitCount() + bucketCacheStats.getRootIndexHitCount(); 302 } 303 304 @Override 305 public long getIntermediateIndexHitCount() { 306 return lruCacheStats.getIntermediateIndexHitCount() 307 + bucketCacheStats.getIntermediateIndexHitCount(); 308 } 309 310 @Override 311 public long getFileInfoHitCount() { 312 return lruCacheStats.getFileInfoHitCount() + bucketCacheStats.getFileInfoHitCount(); 313 } 314 315 @Override 316 public long getGeneralBloomMetaHitCount() { 317 return lruCacheStats.getGeneralBloomMetaHitCount() 318 + bucketCacheStats.getGeneralBloomMetaHitCount(); 319 } 320 321 @Override 322 public long getDeleteFamilyBloomHitCount() { 323 return lruCacheStats.getDeleteFamilyBloomHitCount() 324 + bucketCacheStats.getDeleteFamilyBloomHitCount(); 325 } 326 327 @Override 328 public long getTrailerHitCount() { 329 return lruCacheStats.getTrailerHitCount() + bucketCacheStats.getTrailerHitCount(); 330 } 331 332 @Override 333 public long getRequestCount() { 334 return lruCacheStats.getRequestCount() + bucketCacheStats.getRequestCount(); 335 } 336 337 @Override 338 public long getRequestCachingCount() { 339 return lruCacheStats.getRequestCachingCount() + bucketCacheStats.getRequestCachingCount(); 340 } 341 342 @Override 343 public long getMissCount() { 344 return lruCacheStats.getMissCount() + bucketCacheStats.getMissCount(); 345 } 346 347 @Override 348 public long getPrimaryMissCount() { 349 return lruCacheStats.getPrimaryMissCount() + bucketCacheStats.getPrimaryMissCount(); 350 } 351 352 @Override 353 public long getMissCachingCount() { 354 return lruCacheStats.getMissCachingCount() + bucketCacheStats.getMissCachingCount(); 355 } 356 357 @Override 358 public long getHitCount() { 359 return lruCacheStats.getHitCount() + bucketCacheStats.getHitCount(); 360 } 361 362 @Override 363 public long getPrimaryHitCount() { 364 return lruCacheStats.getPrimaryHitCount() + bucketCacheStats.getPrimaryHitCount(); 365 } 366 367 @Override 368 public long getHitCachingCount() { 369 return lruCacheStats.getHitCachingCount() + bucketCacheStats.getHitCachingCount(); 370 } 371 372 @Override 373 public long getEvictionCount() { 374 return lruCacheStats.getEvictionCount() + bucketCacheStats.getEvictionCount(); 375 } 376 377 @Override 378 public long getEvictedCount() { 379 return lruCacheStats.getEvictedCount() + bucketCacheStats.getEvictedCount(); 380 } 381 382 @Override 383 public long getPrimaryEvictedCount() { 384 return lruCacheStats.getPrimaryEvictedCount() + bucketCacheStats.getPrimaryEvictedCount(); 385 } 386 387 @Override 388 public void rollMetricsPeriod() { 389 lruCacheStats.rollMetricsPeriod(); 390 bucketCacheStats.rollMetricsPeriod(); 391 } 392 393 @Override 394 public long getFailedInserts() { 395 return lruCacheStats.getFailedInserts() + bucketCacheStats.getFailedInserts(); 396 } 397 398 @Override 399 public long getSumHitCountsPastNPeriods() { 400 return lruCacheStats.getSumHitCountsPastNPeriods() 401 + bucketCacheStats.getSumHitCountsPastNPeriods(); 402 } 403 404 @Override 405 public long getSumRequestCountsPastNPeriods() { 406 return lruCacheStats.getSumRequestCountsPastNPeriods() 407 + bucketCacheStats.getSumRequestCountsPastNPeriods(); 408 } 409 410 @Override 411 public long getSumHitCachingCountsPastNPeriods() { 412 return lruCacheStats.getSumHitCachingCountsPastNPeriods() 413 + bucketCacheStats.getSumHitCachingCountsPastNPeriods(); 414 } 415 416 @Override 417 public long getSumRequestCachingCountsPastNPeriods() { 418 return lruCacheStats.getSumRequestCachingCountsPastNPeriods() 419 + bucketCacheStats.getSumRequestCachingCountsPastNPeriods(); 420 } 421 } 422 423 @Override 424 public Iterator<CachedBlock> iterator() { 425 return new BlockCachesIterator(getBlockCaches()); 426 } 427 428 @Override 429 public BlockCache[] getBlockCaches() { 430 return new BlockCache[] { this.l1Cache, this.l2Cache }; 431 } 432 433 /** 434 * Returns the list of fully cached files 435 */ 436 @Override 437 public Optional<Map<String, Pair<String, Long>>> getFullyCachedFiles() { 438 return this.l2Cache.getFullyCachedFiles(); 439 } 440 441 @Override 442 public Optional<Map<String, Long>> getRegionCachedInfo() { 443 return l2Cache.getRegionCachedInfo(); 444 } 445 446 @Override 447 public void setMaxSize(long size) { 448 this.l1Cache.setMaxSize(size); 449 } 450 451 public int getRpcRefCount(BlockCacheKey cacheKey) { 452 return (this.l2Cache instanceof BucketCache) 453 ? ((BucketCache) this.l2Cache).getRpcRefCount(cacheKey) 454 : 0; 455 } 456 457 public FirstLevelBlockCache getFirstLevelCache() { 458 return l1Cache; 459 } 460 461 public BlockCache getSecondLevelCache() { 462 return l2Cache; 463 } 464 465 @Override 466 public void notifyFileCachingCompleted(Path fileName, int totalBlockCount, int dataBlockCount, 467 long size) { 468 l1Cache.getBlockCount(); 469 l1Cache.notifyFileCachingCompleted(fileName, totalBlockCount, dataBlockCount, size); 470 l2Cache.notifyFileCachingCompleted(fileName, totalBlockCount, dataBlockCount, size); 471 472 } 473 474 @Override 475 public void onConfigurationChange(Configuration config) { 476 l1Cache.onConfigurationChange(config); 477 l2Cache.onConfigurationChange(config); 478 } 479 480 @Override 481 public Optional<Boolean> blockFitsIntoTheCache(HFileBlock block) { 482 if (isMetaBlock(block.getBlockType())) { 483 return l1Cache.blockFitsIntoTheCache(block); 484 } else { 485 return l2Cache.blockFitsIntoTheCache(block); 486 } 487 } 488 489 @Override 490 public Optional<Boolean> shouldCacheFile(String fileName) { 491 Optional<Boolean> l1Result = l1Cache.shouldCacheFile(fileName); 492 Optional<Boolean> l2Result = l2Cache.shouldCacheFile(fileName); 493 final Mutable<Boolean> combinedResult = new MutableBoolean(true); 494 l1Result.ifPresent(b -> combinedResult.setValue(b && combinedResult.getValue())); 495 l2Result.ifPresent(b -> combinedResult.setValue(b && combinedResult.getValue())); 496 return Optional.of(combinedResult.getValue()); 497 } 498 499 @Override 500 public Optional<Boolean> isAlreadyCached(BlockCacheKey key) { 501 boolean result = 502 l1Cache.isAlreadyCached(key).orElseGet(() -> l2Cache.isAlreadyCached(key).orElse(false)); 503 return Optional.of(result); 504 } 505 506 @Override 507 public Optional<Integer> getBlockSize(BlockCacheKey key) { 508 Optional<Integer> l1Result = l1Cache.getBlockSize(key); 509 return l1Result.isPresent() ? l1Result : l2Cache.getBlockSize(key); 510 } 511 512 @Override 513 public int evictBlocksRangeByHfileName(String hfileName, long initOffset, long endOffset) { 514 return l1Cache.evictBlocksRangeByHfileName(hfileName, initOffset, endOffset) 515 + l2Cache.evictBlocksRangeByHfileName(hfileName, initOffset, endOffset); 516 } 517 518 @Override 519 public boolean waitForCacheInitialization(long timeout) { 520 return this.l1Cache.waitForCacheInitialization(timeout) 521 && this.l2Cache.waitForCacheInitialization(timeout); 522 } 523 524 @Override 525 public boolean isCacheEnabled() { 526 return l1Cache.isCacheEnabled() && l2Cache.isCacheEnabled(); 527 } 528}