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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 4e0b722850f branch-4.1: [fix](filecache) skip redundant ttl scans for 
non-TTL tablets (pick #65434) (#65429)
4e0b722850f is described below

commit 4e0b722850fcd1a6fa20eacebfbc75a07a6181f3
Author: zhengyu <[email protected]>
AuthorDate: Sat Jul 11 22:11:11 2026 +0800

    branch-4.1: [fix](filecache) skip redundant ttl scans for non-TTL tablets 
(pick #65434) (#65429)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: None
    
    Problem Summary:
    
    The file cache TTL manager keeps registered tablet ids in
    `_tablet_id_set`. For ordinary non-TTL tablets, the old `ttl_seconds <=
    0` path always set `need_convert_from_ttl = true`, so every update round
    scanned cached blocks even when the tablet had never been recorded in
    `_ttl_info_map`.
    
    This PR reduces the repeated CPU work for registered non-TTL tablets:
    
    - If a tablet was previously tracked as TTL, it is still converted back
    to NORMAL immediately when TTL is disabled.
    - If a tablet has no prior TTL info, the manager skips the per-round
    cached block scan.
    - A low-frequency reconciliation scan is kept every 20 update rounds, so
    cleanup behavior is preserved while avoiding scans on every round.
    
    With the default `file_cache_background_ttl_info_update_interval_ms =
    180000`, ordinary non-TTL tablets avoid the repeated per-round block
    scan that previously ran every 3 minutes.
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [x] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    Unit test:
    
    ```bash
    DORIS_TOOLCHAIN=clang DISABLE_BE_JAVA_EXTENSIONS=ON 
ENABLE_INJECTION_POINT=ON ENABLE_CACHE_LOCK_DEBUG=0 ENABLE_PCH=0 sh 
run-be-ut.sh --run --filter='BlockFileCacheTtlMgrTest.*'
    ```
    
    Result: 5 tests passed.
    
    - Behavior changed:
        - [ ] No.
    - [x] Yes. Registered ordinary non-TTL tablets no longer scan cached
    blocks in every TTL manager update round.
    
    - Does this need documentation?
        - [x] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 be/src/io/cache/block_file_cache_ttl_mgr.cpp       |  13 ++-
 be/test/io/cache/block_file_cache_ttl_mgr_test.cpp | 126 ++++++++++++++++++++-
 2 files changed, 131 insertions(+), 8 deletions(-)

diff --git a/be/src/io/cache/block_file_cache_ttl_mgr.cpp 
b/be/src/io/cache/block_file_cache_ttl_mgr.cpp
index 939045aa71e..b394591d42d 100644
--- a/be/src/io/cache/block_file_cache_ttl_mgr.cpp
+++ b/be/src/io/cache/block_file_cache_ttl_mgr.cpp
@@ -27,6 +27,7 @@
 #include "common/config.h"
 #include "common/logging.h"
 #include "common/status.h"
+#include "cpp/sync_point.h"
 #include "io/cache/block_file_cache.h"
 #include "io/cache/cache_block_meta_store.h"
 #include "io/cache/file_block.h"
@@ -134,6 +135,7 @@ void BlockFileCacheTtlMgr::run_background_tablet_id_flush() 
{
 
 FileBlocks BlockFileCacheTtlMgr::get_file_blocks_from_tablet_id(int64_t 
tablet_id) {
     FileBlocks result;
+    
TEST_SYNC_POINT_CALLBACK("BlockFileCacheTtlMgr::get_file_blocks_from_tablet_id",
 tablet_id);
 
     // Use meta store to get all blocks for this tablet
     auto iterator = _meta_store->range_get(tablet_id);
@@ -169,8 +171,12 @@ FileBlocks 
BlockFileCacheTtlMgr::get_file_blocks_from_tablet_id(int64_t tablet_i
 void BlockFileCacheTtlMgr::run_backgroud_update_ttl_info_map() {
     Thread::set_self_name("ttl_mgr_update");
 
+    static constexpr uint64_t kFullReconcileIntervalRounds = 20;
+    uint64_t update_round = 0;
+
     while (!_stop_background.load(std::memory_order_acquire)) {
         try {
+            const bool need_full_reconcile = (++update_round % 
kFullReconcileIntervalRounds) == 0;
             std::unordered_set<int64_t> tablet_ids_to_process;
             {
                 std::lock_guard<std::mutex> lock(_tablet_id_mutex);
@@ -238,9 +244,10 @@ void 
BlockFileCacheTtlMgr::run_backgroud_update_ttl_info_map() {
                             }
                         }
                     } else {
-                        // Remove from TTL map if TTL is 0
-                        _ttl_info_map.erase(tablet_id);
-                        need_convert_from_ttl = true;
+                        // Periodically reconcile blocks restored from 
persisted TTL metadata,
+                        // because _ttl_info_map is rebuilt only in memory 
after restart.
+                        need_convert_from_ttl =
+                                _ttl_info_map.erase(tablet_id) > 0 || 
need_full_reconcile;
                     }
                 }
 
diff --git a/be/test/io/cache/block_file_cache_ttl_mgr_test.cpp 
b/be/test/io/cache/block_file_cache_ttl_mgr_test.cpp
index 48165163581..56f6d3d43d6 100644
--- a/be/test/io/cache/block_file_cache_ttl_mgr_test.cpp
+++ b/be/test/io/cache/block_file_cache_ttl_mgr_test.cpp
@@ -20,6 +20,7 @@
 #include <gtest/gtest.h>
 
 #include <algorithm>
+#include <atomic>
 #include <chrono>
 #include <filesystem>
 #include <memory>
@@ -29,6 +30,7 @@
 
 #include "common/config.h"
 #include "common/status.h"
+#include "cpp/sync_point.h"
 #include "io/cache/block_file_cache.h"
 #include "io/cache/cache_block_meta_store.h"
 #include "io/cache/file_block.h"
@@ -132,6 +134,7 @@ public:
 
     Status get_tablet_meta(int64_t tablet_id, TabletMetaSharedPtr* tablet_meta,
                            bool /*force_use_only_cached*/ = false) override {
+        _get_tablet_meta_call_count.fetch_add(1, std::memory_order_relaxed);
         auto tablet_res = get_tablet(tablet_id);
         if (!tablet_res.has_value()) {
             return tablet_res.error();
@@ -149,9 +152,14 @@ public:
         _tablets[tablet_id] = tablet;
     }
 
+    int64_t get_tablet_meta_call_count() const {
+        return _get_tablet_meta_call_count.load(std::memory_order_relaxed);
+    }
+
 private:
     std::mutex _mutex;
     std::unordered_map<int64_t, BaseTabletSPtr> _tablets;
+    std::atomic<int64_t> _get_tablet_meta_call_count {0};
 };
 
 std::vector<FileBlockSPtr> blocks_from_holder(const FileBlocksHolder& holder) {
@@ -237,7 +245,9 @@ protected:
     }
 
     FileBlockSPtr create_block(int64_t tablet_id, const std::string& 
cache_key, size_t offset,
-                               size_t size, UInt128Wrapper* out_hash) {
+                               size_t size, UInt128Wrapper* out_hash,
+                               FileCacheType cache_type = 
FileCacheType::NORMAL,
+                               uint64_t expiration_time = 0) {
         auto hash = BlockFileCache::hash(cache_key);
         if (out_hash != nullptr) {
             *out_hash = hash;
@@ -246,7 +256,8 @@ protected:
         CacheContext context;
         ReadStatistics stats;
         context.stats = &stats;
-        context.cache_type = FileCacheType::NORMAL;
+        context.cache_type = cache_type;
+        context.expiration_time = expiration_time;
         context.tablet_id = tablet_id;
 
         auto holder = _cache->get_or_set(hash, offset, size, context);
@@ -269,7 +280,7 @@ protected:
         }
         auto block = *it;
         EXPECT_TRUE(block);
-        EXPECT_EQ(FileCacheType::NORMAL, block->cache_type());
+        EXPECT_EQ(cache_type, block->cache_type());
         EXPECT_EQ(FileBlock::get_caller_id(), block->get_or_set_downloader());
         // Only append up to the selected file block's size. The requested
         // range may be split into multiple file blocks, so appending the
@@ -282,9 +293,10 @@ protected:
     }
 
     void persist_block_meta(int64_t tablet_id, const UInt128Wrapper& hash, 
size_t offset,
-                            size_t size) {
+                            size_t size, FileCacheType cache_type = 
FileCacheType::NORMAL,
+                            uint64_t expiration_time = 0) {
         BlockMetaKey key {tablet_id, hash, offset};
-        BlockMeta meta {FileCacheType::NORMAL, size, 0};
+        BlockMeta meta {cache_type, size, expiration_time};
         _meta_store->put(key, meta);
         ASSERT_TRUE(wait_for_condition([this, &key]() { return 
_meta_store->get(key).has_value(); },
                                        std::chrono::seconds(2)));
@@ -345,4 +357,108 @@ TEST_F(BlockFileCacheTtlMgrTest, 
ExpiredTabletMovesBlocksBackToNormal) {
                                    std::chrono::seconds(5)));
 }
 
+TEST_F(BlockFileCacheTtlMgrTest, 
NonTtlTabletWithoutPriorTtlInfoSkipsBlockScan) {
+    config::file_cache_background_ttl_info_update_interval_ms = 100;
+
+    constexpr int64_t kTabletId = 3003;
+    auto tablet = std::make_shared<FakeTablet>(UnixSeconds(), 0);
+    fake_engine()->add_tablet(kTabletId, tablet);
+
+    UInt128Wrapper hash;
+    auto block = create_block(kTabletId, "non-ttl-tablet", 0, 1024, &hash);
+    persist_block_meta(kTabletId, hash, block->range().left, 
block->range().size());
+
+    std::atomic<int64_t> block_scan_count {0};
+    auto* sync_point = SyncPoint::get_instance();
+    sync_point->clear_all_call_backs();
+    sync_point->clear_trace();
+    SyncPoint::CallbackGuard guard;
+    sync_point->set_call_back(
+            "BlockFileCacheTtlMgr::get_file_blocks_from_tablet_id",
+            [&block_scan_count](std::vector<std::any>&& args) {
+                if (doris::try_any_cast<int64_t>(args[0]) == kTabletId) {
+                    block_scan_count.fetch_add(1, std::memory_order_relaxed);
+                }
+            },
+            &guard);
+    sync_point->enable_processing();
+
+    _ttl_mgr = std::make_unique<BlockFileCacheTtlMgr>(_cache.get(), 
_meta_store.get());
+    _ttl_mgr->register_tablet_id(kTabletId);
+
+    bool update_thread_observed = wait_for_condition(
+            [this]() { return fake_engine()->get_tablet_meta_call_count() >= 
2; },
+            std::chrono::seconds(5));
+    std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    sync_point->disable_processing();
+    sync_point->clear_trace();
+
+    EXPECT_TRUE(update_thread_observed);
+    EXPECT_EQ(0, block_scan_count.load(std::memory_order_relaxed));
+    EXPECT_EQ(FileCacheType::NORMAL, block->cache_type());
+}
+
+TEST_F(BlockFileCacheTtlMgrTest, 
PeriodicReconcileDemotesTtlBlockWithoutPriorTtlInfo) {
+    constexpr int64_t kTabletId = 4004;
+    auto tablet = std::make_shared<FakeTablet>(UnixSeconds(), 0);
+    fake_engine()->add_tablet(kTabletId, tablet);
+
+    const uint64_t expiration_time = UnixSeconds() + 3600;
+    UInt128Wrapper hash;
+    auto block = create_block(kTabletId, "ttl-block-without-info", 0, 1024, 
&hash,
+                              FileCacheType::TTL, expiration_time);
+    persist_block_meta(kTabletId, hash, block->range().left, 
block->range().size(),
+                       FileCacheType::TTL, expiration_time);
+    ASSERT_EQ(FileCacheType::TTL, block->cache_type());
+
+    std::atomic<int64_t> block_scan_count {0};
+    auto* sync_point = SyncPoint::get_instance();
+    sync_point->clear_all_call_backs();
+    sync_point->clear_trace();
+    SyncPoint::CallbackGuard guard;
+    sync_point->set_call_back(
+            "BlockFileCacheTtlMgr::get_file_blocks_from_tablet_id",
+            [&block_scan_count](std::vector<std::any>&& args) {
+                if (doris::try_any_cast<int64_t>(args[0]) == kTabletId) {
+                    block_scan_count.fetch_add(1, std::memory_order_relaxed);
+                }
+            },
+            &guard);
+    sync_point->enable_processing();
+
+    _ttl_mgr = std::make_unique<BlockFileCacheTtlMgr>(_cache.get(), 
_meta_store.get());
+    _ttl_mgr->register_tablet_id(kTabletId);
+
+    bool demoted =
+            wait_for_condition([&]() { return block->cache_type() == 
FileCacheType::NORMAL; },
+                               std::chrono::seconds(5));
+    sync_point->disable_processing();
+    sync_point->clear_trace();
+
+    EXPECT_TRUE(demoted);
+    EXPECT_GE(block_scan_count.load(std::memory_order_relaxed), 1);
+}
+
+TEST_F(BlockFileCacheTtlMgrTest, TabletTtlRemovedMovesBlocksBackToNormal) {
+    constexpr int64_t kTabletId = 5005;
+    auto tablet = std::make_shared<FakeTablet>(UnixSeconds(), 120);
+    fake_engine()->add_tablet(kTabletId, tablet);
+
+    UInt128Wrapper hash;
+    auto block = create_block(kTabletId, "ttl-remove", 0, 1024, &hash);
+    persist_block_meta(kTabletId, hash, block->range().left, 
block->range().size());
+
+    _ttl_mgr = std::make_unique<BlockFileCacheTtlMgr>(_cache.get(), 
_meta_store.get());
+    _ttl_mgr->register_tablet_id(kTabletId);
+
+    ASSERT_TRUE(wait_for_condition([&]() { return block->cache_type() == 
FileCacheType::TTL; },
+                                   std::chrono::seconds(5)));
+
+    tablet->set_ttl_seconds(0);
+    _ttl_mgr->register_tablet_id(kTabletId);
+
+    ASSERT_TRUE(wait_for_condition([&]() { return block->cache_type() == 
FileCacheType::NORMAL; },
+                                   std::chrono::seconds(5)));
+}
+
 } // namespace doris::io


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

Reply via email to