@Test
  public void testBucketCacheRecovery() throws Exception {
    HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
    Path testDir = TEST_UTIL.getDataTestDir();
    TEST_UTIL.getTestFileSystem().mkdirs(testDir);
    Configuration conf = HBaseConfiguration.create();
    // Disables the persister thread by setting its interval to MAX_VALUE
    conf.setLong(BUCKETCACHE_PERSIST_INTERVAL_KEY, Long.MAX_VALUE);
    String mapFileName = testDir + "/bucket.persistence" + EnvironmentEdgeManager.currentTime();
    BucketCache bucketCache = null;
    BucketCache newBucketCache = null;
    try {
      bucketCache = new BucketCache("file:" + testDir + "/bucket.cache", capacitySize,
        constructedBlockSize, constructedBlockSizes, writeThreads, writerQLen, mapFileName,
        DEFAULT_ERROR_TOLERATION_DURATION, conf);
      assertTrue(bucketCache.waitForCacheInitialization(10000));

      CacheTestUtils.HFileBlockPair[] blocks =
        CacheTestUtils.generateHFileBlocks(constructedBlockSize, 4);
      // Add three blocks
      cacheAndWaitUntilFlushedToBucket(bucketCache, blocks[0].getBlockName(), blocks[0].getBlock());
      cacheAndWaitUntilFlushedToBucket(bucketCache, blocks[1].getBlockName(), blocks[1].getBlock());
      cacheAndWaitUntilFlushedToBucket(bucketCache, blocks[2].getBlockName(), blocks[2].getBlock());
      // saves the current state
      bucketCache.persistToFile();
      // evicts first block
      bucketCache.evictBlock(blocks[0].getBlockName());

      // now adds a fourth block to bucket cache
      cacheAndWaitUntilFlushedToBucket(bucketCache, blocks[3].getBlockName(), blocks[3].getBlock());
      // Creates new bucket cache instance without persisting to file after evicting first block
      // and caching fourth block. So the bucket cache file has only the last three blocks,
      // but backing map (containing cache keys) was persisted when first three blocks
      // were in the cache. So the state on this recovery is:
      // - Backing map: [block0, block1, block2]
      // - Cache: [block1, block2, block3]
      // Therefore, this bucket cache would be able to recover only block1 and block2.
      newBucketCache = new BucketCache("file:" + testDir + "/bucket.cache", capacitySize,
        constructedBlockSize, constructedBlockSizes, writeThreads, writerQLen, mapFileName,
        DEFAULT_ERROR_TOLERATION_DURATION, conf);
      assertTrue(newBucketCache.waitForCacheInitialization(10000));
      waitPersistentCacheValidation(conf, newBucketCache);
      assertNull(newBucketCache.getBlock(blocks[0].getBlockName(), false, false, false));
      assertEquals(blocks[1].getBlock(),
        newBucketCache.getBlock(blocks[1].getBlockName(), false, false, false));
      assertEquals(blocks[2].getBlock(),
        newBucketCache.getBlock(blocks[2].getBlockName(), false, false, false));
      assertNull(newBucketCache.getBlock(blocks[3].getBlockName(), false, false, false));
      assertEquals(2, newBucketCache.backingMap.size());
    } finally {
      if (bucketCache != null) {
        bucketCache.shutdown();
      }
      if (newBucketCache != null) {
        newBucketCache.shutdown();
      }
    }
    TEST_UTIL.cleanupTestDir();
  }
When using persistent bucket cache, there may be crashes between persisting the backing map and
syncing new blocks to the cache file itself, leading to an inconsistent state between the cache
keys and the cached data. This is to make sure the cache keys are updated accordingly, and the
keys that are still valid do succeed in retrieve related block data from the cache without any
corruption.
@throws Exception the exception
hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestVerifyBucketCacheFile.java