From 30751419b60abbc6788b61ce5a8e7982191087b4 Mon Sep 17 00:00:00 2001 From: lijiachen Date: Thu, 23 Oct 2025 19:18:03 +0800 Subject: [PATCH] hotness management for gc --- ucm/store/infra/file/ifile.h | 1 + ucm/store/infra/file/posix_file.cc | 12 +++ ucm/store/infra/file/posix_file.h | 1 + ucm/store/infra/template/timer.h | 93 +++++++++++++++++++ ucm/store/nfsstore/cc/api/nfsstore.cc | 18 +++- ucm/store/nfsstore/cc/api/nfsstore.h | 4 +- .../cc/domain/hotness/hotness_manager.h | 75 +++++++++++++++ .../nfsstore/cc/domain/hotness/hotness_set.cc | 70 ++++++++++++++ .../nfsstore/cc/domain/hotness/hotness_set.h | 47 ++++++++++ .../cc/domain/hotness/hotness_timer.h | 53 +++++++++++ ucm/store/nfsstore/cpy/nfsstore.py.cc | 2 + ucm/store/test/case/nfsstore/hotness_test.cc | 54 +++++++++++ 12 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 ucm/store/infra/template/timer.h create mode 100644 ucm/store/nfsstore/cc/domain/hotness/hotness_manager.h create mode 100644 ucm/store/nfsstore/cc/domain/hotness/hotness_set.cc create mode 100644 ucm/store/nfsstore/cc/domain/hotness/hotness_set.h create mode 100644 ucm/store/nfsstore/cc/domain/hotness/hotness_timer.h create mode 100644 ucm/store/test/case/nfsstore/hotness_test.cc diff --git a/ucm/store/infra/file/ifile.h b/ucm/store/infra/file/ifile.h index 4ff05a9d..74b77cba 100644 --- a/ucm/store/infra/file/ifile.h +++ b/ucm/store/infra/file/ifile.h @@ -70,6 +70,7 @@ class IFile { virtual Status MMap(void*& addr, size_t size, bool write, bool read, bool shared) = 0; virtual void MUnmap(void* addr, size_t size) = 0; virtual void ShmUnlink() = 0; + virtual Status UpdateTime() = 0; private: std::string path_; diff --git a/ucm/store/infra/file/posix_file.cc b/ucm/store/infra/file/posix_file.cc index 12566156..d9c47f36 100644 --- a/ucm/store/infra/file/posix_file.cc +++ b/ucm/store/infra/file/posix_file.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "logger/logger.h" @@ -230,4 +231,15 @@ void PosixFile::ShmUnlink() } } +Status PosixFile::UpdateTime() +{ + auto ret = utime(this->Path().c_str(), nullptr); + auto eno = errno; + if (ret != 0) { + UC_ERROR("Failed({},{}) to update time file({}).", ret, eno, this->Path()); + return Status::OsApiError(); + } + return Status::OK(); +} + } // namespace UC diff --git a/ucm/store/infra/file/posix_file.h b/ucm/store/infra/file/posix_file.h index a401c0c5..becbd28a 100644 --- a/ucm/store/infra/file/posix_file.h +++ b/ucm/store/infra/file/posix_file.h @@ -47,6 +47,7 @@ class PosixFile : public IFile { Status MMap(void*& addr, size_t size, bool write, bool read, bool shared) override; void MUnmap(void* addr, size_t size) override; void ShmUnlink() override; + Status UpdateTime() override; private: int32_t handle_; diff --git a/ucm/store/infra/template/timer.h b/ucm/store/infra/template/timer.h new file mode 100644 index 00000000..1963faa8 --- /dev/null +++ b/ucm/store/infra/template/timer.h @@ -0,0 +1,93 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ +#ifndef UNIFIEDCACHE_TIMER_H +#define UNIFIEDCACHE_TIMER_H + +#include +#include +#include +#include +#include +#include "logger/logger.h" +#include "status/status.h" + +namespace UC { + +template +class Timer { +public: + Timer(const std::chrono::seconds& interval, Callable&& callable) + : interval_(interval), callable_(callable), running_(false) {} + ~Timer() { + { + std::lock_guard lg(this->mutex_); + this->running_ = false; + } + + this->cv_.notify_one(); + if (this->thread_.joinable()) { this->thread_.join(); } + } + Status Start() + { + { + std::lock_guard lg(this->mutex_); + if (this->running_) { return Status::OK(); } + } + try { + this->running_ = true; + this->thread_ = std::thread(&Timer::Runner, this); + return Status::OK(); + } catch (const std::exception& e) { + UC_ERROR("Failed({}) to start timer.", e.what()); + return Status::OutOfMemory(); + } + } + +private: + void Runner() + { + while (this->running_) { + try { + { + std::unique_lock lg(this->mutex_); + this->cv_.wait_for(lg, this->interval_, [this] { return !this->running_; }); + if (!this->running_) { break; } + } + this->callable_(); + } catch (const std::exception& e) { UC_ERROR("Failed({}) to run timer.", e.what()); } + } + } + +private: + std::chrono::seconds interval_; + Callable callable_; + std::thread thread_; + std::mutex mutex_; + std::condition_variable cv_; + std::atomic running_; +}; + +} // namespace UC + +#endif \ No newline at end of file diff --git a/ucm/store/nfsstore/cc/api/nfsstore.cc b/ucm/store/nfsstore/cc/api/nfsstore.cc index 0b5f9637..013b5980 100644 --- a/ucm/store/nfsstore/cc/api/nfsstore.cc +++ b/ucm/store/nfsstore/cc/api/nfsstore.cc @@ -26,6 +26,7 @@ #include "logger/logger.h" #include "space/space_manager.h" #include "trans/trans_manager.h" +#include "hotness/hotness_manager.h" namespace UC { @@ -49,6 +50,13 @@ class NFSStoreImpl : public NFSStore { return status.Underlying(); } } + if (config.hotnessEnable) { + status = this->hotnessMgr_.Setup(config.hotnessInterval, this->spaceMgr_.GetSpaceLayout()); + if (status.Failure()) { + UC_ERROR("Failed({}) to setup HotnessManager.", status); + return status.Underlying(); + } + } this->ShowConfig(config); return Status::OK().Underlying(); } @@ -56,7 +64,12 @@ class NFSStoreImpl : public NFSStore { { return this->spaceMgr_.NewBlock(block).Underlying(); } - bool Lookup(const std::string& block) override { return this->spaceMgr_.LookupBlock(block); } + bool Lookup(const std::string& block) override + { + auto found = this->spaceMgr_.LookupBlock(block); + if (found) { this->hotnessMgr_.Visit(block); } + return found; + } void Commit(const std::string& block, const bool success) override { this->spaceMgr_.CommitBlock(block, success); @@ -105,11 +118,14 @@ class NFSStoreImpl : public NFSStore { UC_INFO("Set UC::BufferNumber to {}.", config.transferBufferNumber); UC_INFO("Set UC::TimeoutMs to {}.", config.transferTimeoutMs); UC_INFO("Set UC::TempDumpDirEnable to {}.", config.tempDumpDirEnable); + UC_INFO("Set UC::HotnessInterval to {}.", config.hotnessInterval); + UC_INFO("Set UC::HotnessEnable to {}.", config.hotnessEnable); } private: SpaceManager spaceMgr_; TransManager transMgr_; + HotnessManager hotnessMgr_; }; int32_t NFSStore::Setup(const Config& config) diff --git a/ucm/store/nfsstore/cc/api/nfsstore.h b/ucm/store/nfsstore/cc/api/nfsstore.h index 5c1af70f..8dc6854f 100644 --- a/ucm/store/nfsstore/cc/api/nfsstore.h +++ b/ucm/store/nfsstore/cc/api/nfsstore.h @@ -41,13 +41,15 @@ class NFSStore : public CCStore { size_t transferBufferNumber; size_t transferTimeoutMs; bool tempDumpDirEnable; + bool hotnessEnable; + size_t hotnessInterval; Config(const std::vector& storageBackends, const size_t kvcacheBlockSize, const bool transferEnable) : storageBackends{storageBackends}, kvcacheBlockSize{kvcacheBlockSize}, transferEnable{transferEnable}, transferDeviceId{-1}, transferStreamNumber{32}, transferIoSize{262144}, transferBufferNumber{512}, transferTimeoutMs{30000}, - tempDumpDirEnable{false} + tempDumpDirEnable{false}, hotnessEnable{true}, hotnessInterval{60} { } }; diff --git a/ucm/store/nfsstore/cc/domain/hotness/hotness_manager.h b/ucm/store/nfsstore/cc/domain/hotness/hotness_manager.h new file mode 100644 index 00000000..6572ee48 --- /dev/null +++ b/ucm/store/nfsstore/cc/domain/hotness/hotness_manager.h @@ -0,0 +1,75 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#ifndef UNIFIEDCACHE_HOTNESS_MANAGER_H +#define UNIFIEDCACHE_HOTNESS_MANAGER_H + +#include +#include +#include "hotness_set.h" +#include "hotness_timer.h" +#include "logger/logger.h" + +namespace UC { + +class HotnessManager { +public: + Status Setup(const size_t interval, const SpaceLayout* spaceLayout) + { + this->hotnessTimer_.SetInterval(interval); + this->layout_ = spaceLayout; + this->setupSuccess_ = true; + return Status::OK(); + } + + void Visit(const std::string& blockId) + { + if (!this->setupSuccess_) { + return; + } + + this->hotnessSet_.Insert(blockId); + auto old = this->serviceRunning_.load(std::memory_order_acquire); + if (old) { return; } + if (this->serviceRunning_.compare_exchange_weak(old, true, std::memory_order_acq_rel)) { + auto updater = std::bind(&HotnessSet::UpdateHotness, &this->hotnessSet_, this->layout_); + if (this->hotnessTimer_.Start(std::move(updater)).Success()) { + UC_INFO("Space hotness service started."); + return; + } + this->serviceRunning_ = old; + } + } + +private: + bool setupSuccess_{false}; + std::atomic_bool serviceRunning_{false}; + const SpaceLayout* layout_; + HotnessSet hotnessSet_; + HotnessTimer hotnessTimer_; +}; + +} // namespace UC + +#endif \ No newline at end of file diff --git a/ucm/store/nfsstore/cc/domain/hotness/hotness_set.cc b/ucm/store/nfsstore/cc/domain/hotness/hotness_set.cc new file mode 100644 index 00000000..8bc739e9 --- /dev/null +++ b/ucm/store/nfsstore/cc/domain/hotness/hotness_set.cc @@ -0,0 +1,70 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#include "hotness_set.h" +#include "logger/logger.h" +#include "file/file.h" +#include "template/singleton.h" +namespace UC { + +void HotnessSet::Insert(const std::string& blockId) +{ + std::lock_guard lg(this->mutex_); + this->pendingBlocks_.insert(blockId); +} + +void HotnessSet::UpdateHotness(const SpaceLayout* spaceLayout) +{ + std::unordered_set blocksToUpdate; + { + std::lock_guard lg(this->mutex_); + if (this->pendingBlocks_.empty()) { + return; + } + blocksToUpdate.swap(this->pendingBlocks_); + } + + size_t number = 0; + for (const std::string& blockId : blocksToUpdate) { + auto blockPath = spaceLayout->DataFilePath(blockId, false); + auto file = File::Make(blockPath); + if (!file) { + UC_WARN("Failed to make file({}), blockId({}).", blockPath, blockId); + continue; + } + auto status = file->UpdateTime(); + if (status.Failure()) { + UC_WARN("Failed({}) to update time({}), blockId({}).", status, blockPath, blockId); + continue; + } + number++; + } + if (blocksToUpdate.size() == number) { + UC_INFO("All blocks are hotness."); + } else { + UC_WARN("{} of {} blocks are hotness.", blocksToUpdate.size() - number, blocksToUpdate.size()); + } +} + +} // namespace UC \ No newline at end of file diff --git a/ucm/store/nfsstore/cc/domain/hotness/hotness_set.h b/ucm/store/nfsstore/cc/domain/hotness/hotness_set.h new file mode 100644 index 00000000..4bb8beca --- /dev/null +++ b/ucm/store/nfsstore/cc/domain/hotness/hotness_set.h @@ -0,0 +1,47 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#ifndef UNIFIEDCACHE_HOTNESS_SET_H +#define UNIFIEDCACHE_HOTNESS_SET_H + +#include +#include +#include "space/space_layout.h" + +namespace UC { + +class HotnessSet { +public: + void Insert(const std::string& blockId); + void UpdateHotness(const SpaceLayout* spaceLayout); + +private: + std::mutex mutex_; + std::unordered_set pendingBlocks_; +}; + + +} // namespace UC + +#endif \ No newline at end of file diff --git a/ucm/store/nfsstore/cc/domain/hotness/hotness_timer.h b/ucm/store/nfsstore/cc/domain/hotness/hotness_timer.h new file mode 100644 index 00000000..add777ba --- /dev/null +++ b/ucm/store/nfsstore/cc/domain/hotness/hotness_timer.h @@ -0,0 +1,53 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#ifndef UNIFIEDCACHE_HOTNESS_TIMER_H +#define UNIFIEDCACHE_HOTNESS_TIMER_H +#include +#include +#include "template/timer.h" + +namespace UC { + +class HotnessTimer { +public: + void SetInterval(const size_t interval) { this->interval_ = std::chrono::seconds(interval); } + Status Start(std::function callable) + { + try { + this->timer_ = std::make_unique>>(this->interval_, std::move(callable)); + } catch (const std::exception& e) { + UC_ERROR("Failed({}) to start hotness timer.", e.what()); + return Status::OutOfMemory(); + } + return this->timer_->Start(); + } +private: + std::chrono::seconds interval_; + std::unique_ptr>> timer_; +}; + +} // namespace UC + +#endif \ No newline at end of file diff --git a/ucm/store/nfsstore/cpy/nfsstore.py.cc b/ucm/store/nfsstore/cpy/nfsstore.py.cc index fb3c340e..7f148f1b 100644 --- a/ucm/store/nfsstore/cpy/nfsstore.py.cc +++ b/ucm/store/nfsstore/cpy/nfsstore.py.cc @@ -123,6 +123,8 @@ PYBIND11_MODULE(ucmnfsstore, module) config.def_readwrite("transferBufferNumber", &UC::NFSStorePy::Config::transferBufferNumber); config.def_readwrite("transferTimeoutMs", &UC::NFSStorePy::Config::transferTimeoutMs); config.def_readwrite("tempDumpDirEnable", &UC::NFSStorePy::Config::tempDumpDirEnable); + config.def_readwrite("hotnessEnable", &UC::NFSStorePy::Config::hotnessEnable); + config.def_readwrite("hotnessInterval", &UC::NFSStorePy::Config::hotnessInterval); store.def(py::init<>()); store.def("CCStoreImpl", &UC::NFSStorePy::CCStoreImpl); store.def("Setup", &UC::NFSStorePy::Setup); diff --git a/ucm/store/test/case/nfsstore/hotness_test.cc b/ucm/store/test/case/nfsstore/hotness_test.cc new file mode 100644 index 00000000..21cc83cd --- /dev/null +++ b/ucm/store/test/case/nfsstore/hotness_test.cc @@ -0,0 +1,54 @@ +/** + * MIT License + * + * Copyright (c) 2025 Huawei Technologies Co., Ltd. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * */ + +#include +#include +#include "hotness/hotness_set.h" +#include "hotness/hotness_timer.h" +#include "cmn/path_base.h" +#include "file/file.h" +#include "space/space_manager.h" + +class UCHotnessTest : public UC::PathBase {}; + +TEST_F(UCHotnessTest, UpdateHotness) +{ + UC::SpaceManager mgr; + ASSERT_EQ(mgr.Setup({this->Path()}, 1024 * 1024, false), UC::Status::OK()); + + std::string block1 = "a1b2c3d4e5f6789012345678901234ab"; + ASSERT_EQ(mgr.NewBlock(block1), UC::Status::OK()); + ASSERT_EQ(mgr.CommitBlock(block1), UC::Status::OK()); + + UC::HotnessSet hotness_set; + hotness_set.Insert(block1); + auto space_layout = mgr.GetSpaceLayout(); + auto path = space_layout->DataFilePath(block1, false); + auto currentTime = std::filesystem::last_write_time(path); + std::filesystem::last_write_time(path, currentTime - std::chrono::seconds(2)); + auto lastTime = std::filesystem::last_write_time(path); + hotness_set.UpdateHotness(space_layout); + auto newTime = std::filesystem::last_write_time(path); + ASSERT_GT(newTime, lastTime); +} \ No newline at end of file