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