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