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

freemandealer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 138ab5cb1f4 [fix](filecache) fix clear_file_cache right after reboot 
causing file cache size percent overflow (#63410)
138ab5cb1f4 is described below

commit 138ab5cb1f4b07601c745fe701e7ca72c389dd35
Author: zhengyu <[email protected]>
AuthorDate: Mon Jun 1 16:43:38 2026 +0800

    [fix](filecache) fix clear_file_cache right after reboot causing file cache 
size percent overflow (#63410)
    
    Problem Summary: When file cache LRU restore creates a block from dump
    metadata and later lazy loading finds the same hash/offset with a
    smaller real file size, reset_range only updated the LRU queue size and
    _cur_cache_size. The FileBlock range still kept the old restored size,
    so a later async clear or eviction subtracted the old block size and
    could underflow _cur_cache_size, producing huge size_percent values in
    need-evict-cache-in-advance logs. This change makes reset_range update
    the FileBlock range as the single place that keeps the FileBlock, LRU
    queue, _cur_cache_size, and TTL size accounting consistent.
    FileBlock::finalize now delegates the range shrink to reset_range
    instead of changing the range before calling it.
---
 be/src/io/cache/block_file_cache.cpp       |  6 +++
 be/src/io/cache/block_file_cache.h         |  2 +-
 be/src/io/cache/file_block.cpp             |  3 +-
 be/test/io/cache/block_file_cache_test.cpp | 71 ++++++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 3 deletions(-)

diff --git a/be/src/io/cache/block_file_cache.cpp 
b/be/src/io/cache/block_file_cache.cpp
index f8191a0e19a..9f1fc29f1f1 100644
--- a/be/src/io/cache/block_file_cache.cpp
+++ b/be/src/io/cache/block_file_cache.cpp
@@ -1260,6 +1260,7 @@ void BlockFileCache::reset_range(const UInt128Wrapper& 
hash, size_t offset, size
                      << " new_size=" << new_size;
         return;
     }
+    DCHECK_EQ(cell->file_block->_block_range.size(), old_size);
     if (cell->queue_iterator) {
         auto& queue = get_queue(cell->file_block->cache_type());
         DCHECK(queue.contains(hash, offset, cache_lock));
@@ -1268,8 +1269,13 @@ void BlockFileCache::reset_range(const UInt128Wrapper& 
hash, size_t offset, size
                                           cell->file_block->get_hash_value(),
                                           cell->file_block->offset(), 
new_size);
     }
+    cell->file_block->_block_range.right = cell->file_block->_block_range.left 
+ new_size - 1;
     _cur_cache_size -= old_size;
     _cur_cache_size += new_size;
+    if (cell->file_block->cache_type() == FileCacheType::TTL) {
+        _cur_ttl_size -= old_size;
+        _cur_ttl_size += new_size;
+    }
 }
 
 bool BlockFileCache::try_reserve_from_other_queue_by_time_interval(
diff --git a/be/src/io/cache/block_file_cache.h 
b/be/src/io/cache/block_file_cache.h
index 9ad45b0a878..5bdbd09f914 100644
--- a/be/src/io/cache/block_file_cache.h
+++ b/be/src/io/cache/block_file_cache.h
@@ -275,7 +275,7 @@ public:
     void remove_if_cached(const UInt128Wrapper& key);
     void remove_if_cached_async(const UInt128Wrapper& key);
 
-    // Shrink the block size. old_size is always larged than new_size.
+    // Reset the block size and keep FileBlock, LRU queue, and cache counters 
consistent.
     void reset_range(const UInt128Wrapper&, size_t offset, size_t old_size, 
size_t new_size,
                      std::lock_guard<std::mutex>& cache_lock);
 
diff --git a/be/src/io/cache/file_block.cpp b/be/src/io/cache/file_block.cpp
index 501c53a6160..8037a5468fa 100644
--- a/be/src/io/cache/file_block.cpp
+++ b/be/src/io/cache/file_block.cpp
@@ -163,8 +163,7 @@ Status FileBlock::finalize() {
     if (_downloaded_size != _block_range.size()) {
         SCOPED_CACHE_LOCK(_mgr->_mutex, _mgr);
         size_t old_size = _block_range.size();
-        _block_range.right = _block_range.left + _downloaded_size - 1;
-        size_t new_size = _block_range.size();
+        size_t new_size = _downloaded_size;
         DCHECK(new_size < old_size);
         _mgr->reset_range(_key.hash, _block_range.left, old_size, new_size, 
cache_lock);
     }
diff --git a/be/test/io/cache/block_file_cache_test.cpp 
b/be/test/io/cache/block_file_cache_test.cpp
index da078c3bff4..cf4087a2624 100644
--- a/be/test/io/cache/block_file_cache_test.cpp
+++ b/be/test/io/cache/block_file_cache_test.cpp
@@ -8378,4 +8378,75 @@ TEST_F(BlockFileCacheTest, 
add_cell_rejects_oversized_size) {
     }
 }
 
+TEST_F(BlockFileCacheTest, 
lru_restore_size_mismatch_does_not_underflow_on_clear) {
+    std::string my_cache_path =
+            caches_dir / 
"lru_restore_size_mismatch_does_not_underflow_on_clear" / "";
+    if (fs::exists(my_cache_path)) {
+        fs::remove_all(my_cache_path);
+    }
+    fs::create_directories(my_cache_path);
+
+    io::FileCacheSettings settings;
+    settings.query_queue_size = 1_mb;
+    settings.query_queue_elements = 30;
+    settings.capacity = 1_mb;
+    settings.max_file_block_size = 1_mb;
+    settings.max_query_cache_size = 1_mb;
+
+    io::BlockFileCache cache(my_cache_path, settings);
+    ASSERT_TRUE(cache.initialize());
+    for (int i = 0; i < 100; ++i) {
+        if (cache.get_async_open_success()) {
+            break;
+        }
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+    }
+    ASSERT_TRUE(cache.get_async_open_success());
+
+    auto hash = 
io::BlockFileCache::hash("lru_restore_size_mismatch_does_not_underflow_on_clear");
+    io::CacheContext ctx;
+    TUniqueId qid;
+    qid.hi = 1;
+    qid.lo = 1;
+    ctx.query_id = qid;
+    ctx.cache_type = io::FileCacheType::NORMAL;
+    ReadStatistics rstats;
+    ctx.stats = &rstats;
+
+    constexpr size_t old_size = 128_kb;
+    constexpr size_t new_size = 64_kb;
+    constexpr size_t offset = 0;
+
+    {
+        std::lock_guard<std::mutex> cache_lock(cache._mutex);
+        auto* cell = cache.add_cell(hash, ctx, offset, old_size, 
io::FileBlock::State::DOWNLOADED,
+                                    cache_lock);
+        ASSERT_NE(cell, nullptr);
+        ASSERT_EQ(cell->file_block->range().size(), old_size);
+        ASSERT_EQ(cache._cur_cache_size, old_size);
+
+        auto* storage = 
dynamic_cast<io::FSFileCacheStorage*>(cache._storage.get());
+        ASSERT_NE(storage, nullptr);
+        ASSERT_TRUE(storage->handle_already_loaded_block(&cache, hash, offset, 
new_size,
+                                                         /*tablet_id*/ 0, 
cache_lock));
+        ASSERT_EQ(cell->file_block->range().size(), new_size);
+        ASSERT_EQ(cell->file_block->range().right, offset + new_size - 1);
+        ASSERT_EQ(cache._cur_cache_size, new_size);
+        
ASSERT_EQ(cache.get_queue(io::FileCacheType::NORMAL).get_capacity(cache_lock), 
new_size);
+    }
+
+    cache.clear_file_cache_async();
+
+    {
+        std::lock_guard<std::mutex> cache_lock(cache._mutex);
+        ASSERT_EQ(cache._cur_cache_size, 0);
+        
ASSERT_EQ(cache.get_queue(io::FileCacheType::NORMAL).get_capacity(cache_lock), 
0);
+        ASSERT_TRUE(cache._files.empty());
+    }
+
+    if (fs::exists(my_cache_path)) {
+        fs::remove_all(my_cache_path);
+    }
+}
+
 } // namespace doris::io


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to