github-actions[bot] commented on code in PR #65722:
URL: https://github.com/apache/doris/pull/65722#discussion_r3600423297


##########
be/src/io/cache/block_file_cache.cpp:
##########
@@ -2023,31 +2059,70 @@ std::string BlockFileCache::reset_capacity(size_t 
new_capacity) {
             ss << " index_queue released " << queue_released;
             queue_released = remove_blocks(_ttl_queue);
             ss << " ttl_queue released " << queue_released;
-
-            _disk_resource_limit_mode = true;
-            _disk_limit_mode_metrics->set_value(1);
             ss << " total_space_released=" << space_released;
         }
         old_capacity = _capacity;
         _capacity = new_capacity;
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
         _cache_capacity_metrics->set_value(_capacity);
+        _cur_cache_size_metrics->set_value(_cur_cache_size);
+        _disk_limit_mode_metrics->set_value(
+                _disk_resource_limit_mode.load(std::memory_order_relaxed));
+        _need_evict_cache_in_advance_metrics->set_value(
+                _need_evict_cache_in_advance.load(std::memory_order_relaxed));
     }
-    auto use_time = duration_cast<milliseconds>(steady_clock::time_point() - 
adjust_start_time);
+    auto use_time = duration_cast<milliseconds>(steady_clock::now() - 
adjust_start_time);
     LOG(INFO) << "Finish tag deleted block. path=" << _cache_base_path
