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