github-actions[bot] commented on code in PR #64496:
URL: https://github.com/apache/doris/pull/64496#discussion_r3670971105
##########
be/src/io/cache/fs_file_cache_storage.cpp:
##########
@@ -750,8 +1383,8 @@ void
FSFileCacheStorage::load_cache_info_into_memory(BlockFileCache* _mgr) const
BatchLoadArgs args;
args.ctx = context;
args.hash = hash;
- args.key_path = key_it->path();
- args.offset_path = offset_it->path();
+ args.key_path = key_path;
+ args.offset_path = offset_path;
Review Comment:
[P1] Keep duplicate sizes bound to the surviving TTL path
During startup the first `(hash, offset)` fixes this cell's expiration/path,
but later duplicate handling lets a same-offset file from another positive-TTL
directory resize that cell. The new duplicate checker then treats the first
expiration as stable and deletes the later directory. If the later file was
larger, the surviving cell advertises a larger range than its file;
`LocalFileReader` clamps at EOF and `FSFileCacheStorage::read()` enforces the
byte count only with `DCHECK`, so release reads can return success with the
tail unfilled. If it was smaller, range and cache accounting still come from
the deleted sibling. Please resize only from the same canonical path, or mark
the startup image inconsistent and re-stat the selected canonical file before
repair. Add a restart test with the same offset and unequal sizes in two
positive-TTL directories.
##########
be/src/io/cache/fs_file_cache_storage.cpp:
##########
@@ -291,6 +507,391 @@ Status
FSFileCacheStorage::change_key_meta_expiration(const FileCacheKey& key,
return Status::OK();
}
+Status FSFileCacheStorage::scan_disk_cache(DiskScanKeyDirCallback on_key_dir,
+ DiskScanBlockFileCallback
on_block_file,
+ DiskScanQuarantineCallback
on_quarantine,
+ TokenBucketRateLimiterHolder*
scan_limiter,
+ DiskScanCancellationCallback
should_cancel) {
+ auto is_cancelled = [&]() { return should_cancel && should_cancel(); };
+ auto consume_scan_token = [&]() -> bool {
+ if (is_cancelled()) {
+ return false;
+ }
+ TEST_SYNC_POINT_CALLBACK("FSFileCacheStorage::before_disk_scan_token");
+ if (scan_limiter != nullptr) {
+ scan_limiter->add(1, should_cancel);
+ }
+ return !is_cancelled();
+ };
+
+ auto scan_key_dir = [&](const std::string& prefix,
+ const std::filesystem::path& key_dir_path) ->
Status {
+ if (is_cancelled()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ std::error_code ec;
+ const auto key_dir_status =
std::filesystem::symlink_status(key_dir_path, ec);
+ if (ec || key_dir_status.type() !=
std::filesystem::file_type::directory) {
+ return Status::OK();
+ }
+
+ UInt128Wrapper hash;
+ uint64_t expiration_time = 0;
+ if (!parse_key_dir_name(key_dir_path.filename().native(), &hash,
&expiration_time)) {
+ return Status::OK();
+ }
+
+ DiskScanKeyDirEntry key_dir_entry;
+ key_dir_entry.prefix = prefix;
+ key_dir_entry.hash = hash;
+ key_dir_entry.expiration_time = expiration_time;
+ key_dir_entry.key_dir = key_dir_path;
+ RETURN_IF_ERROR(on_key_dir(key_dir_entry));
+
+ std::vector<std::string> block_files;
+ if (!consume_scan_token()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ auto st = collect_directory_entries(key_dir_path, block_files,
should_cancel);
+ if (!st.ok()) {
+ if (st.is<ErrorCode::CANCELLED>()) {
+ return st;
+ }
+ LOG(WARNING) << "Failed to list file cache key dir " <<
key_dir_path
+ << ", error=" << st;
+ return Status::OK();
+ }
+ for (const auto& block_file : block_files) {
+ if (!consume_scan_token()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ std::filesystem::path block_file_path(block_file);
+ std::string quarantined_file_name;
+ if (parse_disk_scan_quarantine(block_file_path,
&quarantined_file_name) &&
+ is_cache_block_file_name(quarantined_file_name)) {
+ auto file_status =
std::filesystem::symlink_status(block_file_path, ec);
+ if (ec) {
+ continue;
+ }
+ RETURN_IF_ERROR(on_quarantine(
+ {block_file_path,
+ file_status.type() ==
std::filesystem::file_type::directory}));
+ continue;
+ }
+ const auto block_file_status =
std::filesystem::symlink_status(block_file_path, ec);
+ if (ec || block_file_status.type() !=
std::filesystem::file_type::regular) {
+ continue;
+ }
+ int64_t file_size = 0;
+ auto identity = get_file_identity(block_file_path, 0);
+ if (!identity) {
+ continue;
+ }
+ file_size = static_cast<int64_t>(identity->size);
+ size_t offset = 0;
+ bool is_tmp = false;
+ FileCacheType cache_type = FileCacheType::NORMAL;
+ if (!parse_filename_suffix_to_cache_type(fs,
block_file_path.filename().native(),
+ expiration_time,
file_size, &offset, &is_tmp,
+ &cache_type, false)) {
+ continue;
+ }
+
+ DiskScanBlockFileEntry block_entry;
+ block_entry.hash = hash;
+ block_entry.offset = offset;
+ block_entry.expiration_time = expiration_time;
+ block_entry.cache_type = cache_type;
+ block_entry.is_tmp = is_tmp;
+ block_entry.identity = *identity;
+ block_entry.file_path = block_file_path;
+ block_entry.key_dir = key_dir_path;
+ RETURN_IF_ERROR(on_block_file(block_entry));
+ }
+ return Status::OK();
+ };
+
+ if constexpr (USE_CACHE_VERSION2) {
+ std::vector<std::string> prefix_entries;
+ if (!consume_scan_token()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ RETURN_IF_ERROR(collect_directory_entries(_cache_base_path,
prefix_entries, should_cancel));
+ std::sort(prefix_entries.begin(), prefix_entries.end());
+ for (const auto& prefix_path_str : prefix_entries) {
+ if (is_cancelled()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ std::filesystem::path prefix_path(prefix_path_str);
+ std::error_code ec;
+ const auto prefix_status =
std::filesystem::symlink_status(prefix_path, ec);
+ if (ec || prefix_status.type() !=
std::filesystem::file_type::directory) {
+ continue;
+ }
+ auto prefix = prefix_path.filename().native();
+ if (prefix.size() != KEY_PREFIX_LENGTH) {
+ continue;
+ }
+
+ std::vector<std::string> key_dir_entries;
+ if (!consume_scan_token()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ auto st = collect_directory_entries(prefix_path, key_dir_entries,
should_cancel);
+ if (!st.ok()) {
+ if (st.is<ErrorCode::CANCELLED>()) {
+ return st;
+ }
+ LOG(WARNING) << "Failed to list file cache prefix dir " <<
prefix_path
+ << ", error=" << st;
+ continue;
+ }
+ std::sort(key_dir_entries.begin(), key_dir_entries.end());
+ for (const auto& key_dir : key_dir_entries) {
+ std::filesystem::path key_dir_path(key_dir);
+ std::string quarantined_key_dir_name;
+ UInt128Wrapper quarantined_hash;
+ uint64_t quarantined_expiration = 0;
+ if (parse_disk_scan_quarantine(key_dir_path,
&quarantined_key_dir_name) &&
+ parse_key_dir_name(quarantined_key_dir_name,
&quarantined_hash,
+ &quarantined_expiration)) {
+ auto key_dir_status =
std::filesystem::symlink_status(key_dir_path, ec);
+ if (ec) {
+ continue;
+ }
+ RETURN_IF_ERROR(on_quarantine(
+ {key_dir_path,
+ key_dir_status.type() ==
std::filesystem::file_type::directory}));
+ continue;
+ }
+ RETURN_IF_ERROR(scan_key_dir(prefix, key_dir));
+ }
+ }
+ } else {
+ std::vector<std::string> key_dir_entries;
+ if (!consume_scan_token()) {
+ return Status::Cancelled("file cache disk scan cancelled");
+ }
+ RETURN_IF_ERROR(
+ collect_directory_entries(_cache_base_path, key_dir_entries,
should_cancel));
+ std::sort(key_dir_entries.begin(), key_dir_entries.end());
+ for (const auto& key_dir : key_dir_entries) {
+ std::filesystem::path key_dir_path(key_dir);
+ std::string quarantined_key_dir_name;
+ UInt128Wrapper quarantined_hash;
+ uint64_t quarantined_expiration = 0;
+ if (parse_disk_scan_quarantine(key_dir_path,
&quarantined_key_dir_name) &&
+ parse_key_dir_name(quarantined_key_dir_name, &quarantined_hash,
+ &quarantined_expiration)) {
+ std::error_code ec;
+ auto key_dir_status =
std::filesystem::symlink_status(key_dir_path, ec);
+ if (ec) {
+ continue;
+ }
+ RETURN_IF_ERROR(on_quarantine(
+ {key_dir_path,
+ key_dir_status.type() ==
std::filesystem::file_type::directory}));
+ continue;
+ }
+ RETURN_IF_ERROR(scan_key_dir("", key_dir));
+ }
+ }
+ return Status::OK();
+}
+
+bool FSFileCacheStorage::has_active_writer(const UInt128Wrapper& hash, size_t
offset) {
+ std::lock_guard lock(_mtx);
+ auto key = std::make_pair(hash, offset);
+ return _key_to_writer.contains(key) || _publishing_writers.contains(key);
+}
+
+bool FSFileCacheStorage::has_active_writer_for_hash(const UInt128Wrapper&
hash) {
+ std::lock_guard lock(_mtx);
+ for (const auto& [key, _] : _key_to_writer) {
Review Comment:
[P2] Keep cache-wide repair validation independent of unrelated writers
Each stale TTL-directory action reaches this scan while
`quarantine_disk_scan_action()` holds `BlockFileCache::_mutex`. Walking both
cache-wide writer containers makes `W` active/publishing writers and `D` stale
directories cost `O(W*D)` unrelated comparisons with all `get_or_set()` and
metadata work excluded. This can also wait on the same storage `_mtx` that
`append()` holds across `create_directory()` and `create_file()`, so slow
filesystem creation for another hash stalls the cache under `_mutex`. The
per-action repair token does not bound either wait. Please maintain an O(1)
per-hash active/publishing count or generation that does not wait behind
unrelated writer creation, and add a many-writer/many-stale-directory latency
test.
--
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]