+              << " storage_type=" << 
file_cache_storage_type_to_string(_storage->get_type())
+              << " size_percent=" << size_percentage
               << " use_time=" << cast_set<int64_t>(use_time.count());
     ss << " old_capacity=" << old_capacity << " new_capacity=" << new_capacity;
     LOG(INFO) << ss.str();
     return ss.str();
 }
 
 void BlockFileCache::check_disk_resource_limit() {
-    if (_storage->get_type() != FileCacheStorageType::DISK) {
+    // ATTN: due to that can be changed dynamically, set it to default value 
if it's invalid
+    // FIXME: reject with config validator
+    if (config::file_cache_enter_disk_resource_limit_mode_percent <=
+        config::file_cache_exit_disk_resource_limit_mode_percent) {
+        LOG_WARNING("config error, set to default value")
+                .tag("enter", 
config::file_cache_enter_disk_resource_limit_mode_percent)
+                .tag("exit", 
config::file_cache_exit_disk_resource_limit_mode_percent);
+        config::file_cache_enter_disk_resource_limit_mode_percent = 88;
+        config::file_cache_exit_disk_resource_limit_mode_percent = 80;
+    }
+    size_t size_percentage = 0;
+    {
+        SCOPED_CACHE_LOCK(_mutex, this);
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
+    }
+    auto is_insufficient = [](const int& percentage) {
+        return percentage >= 
config::file_cache_enter_disk_resource_limit_mode_percent;
+    };
+    const bool previous_mode = 
_disk_resource_limit_mode.load(std::memory_order_relaxed);
+    bool current_mode = previous_mode;
+    if (is_memory_storage()) {
+        bool is_size_insufficient = 
is_insufficient(cast_set<int>(size_percentage));

Review Comment:
   [P1] Keep the computed pressure percentage wide
   
   A valid reset can leave `_cur_cache_size` far above the new capacity while 
readers still hold blocks. For example, three held 10 MiB blocks plus `RESET 
capacity=1` (accepted because it is positive) yields 3,145,728,000%; 
`cast_set<int>` throws above `INT_MAX`. This runs from the uncaught 
monitor-thread entry, so the next check terminates the BE. Compare the `size_t` 
percentage against a safely converted threshold (or clamp it) without 
narrowing, and add a held-block tiny-shrink case that runs both pressure checks.



##########
be/src/io/cache/block_file_cache.cpp:
##########
@@ -2023,31 +2059,70 @@ std::string BlockFileCache::reset_capacity(size_t 
new_capacity) {
             ss << " index_queue released " << queue_released;
             queue_released = remove_blocks(_ttl_queue);
             ss << " ttl_queue released " << queue_released;
-
-            _disk_resource_limit_mode = true;
-            _disk_limit_mode_metrics->set_value(1);
             ss << " total_space_released=" << space_released;
         }
         old_capacity = _capacity;
         _capacity = new_capacity;
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
         _cache_capacity_metrics->set_value(_capacity);
+        _cur_cache_size_metrics->set_value(_cur_cache_size);
+        _disk_limit_mode_metrics->set_value(
+                _disk_resource_limit_mode.load(std::memory_order_relaxed));
+        _need_evict_cache_in_advance_metrics->set_value(
+                _need_evict_cache_in_advance.load(std::memory_order_relaxed));
     }
-    auto use_time = duration_cast<milliseconds>(steady_clock::time_point() - 
adjust_start_time);
+    auto use_time = duration_cast<milliseconds>(steady_clock::now() - 
adjust_start_time);
     LOG(INFO) << "Finish tag deleted block. path=" << _cache_base_path
+              << " storage_type=" << 
file_cache_storage_type_to_string(_storage->get_type())
+              << " size_percent=" << size_percentage
               << " use_time=" << cast_set<int64_t>(use_time.count());
     ss << " old_capacity=" << old_capacity << " new_capacity=" << new_capacity;
     LOG(INFO) << ss.str();
     return ss.str();
 }
 
 void BlockFileCache::check_disk_resource_limit() {
-    if (_storage->get_type() != FileCacheStorageType::DISK) {
+    // ATTN: due to that can be changed dynamically, set it to default value 
if it's invalid
+    // FIXME: reject with config validator
+    if (config::file_cache_enter_disk_resource_limit_mode_percent <=
+        config::file_cache_exit_disk_resource_limit_mode_percent) {
+        LOG_WARNING("config error, set to default value")
+                .tag("enter", 
config::file_cache_enter_disk_resource_limit_mode_percent)
+                .tag("exit", 
config::file_cache_exit_disk_resource_limit_mode_percent);
+        config::file_cache_enter_disk_resource_limit_mode_percent = 88;
+        config::file_cache_exit_disk_resource_limit_mode_percent = 80;
+    }
+    size_t size_percentage = 0;
+    {
+        SCOPED_CACHE_LOCK(_mutex, this);
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
+    }
+    auto is_insufficient = [](const int& percentage) {
+        return percentage >= 
config::file_cache_enter_disk_resource_limit_mode_percent;
+    };
+    const bool previous_mode = 
_disk_resource_limit_mode.load(std::memory_order_relaxed);
+    bool current_mode = previous_mode;
+    if (is_memory_storage()) {
+        bool is_size_insufficient = 
is_insufficient(cast_set<int>(size_percentage));
+        if (is_size_insufficient) {
+            current_mode = true;
+        } else if (current_mode &&
+                   size_percentage < 
config::file_cache_exit_disk_resource_limit_mode_percent) {
+            current_mode = false;
+        }
+        _disk_resource_limit_mode.store(current_mode, 
std::memory_order_relaxed);

Review Comment:
   [P1] Keep query accounting at the actual block size
   
   This newly enables resource-limit mode for memory caches, but 
`try_reserve()` multiplies its local `size` by five before passing it to 
`QueryFileCacheContext::reserve()`. The caller still creates the cell with the 
original `current_size`, so a 10 MiB block is recorded as 50 MiB in the query 
LRU. The next over-limit admission hits `DCHECK(iter->size == cell_size)` (or, 
without checks, evicts/rejects using inconsistent byte counts). Keep the real 
block size for query accounting and use a separate enlarged eviction target, 
then cover memory pressure with `enable_file_cache_query_limit=true`.



##########
be/src/io/cache/block_file_cache.cpp:
##########
@@ -2023,31 +2059,70 @@ std::string BlockFileCache::reset_capacity(size_t 
new_capacity) {
             ss << " index_queue released " << queue_released;
             queue_released = remove_blocks(_ttl_queue);
             ss << " ttl_queue released " << queue_released;
-
-            _disk_resource_limit_mode = true;
-            _disk_limit_mode_metrics->set_value(1);
             ss << " total_space_released=" << space_released;
         }
         old_capacity = _capacity;
         _capacity = new_capacity;
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
         _cache_capacity_metrics->set_value(_capacity);
+        _cur_cache_size_metrics->set_value(_cur_cache_size);
+        _disk_limit_mode_metrics->set_value(
+                _disk_resource_limit_mode.load(std::memory_order_relaxed));
+        _need_evict_cache_in_advance_metrics->set_value(
+                _need_evict_cache_in_advance.load(std::memory_order_relaxed));
     }
-    auto use_time = duration_cast<milliseconds>(steady_clock::time_point() - 
adjust_start_time);
+    auto use_time = duration_cast<milliseconds>(steady_clock::now() - 
adjust_start_time);
     LOG(INFO) << "Finish tag deleted block. path=" << _cache_base_path
+              << " storage_type=" << 
file_cache_storage_type_to_string(_storage->get_type())
+              << " size_percent=" << size_percentage
               << " use_time=" << cast_set<int64_t>(use_time.count());
     ss << " old_capacity=" << old_capacity << " new_capacity=" << new_capacity;
     LOG(INFO) << ss.str();
     return ss.str();
 }
 
 void BlockFileCache::check_disk_resource_limit() {
-    if (_storage->get_type() != FileCacheStorageType::DISK) {
+    // ATTN: due to that can be changed dynamically, set it to default value 
if it's invalid
+    // FIXME: reject with config validator
+    if (config::file_cache_enter_disk_resource_limit_mode_percent <=
+        config::file_cache_exit_disk_resource_limit_mode_percent) {
+        LOG_WARNING("config error, set to default value")
+                .tag("enter", 
config::file_cache_enter_disk_resource_limit_mode_percent)
+                .tag("exit", 
config::file_cache_exit_disk_resource_limit_mode_percent);
+        config::file_cache_enter_disk_resource_limit_mode_percent = 88;
+        config::file_cache_exit_disk_resource_limit_mode_percent = 80;
+    }
+    size_t size_percentage = 0;
+    {
+        SCOPED_CACHE_LOCK(_mutex, this);
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
+    }
+    auto is_insufficient = [](const int& percentage) {
+        return percentage >= 
config::file_cache_enter_disk_resource_limit_mode_percent;
+    };
+    const bool previous_mode = 
_disk_resource_limit_mode.load(std::memory_order_relaxed);
+    bool current_mode = previous_mode;
+    if (is_memory_storage()) {
+        bool is_size_insufficient = 
is_insufficient(cast_set<int>(size_percentage));
+        if (is_size_insufficient) {
+            current_mode = true;

Review Comment:
   [P2] Stop five-times memory eviction below the exit watermark
   
   This newly lets cache-size pressure enable the disk resource-limit policy 
for memory storage, but requests only consume the cached flag; they never 
refresh it after their own eviction. With 95/100 MiB cached, successive 1 MiB 
misses can evict 5 MiB then admit 1 MiB, taking usage 95 -> 91 -> 87. Although 
that is already below the 88% exit watermark, more requests keep evicting 
five-for-one until the 5-second monitor runs, potentially draining all 
releasable cache or returning `SKIP_CACHE`. This is distinct from the 
reset-growth thread because no reset is involved. Recheck/clear the memory 
policy from the locked current size before applying the enlarged eviction 
target, while preserving the real request size for query accounting, and cover 
sustained misses between monitor ticks.



##########
be/src/io/cache/block_file_cache.cpp:
##########
@@ -2113,7 +2175,41 @@ void BlockFileCache::check_disk_resource_limit() {
 }
 
 void BlockFileCache::check_need_evict_cache_in_advance() {
-    if (_storage->get_type() != FileCacheStorageType::DISK) {
+    // ATTN: due to that can be changed dynamically, set it to default value 
if it's invalid
+    // FIXME: reject with config validator
+    if (config::file_cache_enter_need_evict_cache_in_advance_percent <=
+        config::file_cache_exit_need_evict_cache_in_advance_percent) {
+        LOG_WARNING("config error, set to default value")
+                .tag("enter", 
config::file_cache_enter_need_evict_cache_in_advance_percent)
+                .tag("exit", 
config::file_cache_exit_need_evict_cache_in_advance_percent);
+        config::file_cache_enter_need_evict_cache_in_advance_percent = 78;
+        config::file_cache_exit_need_evict_cache_in_advance_percent = 75;
+    }
+    size_t size_percentage = 0;
+    {
+        SCOPED_CACHE_LOCK(_mutex, this);
+        size_percentage = calc_size_percentage_unlocked(cache_lock);
+    }
+    auto is_insufficient = [](const int& percentage) {
+        return percentage >= 
config::file_cache_enter_need_evict_cache_in_advance_percent;
+    };
+    const bool previous_mode = 
_need_evict_cache_in_advance.load(std::memory_order_relaxed);
+    bool current_mode = previous_mode;
+    if (is_memory_storage()) {
+        bool is_size_insufficient = 
is_insufficient(cast_set<int>(size_percentage));
+        if (is_size_insufficient) {
+            current_mode = true;
+        } else if (current_mode &&
+                   size_percentage < 
config::file_cache_exit_need_evict_cache_in_advance_percent) {
+            current_mode = false;
+        }
+        _need_evict_cache_in_advance.store(current_mode, 
std::memory_order_relaxed);

Review Comment:
   [P1] Prevent a stale memory recycle from deleting a replacement
   
   This newly enables the advance-eviction worker for memory storage, but that 
worker removes metadata with `sync=false` and queues only the block's 
hash/offset for GC. Before GC consumes the entry, a request for the same block 
can create a new cell and replace the still-present payload in 
`ShardMemHashTable`; the old queued `remove()` then erases that replacement by 
the same key. The new cell remains `DOWNLOADED`, so its next read fails with 
`key not found in in-memory cache map`. Make memory advance deletion 
synchronous, or give queued deletions a generation/identity check, and cover 
pause-GC -> evict -> redownload -> resume-GC deterministically.



##########
be/src/io/cache/block_file_cache.cpp:
##########
@@ -2327,7 +2408,7 @@ void BlockFileCache::run_background_evict_in_advance() {
         batch = config::file_cache_evict_in_advance_batch_bytes;
 
         // Skip if eviction not needed or too many pending recycles
-        if (!_need_evict_cache_in_advance ||
+        if (!_need_evict_cache_in_advance.load(std::memory_order_relaxed) ||

Review Comment:
   [P2] Stop memory advance eviction at the exit watermark
   
   This worker now consumes the memory-capacity flag, but its fixed batch 
ignores the exit watermark and only the 5-second monitor clears the flag. With 
the defaults, a 90/100 MiB memory cache needs to release only 5 MiB to reach 
the 85% exit point, yet the first 1-second worker pass can evict 30 MiB and 
fall to 60 MiB; later passes can continue toward empty before the monitor 
refreshes the flag. This is distinct from the existing reset-growth thread 
because no reset is involved. Under `_mutex`, cap each memory pass to the bytes 
above the exit watermark and clear/skip when already at or below it, then cover 
first-pass capping and later-pass termination with real blocks.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to