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 static org.apache.hadoop.hbase.io.ByteBuffAllocator.HEAP; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertFalse; 023import static org.junit.jupiter.api.Assertions.assertNotNull; 024import static org.junit.jupiter.api.Assertions.assertNull; 025import static org.junit.jupiter.api.Assertions.assertTrue; 026 027import java.nio.ByteBuffer; 028import java.util.Random; 029import java.util.concurrent.ExecutorService; 030import java.util.concurrent.Executors; 031import java.util.concurrent.ThreadLocalRandom; 032import java.util.concurrent.TimeUnit; 033import java.util.concurrent.atomic.AtomicBoolean; 034import java.util.concurrent.atomic.AtomicInteger; 035import org.apache.hadoop.conf.Configuration; 036import org.apache.hadoop.hbase.HBaseConfiguration; 037import org.apache.hadoop.hbase.HConstants; 038import org.apache.hadoop.hbase.Waiter; 039import org.apache.hadoop.hbase.Waiter.ExplainingPredicate; 040import org.apache.hadoop.hbase.io.HeapSize; 041import org.apache.hadoop.hbase.io.hfile.LruAdaptiveBlockCache.EvictionThread; 042import org.apache.hadoop.hbase.nio.ByteBuff; 043import org.apache.hadoop.hbase.testclassification.IOTests; 044import org.apache.hadoop.hbase.testclassification.SmallTests; 045import org.apache.hadoop.hbase.util.ClassSize; 046import org.junit.jupiter.api.Tag; 047import org.junit.jupiter.api.Test; 048import org.slf4j.Logger; 049import org.slf4j.LoggerFactory; 050 051/** 052 * Tests the concurrent LruAdaptiveBlockCache. 053 * <p> 054 * Tests will ensure it grows and shrinks in size properly, evictions run when they're supposed to 055 * and do what they should, and that cached blocks are accessible when expected to be. 056 */ 057@Tag(IOTests.TAG) 058@Tag(SmallTests.TAG) 059public class TestLruAdaptiveBlockCache { 060 061 private static final Logger LOG = LoggerFactory.getLogger(TestLruAdaptiveBlockCache.class); 062 063 private static final Configuration CONF = HBaseConfiguration.create(); 064 065 @Test 066 public void testCacheEvictionThreadSafe() throws Exception { 067 long maxSize = 100000; 068 int numBlocks = 9; 069 int testRuns = 10; 070 final long blockSize = calculateBlockSizeDefault(maxSize, numBlocks); 071 assertTrue(blockSize * numBlocks <= maxSize, "calculateBlockSize appears broken."); 072 073 final LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize); 074 EvictionThread evictionThread = cache.getEvictionThread(); 075 assertNotNull(evictionThread); 076 Waiter.waitFor(CONF, 10000, 100, () -> evictionThread.isEnteringRun()); 077 final String hfileName = "hfile"; 078 int threads = 10; 079 final int blocksPerThread = 5 * numBlocks; 080 for (int run = 0; run != testRuns; ++run) { 081 final AtomicInteger blockCount = new AtomicInteger(0); 082 ExecutorService service = Executors.newFixedThreadPool(threads); 083 for (int i = 0; i != threads; ++i) { 084 service.execute(() -> { 085 for (int blockIndex = 0; blockIndex < blocksPerThread 086 || (!cache.isEvictionInProgress()); ++blockIndex) { 087 CachedItem block = 088 new CachedItem(hfileName, (int) blockSize, blockCount.getAndIncrement()); 089 boolean inMemory = Math.random() > 0.5; 090 cache.cacheBlock(block.cacheKey, block, inMemory); 091 } 092 cache.evictBlocksByHfileName(hfileName); 093 }); 094 } 095 service.shutdown(); 096 // The test may fail here if the evict thread frees the blocks too fast 097 service.awaitTermination(10, TimeUnit.MINUTES); 098 Waiter.waitFor(CONF, 10000, 100, new ExplainingPredicate<Exception>() { 099 @Override 100 public boolean evaluate() throws Exception { 101 return cache.getBlockCount() == 0; 102 } 103 104 @Override 105 public String explainFailure() throws Exception { 106 return "Cache block count failed to return to 0"; 107 } 108 }); 109 assertEquals(0, cache.getBlockCount()); 110 assertEquals(cache.getOverhead(), cache.getCurrentSize()); 111 } 112 } 113 114 @Test 115 public void testBackgroundEvictionThread() throws Exception { 116 long maxSize = 100000; 117 int numBlocks = 9; 118 long blockSize = calculateBlockSizeDefault(maxSize, numBlocks); 119 assertTrue(blockSize * numBlocks <= maxSize, "calculateBlockSize appears broken."); 120 121 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize); 122 EvictionThread evictionThread = cache.getEvictionThread(); 123 assertNotNull(evictionThread); 124 125 CachedItem[] blocks = generateFixedBlocks(numBlocks + 1, blockSize, "block"); 126 127 // Make sure eviction thread has entered run method 128 Waiter.waitFor(CONF, 10000, 10, () -> evictionThread.isEnteringRun()); 129 130 // Add all the blocks 131 for (CachedItem block : blocks) { 132 cache.cacheBlock(block.cacheKey, block); 133 } 134 135 // wait until at least one eviction has run 136 Waiter.waitFor(CONF, 30000, 200, new ExplainingPredicate<Exception>() { 137 138 @Override 139 public boolean evaluate() throws Exception { 140 return cache.getStats().getEvictionCount() > 0; 141 } 142 143 @Override 144 public String explainFailure() throws Exception { 145 return "Eviction never happened."; 146 } 147 }); 148 149 // let cache stabilize 150 // On some systems, the cache will run multiple evictions before it attains 151 // steady-state. For instance, after populating the cache with 10 blocks, 152 // the first eviction evicts a single block and then a second eviction 153 // evicts another. I think this is due to the delta between minSize and 154 // acceptableSize, combined with variance between object overhead on 155 // different environments. 156 int n = 0; 157 for (long prevCnt = 0 /* < number of blocks added */, curCnt = cache.getBlockCount(); prevCnt 158 != curCnt; prevCnt = curCnt, curCnt = cache.getBlockCount()) { 159 Thread.sleep(200); 160 assertTrue(n++ < 100, "Cache never stabilized."); 161 } 162 163 long evictionCount = cache.getStats().getEvictionCount(); 164 assertTrue(evictionCount >= 1); 165 LOG.info("Background Evictions run: {}", evictionCount); 166 } 167 168 @Test 169 public void testCacheSimple() throws Exception { 170 long maxSize = 1000000; 171 long blockSize = calculateBlockSizeDefault(maxSize, 101); 172 173 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize); 174 175 CachedItem[] blocks = generateRandomBlocks(100, blockSize); 176 177 long expectedCacheSize = cache.heapSize(); 178 179 // Confirm empty 180 for (CachedItem block : blocks) { 181 assertTrue(cache.getBlock(block.cacheKey, true, false, true) == null); 182 } 183 184 // Add blocks 185 for (CachedItem block : blocks) { 186 cache.cacheBlock(block.cacheKey, block); 187 expectedCacheSize += block.cacheBlockHeapSize(); 188 } 189 190 // Verify correctly calculated cache heap size 191 assertEquals(expectedCacheSize, cache.heapSize()); 192 193 // Check if all blocks are properly cached and retrieved 194 for (CachedItem block : blocks) { 195 HeapSize buf = cache.getBlock(block.cacheKey, true, false, true); 196 assertTrue(buf != null); 197 assertEquals(buf.heapSize(), block.heapSize()); 198 } 199 200 // Re-add same blocks and ensure nothing has changed 201 long expectedBlockCount = cache.getBlockCount(); 202 for (CachedItem block : blocks) { 203 cache.cacheBlock(block.cacheKey, block); 204 } 205 assertEquals(expectedBlockCount, cache.getBlockCount(), 206 "Cache should ignore cache requests for blocks already in cache"); 207 208 // Verify correctly calculated cache heap size 209 assertEquals(expectedCacheSize, cache.heapSize()); 210 211 // Check if all blocks are properly cached and retrieved 212 for (CachedItem block : blocks) { 213 HeapSize buf = cache.getBlock(block.cacheKey, true, false, true); 214 assertTrue(buf != null); 215 assertEquals(buf.heapSize(), block.heapSize()); 216 } 217 218 CacheTestUtils.testConvertToJSON(cache); 219 220 // Expect no evictions 221 assertEquals(0, cache.getStats().getEvictionCount()); 222 Thread t = new LruAdaptiveBlockCache.StatisticsThread(cache); 223 t.start(); 224 t.join(); 225 } 226 227 @Test 228 public void testCacheEvictionSimple() throws Exception { 229 long maxSize = 100000; 230 long blockSize = calculateBlockSizeDefault(maxSize, 10); 231 232 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false); 233 234 CachedItem[] blocks = generateFixedBlocks(10, blockSize, "block"); 235 236 long expectedCacheSize = cache.heapSize(); 237 238 // Add all the blocks 239 for (CachedItem block : blocks) { 240 cache.cacheBlock(block.cacheKey, block); 241 expectedCacheSize += block.cacheBlockHeapSize(); 242 } 243 244 // A single eviction run should have occurred 245 assertEquals(1, cache.getStats().getEvictionCount()); 246 247 // Our expected size overruns acceptable limit 248 assertTrue(expectedCacheSize > (maxSize * LruAdaptiveBlockCache.DEFAULT_ACCEPTABLE_FACTOR)); 249 250 // But the cache did not grow beyond max 251 assertTrue(cache.heapSize() < maxSize); 252 253 // And is still below the acceptable limit 254 assertTrue(cache.heapSize() < (maxSize * LruAdaptiveBlockCache.DEFAULT_ACCEPTABLE_FACTOR)); 255 256 // All blocks except block 0 should be in the cache 257 assertTrue(cache.getBlock(blocks[0].cacheKey, true, false, true) == null); 258 for (int i = 1; i < blocks.length; i++) { 259 assertEquals(cache.getBlock(blocks[i].cacheKey, true, false, true), blocks[i]); 260 } 261 } 262 263 @Test 264 public void testCacheEvictionTwoPriorities() throws Exception { 265 long maxSize = 100000; 266 long blockSize = calculateBlockSizeDefault(maxSize, 10); 267 268 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false); 269 270 CachedItem[] singleBlocks = generateFixedBlocks(5, 10000, "single"); 271 CachedItem[] multiBlocks = generateFixedBlocks(5, 10000, "multi"); 272 273 long expectedCacheSize = cache.heapSize(); 274 275 // Add and get the multi blocks 276 for (CachedItem block : multiBlocks) { 277 cache.cacheBlock(block.cacheKey, block); 278 expectedCacheSize += block.cacheBlockHeapSize(); 279 assertEquals(cache.getBlock(block.cacheKey, true, false, true), block); 280 } 281 282 // Add the single blocks (no get) 283 for (CachedItem block : singleBlocks) { 284 cache.cacheBlock(block.cacheKey, block); 285 expectedCacheSize += block.heapSize(); 286 } 287 288 // A single eviction run should have occurred 289 assertEquals(1, cache.getStats().getEvictionCount()); 290 291 // We expect two entries evicted 292 assertEquals(2, cache.getStats().getEvictedCount()); 293 294 // Our expected size overruns acceptable limit 295 assertTrue(expectedCacheSize > (maxSize * LruAdaptiveBlockCache.DEFAULT_ACCEPTABLE_FACTOR)); 296 297 // But the cache did not grow beyond max 298 assertTrue(cache.heapSize() <= maxSize); 299 300 // And is now below the acceptable limit 301 assertTrue(cache.heapSize() <= (maxSize * LruAdaptiveBlockCache.DEFAULT_ACCEPTABLE_FACTOR)); 302 303 // We expect fairness across the two priorities. 304 // This test makes multi go barely over its limit, in-memory 305 // empty, and the rest in single. Two single evictions and 306 // one multi eviction expected. 307 assertTrue(cache.getBlock(singleBlocks[0].cacheKey, true, false, true) == null); 308 assertTrue(cache.getBlock(multiBlocks[0].cacheKey, true, false, true) == null); 309 310 // And all others to be cached 311 for (int i = 1; i < 4; i++) { 312 assertEquals(cache.getBlock(singleBlocks[i].cacheKey, true, false, true), singleBlocks[i]); 313 assertEquals(cache.getBlock(multiBlocks[i].cacheKey, true, false, true), multiBlocks[i]); 314 } 315 } 316 317 @Test 318 public void testCacheEvictionThreePriorities() throws Exception { 319 long maxSize = 100000; 320 long blockSize = calculateBlockSize(maxSize, 10); 321 322 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 323 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 324 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.98f, // min 325 0.99f, // acceptable 326 0.33f, // single 327 0.33f, // multi 328 0.34f, // memory 329 1.2f, // limit 330 false, 16 * 1024 * 1024, 10, 500, 0.01f); 331 332 CachedItem[] singleBlocks = generateFixedBlocks(5, blockSize, "single"); 333 CachedItem[] multiBlocks = generateFixedBlocks(5, blockSize, "multi"); 334 CachedItem[] memoryBlocks = generateFixedBlocks(5, blockSize, "memory"); 335 336 long expectedCacheSize = cache.heapSize(); 337 338 // Add 3 blocks from each priority 339 for (int i = 0; i < 3; i++) { 340 341 // Just add single blocks 342 cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]); 343 expectedCacheSize += singleBlocks[i].cacheBlockHeapSize(); 344 345 // Add and get multi blocks 346 cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]); 347 expectedCacheSize += multiBlocks[i].cacheBlockHeapSize(); 348 cache.getBlock(multiBlocks[i].cacheKey, true, false, true); 349 350 // Add memory blocks as such 351 cache.cacheBlock(memoryBlocks[i].cacheKey, memoryBlocks[i], true); 352 expectedCacheSize += memoryBlocks[i].cacheBlockHeapSize(); 353 354 } 355 356 // Do not expect any evictions yet 357 assertEquals(0, cache.getStats().getEvictionCount()); 358 359 // Verify cache size 360 assertEquals(expectedCacheSize, cache.heapSize()); 361 362 // Insert a single block, oldest single should be evicted 363 cache.cacheBlock(singleBlocks[3].cacheKey, singleBlocks[3]); 364 365 // Single eviction, one thing evicted 366 assertEquals(1, cache.getStats().getEvictionCount()); 367 assertEquals(1, cache.getStats().getEvictedCount()); 368 369 // Verify oldest single block is the one evicted 370 assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false, true)); 371 372 // Change the oldest remaining single block to a multi 373 cache.getBlock(singleBlocks[1].cacheKey, true, false, true); 374 375 // Insert another single block 376 cache.cacheBlock(singleBlocks[4].cacheKey, singleBlocks[4]); 377 378 // Two evictions, two evicted. 379 assertEquals(2, cache.getStats().getEvictionCount()); 380 assertEquals(2, cache.getStats().getEvictedCount()); 381 382 // Oldest multi block should be evicted now 383 assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false, true)); 384 385 // Insert another memory block 386 cache.cacheBlock(memoryBlocks[3].cacheKey, memoryBlocks[3], true); 387 388 // Three evictions, three evicted. 389 assertEquals(3, cache.getStats().getEvictionCount()); 390 assertEquals(3, cache.getStats().getEvictedCount()); 391 392 // Oldest memory block should be evicted now 393 assertEquals(null, cache.getBlock(memoryBlocks[0].cacheKey, true, false, true)); 394 395 // Add a block that is twice as big (should force two evictions) 396 CachedItem[] bigBlocks = generateFixedBlocks(3, blockSize * 3, "big"); 397 cache.cacheBlock(bigBlocks[0].cacheKey, bigBlocks[0]); 398 399 // Four evictions, six evicted (inserted block 3X size, expect +3 evicted) 400 assertEquals(4, cache.getStats().getEvictionCount()); 401 assertEquals(6, cache.getStats().getEvictedCount()); 402 403 // Expect three remaining singles to be evicted 404 assertEquals(null, cache.getBlock(singleBlocks[2].cacheKey, true, false, true)); 405 assertEquals(null, cache.getBlock(singleBlocks[3].cacheKey, true, false, true)); 406 assertEquals(null, cache.getBlock(singleBlocks[4].cacheKey, true, false, true)); 407 408 // Make the big block a multi block 409 cache.getBlock(bigBlocks[0].cacheKey, true, false, true); 410 411 // Cache another single big block 412 cache.cacheBlock(bigBlocks[1].cacheKey, bigBlocks[1]); 413 414 // Five evictions, nine evicted (3 new) 415 assertEquals(5, cache.getStats().getEvictionCount()); 416 assertEquals(9, cache.getStats().getEvictedCount()); 417 418 // Expect three remaining multis to be evicted 419 assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false, true)); 420 assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false, true)); 421 assertEquals(null, cache.getBlock(multiBlocks[2].cacheKey, true, false, true)); 422 423 // Cache a big memory block 424 cache.cacheBlock(bigBlocks[2].cacheKey, bigBlocks[2], true); 425 426 // Six evictions, twelve evicted (3 new) 427 assertEquals(6, cache.getStats().getEvictionCount()); 428 assertEquals(12, cache.getStats().getEvictedCount()); 429 430 // Expect three remaining in-memory to be evicted 431 assertEquals(null, cache.getBlock(memoryBlocks[1].cacheKey, true, false, true)); 432 assertEquals(null, cache.getBlock(memoryBlocks[2].cacheKey, true, false, true)); 433 assertEquals(null, cache.getBlock(memoryBlocks[3].cacheKey, true, false, true)); 434 } 435 436 @Test 437 public void testCacheEvictionInMemoryForceMode() throws Exception { 438 long maxSize = 100000; 439 long blockSize = calculateBlockSize(maxSize, 10); 440 441 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 442 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 443 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.98f, // min 444 0.99f, // acceptable 445 0.2f, // single 446 0.3f, // multi 447 0.5f, // memory 448 1.2f, // limit 449 true, 16 * 1024 * 1024, 10, 500, 0.01f); 450 451 CachedItem[] singleBlocks = generateFixedBlocks(10, blockSize, "single"); 452 CachedItem[] multiBlocks = generateFixedBlocks(10, blockSize, "multi"); 453 CachedItem[] memoryBlocks = generateFixedBlocks(10, blockSize, "memory"); 454 455 long expectedCacheSize = cache.heapSize(); 456 457 // 0. Add 5 single blocks and 4 multi blocks to make cache full, si:mu:me = 5:4:0 458 for (int i = 0; i < 4; i++) { 459 // Just add single blocks 460 cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]); 461 expectedCacheSize += singleBlocks[i].cacheBlockHeapSize(); 462 // Add and get multi blocks 463 cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]); 464 expectedCacheSize += multiBlocks[i].cacheBlockHeapSize(); 465 cache.getBlock(multiBlocks[i].cacheKey, true, false, true); 466 } 467 // 5th single block 468 cache.cacheBlock(singleBlocks[4].cacheKey, singleBlocks[4]); 469 expectedCacheSize += singleBlocks[4].cacheBlockHeapSize(); 470 // Do not expect any evictions yet 471 assertEquals(0, cache.getStats().getEvictionCount()); 472 // Verify cache size 473 assertEquals(expectedCacheSize, cache.heapSize()); 474 475 // 1. Insert a memory block, oldest single should be evicted, si:mu:me = 4:4:1 476 cache.cacheBlock(memoryBlocks[0].cacheKey, memoryBlocks[0], true); 477 // Single eviction, one block evicted 478 assertEquals(1, cache.getStats().getEvictionCount()); 479 assertEquals(1, cache.getStats().getEvictedCount()); 480 // Verify oldest single block (index = 0) is the one evicted 481 assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false, true)); 482 483 // 2. Insert another memory block, another single evicted, si:mu:me = 3:4:2 484 cache.cacheBlock(memoryBlocks[1].cacheKey, memoryBlocks[1], true); 485 // Two evictions, two evicted. 486 assertEquals(2, cache.getStats().getEvictionCount()); 487 assertEquals(2, cache.getStats().getEvictedCount()); 488 // Current oldest single block (index = 1) should be evicted now 489 assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false, true)); 490 491 // 3. Insert 4 memory blocks, 2 single and 2 multi evicted, si:mu:me = 1:2:6 492 cache.cacheBlock(memoryBlocks[2].cacheKey, memoryBlocks[2], true); 493 cache.cacheBlock(memoryBlocks[3].cacheKey, memoryBlocks[3], true); 494 cache.cacheBlock(memoryBlocks[4].cacheKey, memoryBlocks[4], true); 495 cache.cacheBlock(memoryBlocks[5].cacheKey, memoryBlocks[5], true); 496 // Three evictions, three evicted. 497 assertEquals(6, cache.getStats().getEvictionCount()); 498 assertEquals(6, cache.getStats().getEvictedCount()); 499 // two oldest single blocks and two oldest multi blocks evicted 500 assertEquals(null, cache.getBlock(singleBlocks[2].cacheKey, true, false, true)); 501 assertEquals(null, cache.getBlock(singleBlocks[3].cacheKey, true, false, true)); 502 assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false, true)); 503 assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false, true)); 504 505 // 4. Insert 3 memory blocks, the remaining 1 single and 2 multi evicted 506 // si:mu:me = 0:0:9 507 cache.cacheBlock(memoryBlocks[6].cacheKey, memoryBlocks[6], true); 508 cache.cacheBlock(memoryBlocks[7].cacheKey, memoryBlocks[7], true); 509 cache.cacheBlock(memoryBlocks[8].cacheKey, memoryBlocks[8], true); 510 // Three evictions, three evicted. 511 assertEquals(9, cache.getStats().getEvictionCount()); 512 assertEquals(9, cache.getStats().getEvictedCount()); 513 // one oldest single block and two oldest multi blocks evicted 514 assertEquals(null, cache.getBlock(singleBlocks[4].cacheKey, true, false, true)); 515 assertEquals(null, cache.getBlock(multiBlocks[2].cacheKey, true, false, true)); 516 assertEquals(null, cache.getBlock(multiBlocks[3].cacheKey, true, false, true)); 517 518 // 5. Insert one memory block, the oldest memory evicted 519 // si:mu:me = 0:0:9 520 cache.cacheBlock(memoryBlocks[9].cacheKey, memoryBlocks[9], true); 521 // one eviction, one evicted. 522 assertEquals(10, cache.getStats().getEvictionCount()); 523 assertEquals(10, cache.getStats().getEvictedCount()); 524 // oldest memory block evicted 525 assertEquals(null, cache.getBlock(memoryBlocks[0].cacheKey, true, false, true)); 526 527 // 6. Insert one new single block, itself evicted immediately since 528 // all blocks in cache are memory-type which have higher priority 529 // si:mu:me = 0:0:9 (no change) 530 cache.cacheBlock(singleBlocks[9].cacheKey, singleBlocks[9]); 531 // one eviction, one evicted. 532 assertEquals(11, cache.getStats().getEvictionCount()); 533 assertEquals(11, cache.getStats().getEvictedCount()); 534 // the single block just cached now evicted (can't evict memory) 535 assertEquals(null, cache.getBlock(singleBlocks[9].cacheKey, true, false, true)); 536 } 537 538 // test scan resistance 539 @Test 540 public void testScanResistance() throws Exception { 541 542 long maxSize = 100000; 543 long blockSize = calculateBlockSize(maxSize, 10); 544 545 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 546 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 547 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.66f, // min 548 0.99f, // acceptable 549 0.33f, // single 550 0.33f, // multi 551 0.34f, // memory 552 1.2f, // limit 553 false, 16 * 1024 * 1024, 10, 500, 0.01f); 554 555 CachedItem[] singleBlocks = generateFixedBlocks(20, blockSize, "single"); 556 CachedItem[] multiBlocks = generateFixedBlocks(5, blockSize, "multi"); 557 558 // Add 5 multi blocks 559 for (CachedItem block : multiBlocks) { 560 cache.cacheBlock(block.cacheKey, block); 561 cache.getBlock(block.cacheKey, true, false, true); 562 } 563 564 // Add 5 single blocks 565 for (int i = 0; i < 5; i++) { 566 cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]); 567 } 568 569 // An eviction ran 570 assertEquals(1, cache.getStats().getEvictionCount()); 571 572 // To drop down to 2/3 capacity, we'll need to evict 4 blocks 573 assertEquals(4, cache.getStats().getEvictedCount()); 574 575 // Should have been taken off equally from single and multi 576 assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false, true)); 577 assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false, true)); 578 assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false, true)); 579 assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false, true)); 580 581 // Let's keep "scanning" by adding single blocks. From here on we only 582 // expect evictions from the single bucket. 583 584 // Every time we reach 10 total blocks (every 4 inserts) we get 4 single 585 // blocks evicted. Inserting 13 blocks should yield 3 more evictions and 586 // 12 more evicted. 587 588 for (int i = 5; i < 18; i++) { 589 cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]); 590 } 591 592 // 4 total evictions, 16 total evicted 593 assertEquals(4, cache.getStats().getEvictionCount()); 594 assertEquals(16, cache.getStats().getEvictedCount()); 595 596 // Should now have 7 total blocks 597 assertEquals(7, cache.getBlockCount()); 598 599 } 600 601 @Test 602 public void testMaxBlockSize() throws Exception { 603 long maxSize = 100000; 604 long blockSize = calculateBlockSize(maxSize, 10); 605 606 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 607 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 608 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.66f, // min 609 0.99f, // acceptable 610 0.33f, // single 611 0.33f, // multi 612 0.34f, // memory 613 1.2f, // limit 614 false, 1024, 10, 500, 0.01f); 615 616 CachedItem[] tooLong = generateFixedBlocks(10, 1024 + 5, "long"); 617 CachedItem[] small = generateFixedBlocks(15, 600, "small"); 618 619 for (CachedItem i : tooLong) { 620 cache.cacheBlock(i.cacheKey, i); 621 } 622 for (CachedItem i : small) { 623 cache.cacheBlock(i.cacheKey, i); 624 } 625 assertEquals(15, cache.getBlockCount()); 626 for (CachedItem i : small) { 627 assertNotNull(cache.getBlock(i.cacheKey, true, false, false)); 628 } 629 for (CachedItem i : tooLong) { 630 assertNull(cache.getBlock(i.cacheKey, true, false, false)); 631 } 632 633 assertEquals(10, cache.getStats().getFailedInserts()); 634 } 635 636 // test setMaxSize 637 @Test 638 public void testResizeBlockCache() throws Exception { 639 long maxSize = 300000; 640 long blockSize = calculateBlockSize(maxSize, 31); 641 642 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 643 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 644 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.98f, // min 645 0.99f, // acceptable 646 0.33f, // single 647 0.33f, // multi 648 0.34f, // memory 649 1.2f, // limit 650 false, 16 * 1024 * 1024, 10, 500, 0.01f); 651 652 CachedItem[] singleBlocks = generateFixedBlocks(10, blockSize, "single"); 653 CachedItem[] multiBlocks = generateFixedBlocks(10, blockSize, "multi"); 654 CachedItem[] memoryBlocks = generateFixedBlocks(10, blockSize, "memory"); 655 656 // Add all blocks from all priorities 657 for (int i = 0; i < 10; i++) { 658 // Just add single blocks 659 cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]); 660 661 // Add and get multi blocks 662 cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]); 663 cache.getBlock(multiBlocks[i].cacheKey, true, false, true); 664 665 // Add memory blocks as such 666 cache.cacheBlock(memoryBlocks[i].cacheKey, memoryBlocks[i], true); 667 } 668 669 // Do not expect any evictions yet 670 assertEquals(0, cache.getStats().getEvictionCount()); 671 672 // Resize to half capacity plus an extra block (otherwise we evict an extra) 673 cache.setMaxSize((long) (maxSize * 0.5f)); 674 675 // Should have run a single eviction 676 assertEquals(1, cache.getStats().getEvictionCount()); 677 678 // And we expect 1/2 of the blocks to be evicted 679 assertEquals(15, cache.getStats().getEvictedCount()); 680 681 // And the oldest 5 blocks from each category should be gone 682 for (int i = 0; i < 5; i++) { 683 assertEquals(null, cache.getBlock(singleBlocks[i].cacheKey, true, false, true)); 684 assertEquals(null, cache.getBlock(multiBlocks[i].cacheKey, true, false, true)); 685 assertEquals(null, cache.getBlock(memoryBlocks[i].cacheKey, true, false, true)); 686 } 687 688 // And the newest 5 blocks should still be accessible 689 for (int i = 5; i < 10; i++) { 690 assertEquals(singleBlocks[i], cache.getBlock(singleBlocks[i].cacheKey, true, false, true)); 691 assertEquals(multiBlocks[i], cache.getBlock(multiBlocks[i].cacheKey, true, false, true)); 692 assertEquals(memoryBlocks[i], cache.getBlock(memoryBlocks[i].cacheKey, true, false, true)); 693 } 694 } 695 696 // test metricsPastNPeriods 697 @Test 698 public void testPastNPeriodsMetrics() throws Exception { 699 double delta = 0.01; 700 701 // 3 total periods 702 CacheStats stats = new CacheStats("test", 3); 703 704 // No accesses, should be 0 705 stats.rollMetricsPeriod(); 706 assertEquals(0.0, stats.getHitRatioPastNPeriods(), delta); 707 assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta); 708 709 // period 1, 1 hit caching, 1 hit non-caching, 2 miss non-caching 710 // should be (2/4)=0.5 and (1/1)=1 711 stats.hit(false, true, BlockType.DATA); 712 stats.hit(true, true, BlockType.DATA); 713 stats.miss(false, false, BlockType.DATA); 714 stats.miss(false, false, BlockType.DATA); 715 stats.rollMetricsPeriod(); 716 assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta); 717 assertEquals(1.0, stats.getHitCachingRatioPastNPeriods(), delta); 718 719 // period 2, 1 miss caching, 3 miss non-caching 720 // should be (2/8)=0.25 and (1/2)=0.5 721 stats.miss(true, false, BlockType.DATA); 722 stats.miss(false, false, BlockType.DATA); 723 stats.miss(false, false, BlockType.DATA); 724 stats.miss(false, false, BlockType.DATA); 725 stats.rollMetricsPeriod(); 726 assertEquals(0.25, stats.getHitRatioPastNPeriods(), delta); 727 assertEquals(0.5, stats.getHitCachingRatioPastNPeriods(), delta); 728 729 // period 3, 2 hits of each type 730 // should be (6/12)=0.5 and (3/4)=0.75 731 stats.hit(false, true, BlockType.DATA); 732 stats.hit(true, true, BlockType.DATA); 733 stats.hit(false, true, BlockType.DATA); 734 stats.hit(true, true, BlockType.DATA); 735 stats.rollMetricsPeriod(); 736 assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta); 737 assertEquals(0.75, stats.getHitCachingRatioPastNPeriods(), delta); 738 739 // period 4, evict period 1, two caching misses 740 // should be (4/10)=0.4 and (2/5)=0.4 741 stats.miss(true, false, BlockType.DATA); 742 stats.miss(true, false, BlockType.DATA); 743 stats.rollMetricsPeriod(); 744 assertEquals(0.4, stats.getHitRatioPastNPeriods(), delta); 745 assertEquals(0.4, stats.getHitCachingRatioPastNPeriods(), delta); 746 747 // period 5, evict period 2, 2 caching misses, 2 non-caching hit 748 // should be (6/10)=0.6 and (2/6)=1/3 749 stats.miss(true, false, BlockType.DATA); 750 stats.miss(true, false, BlockType.DATA); 751 stats.hit(false, true, BlockType.DATA); 752 stats.hit(false, true, BlockType.DATA); 753 stats.rollMetricsPeriod(); 754 assertEquals(0.6, stats.getHitRatioPastNPeriods(), delta); 755 assertEquals((double) 1 / 3, stats.getHitCachingRatioPastNPeriods(), delta); 756 757 // period 6, evict period 3 758 // should be (2/6)=1/3 and (0/4)=0 759 stats.rollMetricsPeriod(); 760 assertEquals((double) 1 / 3, stats.getHitRatioPastNPeriods(), delta); 761 assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta); 762 763 // period 7, evict period 4 764 // should be (2/4)=0.5 and (0/2)=0 765 stats.rollMetricsPeriod(); 766 assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta); 767 assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta); 768 769 // period 8, evict period 5 770 // should be 0 and 0 771 stats.rollMetricsPeriod(); 772 assertEquals(0.0, stats.getHitRatioPastNPeriods(), delta); 773 assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta); 774 775 // period 9, one of each 776 // should be (2/4)=0.5 and (1/2)=0.5 777 stats.miss(true, false, BlockType.DATA); 778 stats.miss(false, false, BlockType.DATA); 779 stats.hit(true, true, BlockType.DATA); 780 stats.hit(false, true, BlockType.DATA); 781 stats.rollMetricsPeriod(); 782 assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta); 783 assertEquals(0.5, stats.getHitCachingRatioPastNPeriods(), delta); 784 } 785 786 @Test 787 public void testCacheBlockNextBlockMetadataMissing() { 788 long maxSize = 100000; 789 long blockSize = calculateBlockSize(maxSize, 10); 790 int size = 100; 791 int length = HConstants.HFILEBLOCK_HEADER_SIZE + size; 792 byte[] byteArr = new byte[length]; 793 ByteBuffer buf = ByteBuffer.wrap(byteArr, 0, size); 794 HFileContext meta = new HFileContextBuilder().build(); 795 HFileBlock blockWithNextBlockMetadata = new HFileBlock(BlockType.DATA, size, size, -1, 796 ByteBuff.wrap(buf), HFileBlock.FILL_HEADER, -1, 52, -1, meta, HEAP); 797 HFileBlock blockWithoutNextBlockMetadata = new HFileBlock(BlockType.DATA, size, size, -1, 798 ByteBuff.wrap(buf), HFileBlock.FILL_HEADER, -1, -1, -1, meta, HEAP); 799 800 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 801 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 802 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.66f, // min 803 0.99f, // acceptable 804 0.33f, // single 805 0.33f, // multi 806 0.34f, // memory 807 1.2f, // limit 808 false, 1024, 10, 500, 0.01f); 809 810 BlockCacheKey key = new BlockCacheKey("key1", 0); 811 ByteBuffer actualBuffer = ByteBuffer.allocate(length); 812 ByteBuffer block1Buffer = ByteBuffer.allocate(length); 813 ByteBuffer block2Buffer = ByteBuffer.allocate(length); 814 blockWithNextBlockMetadata.serialize(block1Buffer, true); 815 blockWithoutNextBlockMetadata.serialize(block2Buffer, true); 816 817 // Add blockWithNextBlockMetadata, expect blockWithNextBlockMetadata back. 818 CacheTestUtils.getBlockAndAssertEquals(cache, key, blockWithNextBlockMetadata, actualBuffer, 819 block1Buffer); 820 821 // Add blockWithoutNextBlockMetada, expect blockWithNextBlockMetadata back. 822 CacheTestUtils.getBlockAndAssertEquals(cache, key, blockWithoutNextBlockMetadata, actualBuffer, 823 block1Buffer); 824 825 // Clear and add blockWithoutNextBlockMetadata 826 cache.clearCache(); 827 assertNull(cache.getBlock(key, false, false, false)); 828 CacheTestUtils.getBlockAndAssertEquals(cache, key, blockWithoutNextBlockMetadata, actualBuffer, 829 block2Buffer); 830 831 // Add blockWithNextBlockMetadata, expect blockWithNextBlockMetadata to replace. 832 CacheTestUtils.getBlockAndAssertEquals(cache, key, blockWithNextBlockMetadata, actualBuffer, 833 block1Buffer); 834 } 835 836 private CachedItem[] generateFixedBlocks(int numBlocks, int size, String pfx) { 837 CachedItem[] blocks = new CachedItem[numBlocks]; 838 for (int i = 0; i < numBlocks; i++) { 839 blocks[i] = new CachedItem(pfx + i, size); 840 } 841 return blocks; 842 } 843 844 private CachedItem[] generateFixedBlocks(int numBlocks, long size, String pfx) { 845 return generateFixedBlocks(numBlocks, (int) size, pfx); 846 } 847 848 private CachedItem[] generateRandomBlocks(int numBlocks, long maxSize) { 849 CachedItem[] blocks = new CachedItem[numBlocks]; 850 Random rand = ThreadLocalRandom.current(); 851 for (int i = 0; i < numBlocks; i++) { 852 blocks[i] = new CachedItem("block" + i, rand.nextInt((int) maxSize) + 1); 853 } 854 return blocks; 855 } 856 857 private long calculateBlockSize(long maxSize, int numBlocks) { 858 long roughBlockSize = maxSize / numBlocks; 859 int numEntries = (int) Math.ceil((1.2) * maxSize / roughBlockSize); 860 long totalOverhead = LruAdaptiveBlockCache.CACHE_FIXED_OVERHEAD + ClassSize.CONCURRENT_HASHMAP 861 + (numEntries * ClassSize.CONCURRENT_HASHMAP_ENTRY) 862 + (LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL * ClassSize.CONCURRENT_HASHMAP_SEGMENT); 863 long negateBlockSize = (long) (totalOverhead / numEntries); 864 negateBlockSize += LruCachedBlock.PER_BLOCK_OVERHEAD; 865 return ClassSize.align((long) Math.floor((roughBlockSize - negateBlockSize) * 0.99f)); 866 } 867 868 private long calculateBlockSizeDefault(long maxSize, int numBlocks) { 869 long roughBlockSize = maxSize / numBlocks; 870 int numEntries = (int) Math.ceil((1.2) * maxSize / roughBlockSize); 871 long totalOverhead = LruAdaptiveBlockCache.CACHE_FIXED_OVERHEAD + ClassSize.CONCURRENT_HASHMAP 872 + (numEntries * ClassSize.CONCURRENT_HASHMAP_ENTRY) 873 + (LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL * ClassSize.CONCURRENT_HASHMAP_SEGMENT); 874 long negateBlockSize = totalOverhead / numEntries; 875 negateBlockSize += LruCachedBlock.PER_BLOCK_OVERHEAD; 876 return ClassSize.align((long) Math 877 .floor((roughBlockSize - negateBlockSize) * LruAdaptiveBlockCache.DEFAULT_ACCEPTABLE_FACTOR)); 878 } 879 880 private static class CachedItem implements Cacheable { 881 BlockCacheKey cacheKey; 882 int size; 883 884 CachedItem(String blockName, int size, int offset) { 885 this.cacheKey = new BlockCacheKey(blockName, offset); 886 this.size = size; 887 } 888 889 CachedItem(String blockName, int size) { 890 this.cacheKey = new BlockCacheKey(blockName, 0); 891 this.size = size; 892 } 893 894 /** The size of this item reported to the block cache layer */ 895 @Override 896 public long heapSize() { 897 return ClassSize.align(size); 898 } 899 900 /** Size of the cache block holding this item. Used for verification. */ 901 public long cacheBlockHeapSize() { 902 return LruCachedBlock.PER_BLOCK_OVERHEAD + ClassSize.align(cacheKey.heapSize()) 903 + ClassSize.align(size); 904 } 905 906 @Override 907 public int getSerializedLength() { 908 return 0; 909 } 910 911 @Override 912 public CacheableDeserializer<Cacheable> getDeserializer() { 913 return null; 914 } 915 916 @Override 917 public void serialize(ByteBuffer destination, boolean includeNextBlockMetadata) { 918 } 919 920 @Override 921 public BlockType getBlockType() { 922 return BlockType.DATA; 923 } 924 } 925 926 static void testMultiThreadGetAndEvictBlockInternal(BlockCache cache) throws Exception { 927 int size = 100; 928 int length = HConstants.HFILEBLOCK_HEADER_SIZE + size; 929 byte[] byteArr = new byte[length]; 930 HFileContext meta = new HFileContextBuilder().build(); 931 BlockCacheKey key = new BlockCacheKey("key1", 0); 932 HFileBlock blk = new HFileBlock(BlockType.DATA, size, size, -1, 933 ByteBuff.wrap(ByteBuffer.wrap(byteArr, 0, size)), HFileBlock.FILL_HEADER, -1, 52, -1, meta, 934 HEAP); 935 AtomicBoolean err1 = new AtomicBoolean(false); 936 Thread t1 = new Thread(() -> { 937 for (int i = 0; i < 10000 && !err1.get(); i++) { 938 try { 939 cache.getBlock(key, false, false, true); 940 } catch (Exception e) { 941 err1.set(true); 942 LOG.info("Cache block or get block failure: ", e); 943 } 944 } 945 }); 946 947 AtomicBoolean err2 = new AtomicBoolean(false); 948 Thread t2 = new Thread(() -> { 949 for (int i = 0; i < 10000 && !err2.get(); i++) { 950 try { 951 cache.evictBlock(key); 952 } catch (Exception e) { 953 err2.set(true); 954 LOG.info("Evict block failure: ", e); 955 } 956 } 957 }); 958 959 AtomicBoolean err3 = new AtomicBoolean(false); 960 Thread t3 = new Thread(() -> { 961 for (int i = 0; i < 10000 && !err3.get(); i++) { 962 try { 963 cache.cacheBlock(key, blk); 964 } catch (Exception e) { 965 err3.set(true); 966 LOG.info("Cache block failure: ", e); 967 } 968 } 969 }); 970 t1.start(); 971 t2.start(); 972 t3.start(); 973 t1.join(); 974 t2.join(); 975 t3.join(); 976 assertFalse(err1.get()); 977 assertFalse(err2.get()); 978 assertFalse(err3.get()); 979 } 980 981 @Test 982 public void testMultiThreadGetAndEvictBlock() throws Exception { 983 long maxSize = 100000; 984 long blockSize = calculateBlockSize(maxSize, 10); 985 LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, false, 986 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 987 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.66f, // min 988 0.99f, // acceptable 989 0.33f, // single 990 0.33f, // multi 991 0.34f, // memory 992 1.2f, // limit 993 false, 1024, 10, 500, 0.01f); 994 testMultiThreadGetAndEvictBlockInternal(cache); 995 } 996 997 public void testSkipCacheDataBlocksInteral(int heavyEvictionCountLimit) throws Exception { 998 long maxSize = 100000000; 999 int numBlocks = 100000; 1000 final long blockSize = calculateBlockSizeDefault(maxSize, numBlocks); 1001 assertTrue(blockSize * numBlocks <= maxSize, "calculateBlockSize appears broken."); 1002 1003 final LruAdaptiveBlockCache cache = new LruAdaptiveBlockCache(maxSize, blockSize, true, 1004 (int) Math.ceil(1.2 * maxSize / blockSize), LruAdaptiveBlockCache.DEFAULT_LOAD_FACTOR, 1005 LruAdaptiveBlockCache.DEFAULT_CONCURRENCY_LEVEL, 0.5f, // min 1006 0.99f, // acceptable 1007 0.33f, // single 1008 0.33f, // multi 1009 0.34f, // memory 1010 1.2f, // limit 1011 false, maxSize, heavyEvictionCountLimit, 200, 0.01f); 1012 1013 EvictionThread evictionThread = cache.getEvictionThread(); 1014 assertNotNull(evictionThread); 1015 Waiter.waitFor(CONF, 10000, 10, () -> evictionThread.isEnteringRun()); 1016 1017 final String hfileName = "hfile"; 1018 for (int blockIndex = 0; blockIndex <= numBlocks * 3000; ++blockIndex) { 1019 CachedItem block = new CachedItem(hfileName, (int) blockSize, blockIndex); 1020 cache.cacheBlock(block.cacheKey, block, false); 1021 if (cache.getCacheDataBlockPercent() < 70) { 1022 // enough for test 1023 break; 1024 } 1025 } 1026 1027 evictionThread.evict(); 1028 Thread.sleep(100); 1029 1030 if (heavyEvictionCountLimit == 0) { 1031 // Check if all offset (last two digits) of cached blocks less than the percent. 1032 // It means some of blocks haven't put into BlockCache 1033 assertTrue(cache.getCacheDataBlockPercent() < 90); 1034 for (BlockCacheKey key : cache.getMapForTests().keySet()) { 1035 assertTrue(!(key.getOffset() % 100 > 90)); 1036 } 1037 } else { 1038 // Check that auto-scaling is not working (all blocks in BlockCache) 1039 assertTrue(cache.getCacheDataBlockPercent() == 100); 1040 int counter = 0; 1041 for (BlockCacheKey key : cache.getMapForTests().keySet()) { 1042 if (key.getOffset() % 100 > 90) { 1043 counter++; 1044 } 1045 } 1046 assertTrue(counter > 1000); 1047 } 1048 evictionThread.shutdown(); 1049 } 1050 1051 @Test 1052 public void testSkipCacheDataBlocks() throws Exception { 1053 // Check that auto-scaling will work right after start 1054 testSkipCacheDataBlocksInteral(0); 1055 // Check that auto-scaling will not work right after start 1056 // (have to finished before auto-scaling) 1057 testSkipCacheDataBlocksInteral(100); 1058 } 1059}