This is an automated email from the ASF dual-hosted git repository.
gavinchou 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 668805a6ab2 [fix](filecache) reject oversized block size in add_cell
(#62878)
668805a6ab2 is described below
commit 668805a6ab2ce5ffcebf7e539df03ca651d99eea
Author: deardeng <[email protected]>
AuthorDate: Thu May 7 11:09:24 2026 +0800
[fix](filecache) reject oversized block size in add_cell (#62878)
When directory_entry::file_size(ec) fails, it returns (uintmax_t)-1.
load_cache_info_into_memory did not check ec, so UINT64_MAX flowed into
add_cell, which only warned and then registered the cell — corrupting
_files, LRUQueue::cache_size, _cur_cache_size, and the LRU dump
(survives restart).
- fs_file_cache_storage: check ec after file_size, skip on error
- add_cell: turn the >1GiB check into a hard reject (return nullptr)
- UT: add_cell_rejects_oversized_size
---
be/src/io/cache/block_file_cache.cpp | 3 +-
be/src/io/cache/block_file_cache.h | 1 +
be/src/io/cache/fs_file_cache_storage.cpp | 5 ++
be/test/io/cache/block_file_cache_test.cpp | 76 ++++++++++++++++++++++++++++++
4 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/be/src/io/cache/block_file_cache.cpp
b/be/src/io/cache/block_file_cache.cpp
index 5fd8980d5f2..3ff1526c32f 100644
--- a/be/src/io/cache/block_file_cache.cpp
+++ b/be/src/io/cache/block_file_cache.cpp
@@ -881,9 +881,10 @@ FileBlockCell* BlockFileCache::add_cell(const
UInt128Wrapper& hash, const CacheC
<< " tablet_id=" << context.tablet_id;
if (size > 1024 * 1024 * 1024) {
- LOG(WARNING) << "File block size is too large for a block. size=" <<
size
+ LOG(WARNING) << "File block size is too large for a block, reject.
size=" << size
<< " hash=" << hash.to_string() << " offset=" << offset
<< " stack:" << get_stack_trace();
+ return nullptr;
}
auto& offsets = _files[hash];
diff --git a/be/src/io/cache/block_file_cache.h
b/be/src/io/cache/block_file_cache.h
index 4c155ba2d44..bc7ec7ce4c2 100644
--- a/be/src/io/cache/block_file_cache.h
+++ b/be/src/io/cache/block_file_cache.h
@@ -169,6 +169,7 @@ class BlockFileCache {
friend class CacheLRUDumper;
friend class LRUQueueRecorder;
friend struct FileBlockCell;
+ friend class BlockFileCacheTest;
public:
// hash the file_name to uint128
diff --git a/be/src/io/cache/fs_file_cache_storage.cpp
b/be/src/io/cache/fs_file_cache_storage.cpp
index aa8782d761b..67c31290d24 100644
--- a/be/src/io/cache/fs_file_cache_storage.cpp
+++ b/be/src/io/cache/fs_file_cache_storage.cpp
@@ -695,6 +695,11 @@ void
FSFileCacheStorage::load_cache_info_into_memory_from_fs(BlockFileCache* mgr
context.expiration_time = expiration_time;
for (; offset_it != std::filesystem::directory_iterator();
++offset_it) {
size_t size = offset_it->file_size(ec);
+ if (ec) [[unlikely]] {
+ LOG(WARNING) << "skip cache file, file_size failed, file="
+ << offset_it->path().native() << " err=" <<
ec.message();
+ continue;
+ }
size_t offset = 0;
bool is_tmp = false;
FileCacheType cache_type = FileCacheType::NORMAL;
diff --git a/be/test/io/cache/block_file_cache_test.cpp
b/be/test/io/cache/block_file_cache_test.cpp
index cf417b4f347..41ae356519d 100644
--- a/be/test/io/cache/block_file_cache_test.cpp
+++ b/be/test/io/cache/block_file_cache_test.cpp
@@ -8058,4 +8058,80 @@ TEST_F(BlockFileCacheTest,
set_downloaded_empty_block_branch) {
ASSERT_EQ(block.get_downloader(), 0);
}
+// Reproduces OPENSOURCE-325: when fs_file_cache_storage's directory scan reads
+// a file size via std::filesystem::directory_entry::file_size(ec) and the
+// underlying syscall fails (e.g. file removed concurrently by clear/GC), the
+// returned value is (uintmax_t)-1 == UINT64_MAX. Before the fix, that bogus
+// size flowed into BlockFileCache::add_cell, which only logged a WARNING and
+// then registered the cell, polluting _files, the LRU queue's cache_size, and
+// _cur_cache_size. After the fix, add_cell rejects sizes > 1GiB and returns
+// nullptr without touching any internal state.
+TEST_F(BlockFileCacheTest, add_cell_rejects_oversized_size) {
+ if (fs::exists(cache_base_path)) {
+ fs::remove_all(cache_base_path);
+ }
+ fs::create_directories(cache_base_path);
+
+ io::FileCacheSettings settings;
+ settings.query_queue_size = 30_mb;
+ settings.query_queue_elements = 30;
+ settings.capacity = 30_mb;
+ settings.max_file_block_size = 1_mb;
+ settings.max_query_cache_size = 30;
+
+ io::BlockFileCache cache(cache_base_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("opensource_325_key");
+ 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;
+
+ const size_t kBadSize = std::numeric_limits<size_t>::max(); //
(uintmax_t)-1
+ const size_t kOffset = 1128267776; // matches the
JIRA report
+
+ size_t cur_cache_size_before = 0;
+ size_t normal_queue_size_before = 0;
+ {
+ std::lock_guard<std::mutex> cache_lock(cache._mutex);
+ cur_cache_size_before = cache._cur_cache_size;
+ normal_queue_size_before =
+
cache.get_queue(io::FileCacheType::NORMAL).get_capacity(cache_lock);
+ }
+
+ {
+ std::lock_guard<std::mutex> cache_lock(cache._mutex);
+ auto* cell = cache.add_cell(hash, ctx, kOffset, kBadSize,
io::FileBlock::State::DOWNLOADED,
+ cache_lock);
+ // Defensive guard must reject oversized blocks instead of polluting
state.
+ ASSERT_EQ(cell, nullptr);
+ }
+
+ {
+ std::lock_guard<std::mutex> cache_lock(cache._mutex);
+ // _files index untouched: no entry for (hash, kOffset).
+ ASSERT_EQ(cache._files.find(hash), cache._files.end());
+ // Global and per-queue size counters untouched (no UINT64_MAX added).
+ ASSERT_EQ(cache._cur_cache_size, cur_cache_size_before);
+
ASSERT_EQ(cache.get_queue(io::FileCacheType::NORMAL).get_capacity(cache_lock),
+ normal_queue_size_before);
+ }
+
+ if (fs::exists(cache_base_path)) {
+ fs::remove_all(cache_base_path);
+ }
+}
+
} // namespace doris::io
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]