Add more comments

This commit is contained in:
kssenii 2024-04-08 14:36:07 +02:00
parent eb9690016a
commit 259d50c57b
6 changed files with 112 additions and 77 deletions

View File

@ -17,6 +17,11 @@ namespace ErrorCodes
extern const int LOGICAL_ERROR; extern const int LOGICAL_ERROR;
} }
EvictionCandidates::EvictionCandidates()
: log(getLogger("EvictionCandidates"))
{
}
EvictionCandidates::~EvictionCandidates() EvictionCandidates::~EvictionCandidates()
{ {
/// Here `queue_entries_to_invalidate` contains queue entries /// Here `queue_entries_to_invalidate` contains queue entries
@ -64,8 +69,11 @@ void EvictionCandidates::add(
void EvictionCandidates::removeQueueEntries(const CachePriorityGuard::Lock & lock) void EvictionCandidates::removeQueueEntries(const CachePriorityGuard::Lock & lock)
{ {
auto log = getLogger("EvictionCandidates"); /// Remove queue entries of eviction candidates.
/// This will release space we consider to be hold for them.
LOG_TEST(log, "Will remove {} eviction candidates", size()); LOG_TEST(log, "Will remove {} eviction candidates", size());
for (const auto & [key, key_candidates] : candidates) for (const auto & [key, key_candidates] : candidates)
{ {
for (const auto & candidate : key_candidates.candidates) for (const auto & candidate : key_candidates.candidates)
@ -87,6 +95,7 @@ void EvictionCandidates::evict()
auto timer = DB::CurrentThread::getProfileEvents().timer(ProfileEvents::FilesystemCacheEvictMicroseconds); auto timer = DB::CurrentThread::getProfileEvents().timer(ProfileEvents::FilesystemCacheEvictMicroseconds);
/// If queue entries are already removed, then nothing to invalidate.
if (!removed_queue_entries) if (!removed_queue_entries)
queue_entries_to_invalidate.reserve(candidates_size); queue_entries_to_invalidate.reserve(candidates_size);
@ -184,6 +193,12 @@ void EvictionCandidates::finalize(
on_finalize.clear(); on_finalize.clear();
} }
bool EvictionCandidates::needFinalize() const
{
/// Do we need to call finalize()?
return !on_finalize.empty() || !queue_entries_to_invalidate.empty();
}
void EvictionCandidates::setSpaceHolder( void EvictionCandidates::setSpaceHolder(
size_t size, size_t size,
size_t elements, size_t elements,
@ -196,9 +211,4 @@ void EvictionCandidates::setSpaceHolder(
hold_space = std::make_unique<IFileCachePriority::HoldSpace>(size, elements, priority, lock); hold_space = std::make_unique<IFileCachePriority::HoldSpace>(size, elements, priority, lock);
} }
void EvictionCandidates::insert(EvictionCandidates && other, const CachePriorityGuard::Lock &)
{
candidates.insert(make_move_iterator(other.candidates.begin()), make_move_iterator(other.candidates.end()));
}
} }

View File

@ -9,7 +9,7 @@ class EvictionCandidates : private boost::noncopyable
public: public:
using FinalizeEvictionFunc = std::function<void(const CachePriorityGuard::Lock & lk)>; using FinalizeEvictionFunc = std::function<void(const CachePriorityGuard::Lock & lk)>;
EvictionCandidates() = default; EvictionCandidates();
~EvictionCandidates(); ~EvictionCandidates();
void add( void add(
@ -17,8 +17,6 @@ public:
LockedKey & locked_key, LockedKey & locked_key,
const CachePriorityGuard::Lock &); const CachePriorityGuard::Lock &);
void insert(EvictionCandidates && other, const CachePriorityGuard::Lock &);
void evict(); void evict();
void removeQueueEntries(const CachePriorityGuard::Lock &); void removeQueueEntries(const CachePriorityGuard::Lock &);
@ -29,6 +27,8 @@ public:
FileCacheQueryLimit::QueryContext * query_context, FileCacheQueryLimit::QueryContext * query_context,
const CachePriorityGuard::Lock &); const CachePriorityGuard::Lock &);
bool needFinalize() const;
size_t size() const { return candidates_size; } size_t size() const { return candidates_size; }
auto begin() const { return candidates.begin(); } auto begin() const { return candidates.begin(); }
@ -57,6 +57,8 @@ private:
bool removed_queue_entries = false; bool removed_queue_entries = false;
IFileCachePriority::HoldSpacePtr hold_space; IFileCachePriority::HoldSpacePtr hold_space;
LoggerPtr log;
}; };
using EvictionCandidatesPtr = std::unique_ptr<EvictionCandidates>; using EvictionCandidatesPtr = std::unique_ptr<EvictionCandidates>;

View File

@ -1389,7 +1389,18 @@ void FileCache::applySettingsIfPossible(const FileCacheSettings & new_settings,
|| new_settings.max_elements != actual_settings.max_elements) || new_settings.max_elements != actual_settings.max_elements)
{ {
EvictionCandidates eviction_candidates; EvictionCandidates eviction_candidates;
bool limits_satisfied = false; bool modified_size_limit = false;
/// In order to not block cache for the duration of cache resize,
/// we do:
/// a. Take a cache lock.
/// 1. Collect eviction candidates,
/// 2. Remove queue entries of eviction candidates.
/// This will release space we consider to be hold for them,
/// so that we can safely modify size limits.
/// 3. Modify size limits of cache.
/// b. Release a cache lock.
/// 1. Do actual eviction from filesystem.
{ {
cache_is_being_resized.store(true, std::memory_order_relaxed); cache_is_being_resized.store(true, std::memory_order_relaxed);
SCOPE_EXIT({ SCOPE_EXIT({
@ -1399,38 +1410,45 @@ void FileCache::applySettingsIfPossible(const FileCacheSettings & new_settings,
auto cache_lock = lockCache(); auto cache_lock = lockCache();
FileCacheReserveStat stat; FileCacheReserveStat stat;
limits_satisfied = main_priority->collectCandidatesForEviction( if (main_priority->collectCandidatesForEviction(
new_settings.max_size, new_settings.max_elements, 0/* max_candidates_to_evict */, stat, eviction_candidates, cache_lock); new_settings.max_size, new_settings.max_elements, 0/* max_candidates_to_evict */,
stat, eviction_candidates, cache_lock))
eviction_candidates.removeQueueEntries(cache_lock);
if (limits_satisfied)
{ {
/// Remove only queue entries of eviction candidates.
eviction_candidates.removeQueueEntries(cache_lock);
/// Note that (in-memory) metadata about corresponding file segments
/// (e.g. file segment info in CacheMetadata) will be removed
/// only after eviction from filesystem. This is needed to avoid
/// a race on removal of file from filesystsem and
/// addition of the same file as part of a newly cached file segment.
/// Modify cache size limits.
/// From this point cache eviction will follow them.
main_priority->modifySizeLimits( main_priority->modifySizeLimits(
new_settings.max_size, new_settings.max_elements, new_settings.slru_size_ratio, cache_lock); new_settings.max_size, new_settings.max_elements,
new_settings.slru_size_ratio, cache_lock);
modified_size_limit = true;
} }
else }
if (modified_size_limit)
{
try
{ {
LOG_WARNING(log, "Unable to modify size limit from {} to {}, " /// Do actual eviction from filesystem.
"elements limit from {} to {}", eviction_candidates.evict();
actual_settings.max_size, new_settings.max_size, }
actual_settings.max_elements, new_settings.max_elements); catch (...)
{
if (eviction_candidates.needFinalize())
eviction_candidates.finalize(nullptr, lockCache());
throw;
} }
}
try if (eviction_candidates.needFinalize())
{ eviction_candidates.finalize(nullptr, lockCache());
eviction_candidates.evict();
}
catch (...)
{
auto cache_lock = lockCache();
eviction_candidates.finalize(nullptr, cache_lock);
throw;
}
if (limits_satisfied)
{
LOG_INFO(log, "Changed max_size from {} to {}, max_elements from {} to {}", LOG_INFO(log, "Changed max_size from {} to {}, max_elements from {} to {}",
actual_settings.max_size, new_settings.max_size, actual_settings.max_size, new_settings.max_size,
actual_settings.max_elements, new_settings.max_elements); actual_settings.max_elements, new_settings.max_elements);
@ -1438,6 +1456,13 @@ void FileCache::applySettingsIfPossible(const FileCacheSettings & new_settings,
actual_settings.max_size = new_settings.max_size; actual_settings.max_size = new_settings.max_size;
actual_settings.max_elements = new_settings.max_elements; actual_settings.max_elements = new_settings.max_elements;
} }
else
{
LOG_WARNING(log, "Unable to modify size limit from {} to {}, "
"elements limit from {} to {}",
actual_settings.max_size, new_settings.max_size,
actual_settings.max_elements, new_settings.max_elements);
}
} }
if (new_settings.max_file_segment_size != actual_settings.max_file_segment_size) if (new_settings.max_file_segment_size != actual_settings.max_file_segment_size)

View File

@ -142,10 +142,8 @@ void FileCacheFactory::updateSettingsFromConfig(const Poco::Util::AbstractConfig
caches_by_name_copy = caches_by_name; caches_by_name_copy = caches_by_name;
} }
auto * log = &Poco::Logger::get("FileCacheFactory");
std::unordered_set<std::string> checked_paths; std::unordered_set<std::string> checked_paths;
for (const auto & [cache_name, cache_info] : caches_by_name_copy) for (const auto & [_, cache_info] : caches_by_name_copy)
{ {
if (cache_info->config_path.empty() || checked_paths.contains(cache_info->config_path)) if (cache_info->config_path.empty() || checked_paths.contains(cache_info->config_path))
continue; continue;
@ -158,10 +156,12 @@ void FileCacheFactory::updateSettingsFromConfig(const Poco::Util::AbstractConfig
FileCacheSettings old_settings = cache_info->getSettings(); FileCacheSettings old_settings = cache_info->getSettings();
if (old_settings == new_settings) if (old_settings == new_settings)
{ {
LOG_TRACE(log, "No settings changes for cache: {}", cache_name);
continue; continue;
} }
/// FIXME: registerDiskCache modifies `path` setting of FileCacheSettings if path is relative.
/// This can lead to calling applySettingsIfPossible even though nothing changed, which is avoidable.
// LOG_TRACE(log, "Will apply settings changes for cache {}. " // LOG_TRACE(log, "Will apply settings changes for cache {}. "
// "Settings changes: {} (new settings: {}, old_settings: {})", // "Settings changes: {} (new settings: {}, old_settings: {})",
// cache_name, fmt::join(new_settings.getSettingsDiff(old_settings), ", "), // cache_name, fmt::join(new_settings.getSettingsDiff(old_settings), ", "),

View File

@ -280,7 +280,7 @@ bool LRUFileCachePriority::collectCandidatesForEviction(
auto can_fit = [&] auto can_fit = [&]
{ {
return canFit(size, 1, stat.total_stat.releasable_size, stat.total_stat.releasable_count, lock); return canFit(size, elements, stat.total_stat.releasable_size, stat.total_stat.releasable_count, lock);
}; };
iterateForEviction(res, stat, can_fit, lock); iterateForEviction(res, stat, can_fit, lock);

View File

@ -941,49 +941,47 @@ KeyMetadata::iterator LockedKey::removeFileSegmentImpl(
file_segment->detach(segment_lock, *this); file_segment->detach(segment_lock, *this);
try
{ {
try const auto path = key_metadata->getFileSegmentPath(*file_segment);
if (file_segment->segment_kind == FileSegmentKind::Temporary)
{ {
const auto path = key_metadata->getFileSegmentPath(*file_segment); /// FIXME: For temporary file segment the requirement is not as strong because
if (file_segment->segment_kind == FileSegmentKind::Temporary) /// the implementation of "temporary data in cache" creates files in advance.
{ if (fs::exists(path))
/// FIXME: For temporary file segment the requirement is not as strong because
/// the implementation of "temporary data in cache" creates files in advance.
if (fs::exists(path))
fs::remove(path);
}
else if (file_segment->downloaded_size == 0)
{
chassert(!fs::exists(path));
}
else if (fs::exists(path))
{
fs::remove(path); fs::remove(path);
/// Clear OpenedFileCache to avoid reading from incorrect file descriptor.
int flags = file_segment->getFlagsForLocalRead();
/// Files are created with flags from file_segment->getFlagsForLocalRead()
/// plus optionally O_DIRECT is added, depends on query setting, so remove both.
OpenedFileCache::instance().remove(path, flags);
OpenedFileCache::instance().remove(path, flags | O_DIRECT);
LOG_TEST(key_metadata->logger(), "Removed file segment at path: {}", path);
}
else if (!can_be_broken)
{
#ifdef ABORT_ON_LOGICAL_ERROR
throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected path {} to exist", path);
#else
LOG_WARNING(key_metadata->logger(), "Expected path {} to exist, while removing {}:{}",
path, getKey(), file_segment->offset());
#endif
}
} }
catch (...) else if (file_segment->downloaded_size == 0)
{ {
tryLogCurrentException(__PRETTY_FUNCTION__); chassert(!fs::exists(path));
chassert(false);
} }
else if (fs::exists(path))
{
fs::remove(path);
/// Clear OpenedFileCache to avoid reading from incorrect file descriptor.
int flags = file_segment->getFlagsForLocalRead();
/// Files are created with flags from file_segment->getFlagsForLocalRead()
/// plus optionally O_DIRECT is added, depends on query setting, so remove both.
OpenedFileCache::instance().remove(path, flags);
OpenedFileCache::instance().remove(path, flags | O_DIRECT);
LOG_TEST(key_metadata->logger(), "Removed file segment at path: {}", path);
}
else if (!can_be_broken)
{
#ifdef ABORT_ON_LOGICAL_ERROR
throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected path {} to exist", path);
#else
LOG_WARNING(key_metadata->logger(), "Expected path {} to exist, while removing {}:{}",
path, getKey(), file_segment->offset());
#endif
}
}
catch (...)
{
tryLogCurrentException(__PRETTY_FUNCTION__);
chassert(false);
} }
return key_metadata->erase(it); return key_metadata->erase(it);