This is an automated email from the ASF dual-hosted git repository.

Apache9 pushed a commit to branch branch-3
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/branch-3 by this push:
     new 7f4b9023fe6 HBASE-30240 TestPrefetchPersistence fails because of a 
file is not fully cached (#8386)
7f4b9023fe6 is described below

commit 7f4b9023fe6ec549049fea0873bbad116ddfb908
Author: Duo Zhang <[email protected]>
AuthorDate: Tue Jun 23 09:32:12 2026 +0800

    HBASE-30240 TestPrefetchPersistence fails because of a file is not fully 
cached (#8386)
    
    Signed-off-by: Wellington Ramos Chevreuil <[email protected]>
    (cherry picked from commit e2218e0b589bbd9f0a82a8ceeae59fc16f746a6f)
---
 .../hadoop/hbase/io/hfile/bucket/BucketCache.java  | 92 ++++++++++++----------
 .../io/hfile/bucket/TestPrefetchPersistence.java   | 37 +++++----
 2 files changed, 70 insertions(+), 59 deletions(-)

diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java
index bf05285bea3..19cf2af56ee 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/bucket/BucketCache.java
@@ -2409,6 +2409,34 @@ public class BucketCache implements BlockCache, HeapSize 
{
     return Optional.empty();
   }
 
+  private int countBlocksForFile(Path fileName, List<ReentrantReadWriteLock> 
locks) {
+    LOG.debug("iterating over {} entries in the backing map", 
backingMap.size());
+    Set<BlockCacheKey> result = getAllCacheKeysForFile(fileName.getName(), 0, 
Long.MAX_VALUE);
+    if (result.isEmpty() && StoreFileInfo.isReference(fileName)) {
+      result = getAllCacheKeysForFile(
+        
StoreFileInfo.getReferredToRegionAndFile(fileName.getName()).getSecond(), 0,
+        Long.MAX_VALUE);
+    }
+    int count = 0;
+    for (BlockCacheKey entry : result) {
+      LOG.debug("found block for file {} in the backing map. Acquiring read 
lock for offset {}",
+        fileName.getName(), entry.getOffset());
+      ReentrantReadWriteLock lock = offsetLock.getLock(entry.getOffset());
+      lock.readLock().lock();
+      locks.add(lock);
+      if (backingMap.containsKey(entry) && entry.getBlockType().isData()) {
+        count++;
+      }
+    }
+    return count;
+  }
+
+  private void releaseAllLocks(List<ReentrantReadWriteLock> locks) {
+    for (ReentrantReadWriteLock lock : locks) {
+      lock.readLock().unlock();
+    }
+  }
+
   @Override
   public void notifyFileCachingCompleted(Path fileName, int totalBlockCount, 
int dataBlockCount,
     long size) {
@@ -2419,58 +2447,42 @@ public class BucketCache implements BlockCache, 
HeapSize {
     LOG.debug("Notifying caching completed for file {}, with total blocks {}, 
and data blocks {}",
       fileName, totalBlockCount, dataBlockCount);
     try {
-      int count = 0;
-      LOG.debug("iterating over {} entries in the backing map", 
backingMap.size());
-      Set<BlockCacheKey> result = getAllCacheKeysForFile(fileName.getName(), 
0, Long.MAX_VALUE);
-      if (result.isEmpty() && StoreFileInfo.isReference(fileName)) {
-        result = getAllCacheKeysForFile(
-          
StoreFileInfo.getReferredToRegionAndFile(fileName.getName()).getSecond(), 0,
-          Long.MAX_VALUE);
-      }
-      for (BlockCacheKey entry : result) {
-        LOG.debug("found block for file {} in the backing map. Acquiring read 
lock for offset {}",
-          fileName.getName(), entry.getOffset());
-        ReentrantReadWriteLock lock = offsetLock.getLock(entry.getOffset());
-        lock.readLock().lock();
-        locks.add(lock);
-        if (backingMap.containsKey(entry) && entry.getBlockType().isData()) {
-          count++;
+      boolean lastTry = false;
+      for (;;) {
+        int count = countBlocksForFile(fileName, locks);
+        // BucketCache would only have data blocks
+        if (dataBlockCount == count) {
+          LOG.debug("File {} has now been fully cached.", fileName);
+          fileCacheCompleted(fileName, size);
+          break;
         }
-      }
-      // BucketCache would only have data blocks
-      if (dataBlockCount == count) {
-        LOG.debug("File {} has now been fully cached.", fileName);
-        fileCacheCompleted(fileName, size);
-      } else {
-        LOG.debug(
-          "Prefetch executor completed for {}, but only {} data blocks were 
cached. "
-            + "Total data blocks for file: {}. "
-            + "Checking for blocks pending cache in cache writer queue.",
-          fileName, count, dataBlockCount);
-        if (ramCache.hasBlocksForFile(fileName.getName())) {
-          for (ReentrantReadWriteLock lock : locks) {
-            lock.readLock().unlock();
-          }
-          locks.clear();
-          LOG.debug("There are still blocks pending caching for file {}. Will 
sleep 100ms "
-            + "and try the verification again.", fileName);
-          Thread.sleep(100);
-          notifyFileCachingCompleted(fileName, totalBlockCount, 
dataBlockCount, size);
-        } else {
+        if (lastTry) {
           LOG.info(
             "The total block count was {}. We found only {} data blocks cached 
from "
               + "a total of {} data blocks for file {}, "
               + "but no blocks pending caching. Maybe cache is full or 
evictions "
               + "happened concurrently to cache prefetch.",
             totalBlockCount, count, dataBlockCount, fileName);
+          break;
+        }
+        if (ramCache.hasBlocksForFile(fileName.getName())) {
+          releaseAllLocks(locks);
+          locks.clear();
+          LOG.debug("There are still blocks pending caching for file {}. Will 
sleep 100ms "
+            + "and try the verification again.", fileName);
+          Thread.sleep(100);
+        } else {
+          // there are no pending blocks, so count for the last time, if we 
still can not get enough
+          // data blocks, quit
+          LOG.debug("There are no blocks pending cache for file {}. Will try 
the verification "
+            + "for the last time.");
+          lastTry = true;
         }
       }
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
     } finally {
-      for (ReentrantReadWriteLock lock : locks) {
-        lock.readLock().unlock();
-      }
+      releaseAllLocks(locks);
     }
   }
 
diff --git 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchPersistence.java
 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchPersistence.java
index 3c94497b43c..e4c2d7adf8a 100644
--- 
a/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchPersistence.java
+++ 
b/hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/bucket/TestPrefetchPersistence.java
@@ -17,6 +17,10 @@
  */
 package org.apache.hadoop.hbase.io.hfile.bucket;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.hasKey;
+import static org.hamcrest.io.FileMatchers.anExistingFile;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -42,21 +46,24 @@ import org.apache.hadoop.hbase.io.hfile.PrefetchExecutor;
 import org.apache.hadoop.hbase.io.hfile.RandomKeyValueUtil;
 import org.apache.hadoop.hbase.regionserver.StoreFileWriter;
 import org.apache.hadoop.hbase.testclassification.IOTests;
-import org.apache.hadoop.hbase.testclassification.LargeTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Tag;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.params.provider.Arguments;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 @Tag(IOTests.TAG)
-@Tag(LargeTests.TAG)
+@Tag(SmallTests.TAG)
 @HBaseParameterizedTestTemplate(name = "{index}: blockSize={0}, 
bucketSizes={1}")
 public class TestPrefetchPersistence {
 
-  @SuppressWarnings("checkstyle:Indentation")
+  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
+
+  private static final int NUM_VALID_KEY_TYPES = KeyValue.Type.values().length 
- 2;
+  private static final int DATA_BLOCK_SIZE = 2048;
+  private static final int NUM_KV = 1000;
+
   public static Stream<Arguments> parameters() {
     return Stream.of(Arguments.of(16 * 1024,
       new int[] { 2 * 1024 + 1024, 4 * 1024 + 1024, 8 * 1024 + 1024, 16 * 1024 
+ 1024,
@@ -64,22 +71,14 @@ public class TestPrefetchPersistence {
         128 * 1024 + 1024 }));
   }
 
-  final int constructedBlockSize;
-  final int[] constructedBlockSizes;
+  private final int constructedBlockSize;
+  private final int[] constructedBlockSizes;
 
   public TestPrefetchPersistence(int constructedBlockSize, int[] 
constructedBlockSizes) {
     this.constructedBlockSize = constructedBlockSize;
     this.constructedBlockSizes = constructedBlockSizes;
   }
 
-  private static final Logger LOG = 
LoggerFactory.getLogger(TestPrefetchPersistence.class);
-
-  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
-
-  private static final int NUM_VALID_KEY_TYPES = KeyValue.Type.values().length 
- 2;
-  private static final int DATA_BLOCK_SIZE = 2048;
-  private static final int NUM_KV = 1000;
-
   private Configuration conf;
   private CacheConfig cacheConf;
   private FileSystem fs;
@@ -123,15 +122,15 @@ public class TestPrefetchPersistence {
     assertNotEquals(0, usedSize);
 
     bucketCache.shutdown();
-    assertTrue(new File(testDir + "/bucket.persistence").exists());
+    assertThat(new File(testDir + "/bucket.persistence"), anExistingFile());
     bucketCache = new BucketCache("file:" + testDir + "/bucket.cache", 
capacitySize,
       constructedBlockSize, constructedBlockSizes, writeThreads, writerQLen,
       testDir + "/bucket.persistence", 60 * 1000, conf);
     bucketCache.waitForCacheInitialization(10000);
     cacheConf = new CacheConfig(conf, bucketCache);
-    assertTrue(usedSize != 0);
-    assertTrue(bucketCache.fullyCachedFiles.containsKey(storeFile.getName()));
-    assertTrue(bucketCache.fullyCachedFiles.containsKey(storeFile2.getName()));
+    assertNotEquals(usedSize, 0);
+    assertThat(bucketCache.fullyCachedFiles,
+      both(hasKey(storeFile.getName())).and(hasKey(storeFile2.getName())));
   }
 
   @AfterEach

Reply via email to