diff options
| author | Per Larsson <[email protected]> | 2021-12-12 12:04:31 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2021-12-12 12:04:31 +0100 |
| commit | a40133fe893100631f9bb8cd68fb7c2edbab0759 (patch) | |
| tree | 80cdcb8af8fad50d1004116671a655ec4495d729 /zenstore/gc.cpp | |
| parent | Added size to GcStorage. (diff) | |
| download | zen-a40133fe893100631f9bb8cd68fb7c2edbab0759.tar.xz zen-a40133fe893100631f9bb8cd68fb7c2edbab0759.zip | |
Added support for triggering GC with different params and refactored GC scheduler.
Diffstat (limited to 'zenstore/gc.cpp')
| -rw-r--r-- | zenstore/gc.cpp | 181 |
1 files changed, 127 insertions, 54 deletions
diff --git a/zenstore/gc.cpp b/zenstore/gc.cpp index 86f215b97..8f08c8b1f 100644 --- a/zenstore/gc.cpp +++ b/zenstore/gc.cpp @@ -6,22 +6,25 @@ #include <zencore/compactbinarybuilder.h> #include <zencore/compactbinaryvalidation.h> #include <zencore/filesystem.h> +#include <zencore/fmtutils.h> #include <zencore/logging.h> #include <zencore/string.h> #include <zencore/timer.h> #include <zenstore/CAS.h> #include <zenstore/cidstore.h> +#include <fmt/format.h> #include <filesystem> namespace zen { using namespace std::literals; +namespace fs = std::filesystem; ////////////////////////////////////////////////////////////////////////// CbObject -LoadCompactBinaryObject(const std::filesystem::path& Path) +LoadCompactBinaryObject(const fs::path& Path) { FileContents Result = ReadFile(Path); @@ -38,7 +41,7 @@ LoadCompactBinaryObject(const std::filesystem::path& Path) } void -SaveCompactBinaryObject(const std::filesystem::path& Path, const CbObject& Object) +SaveCompactBinaryObject(const fs::path& Path, const CbObject& Object) { WriteFile(Path, Object.GetBuffer().AsIoBuffer()); } @@ -269,6 +272,23 @@ CasGc::OnDroppedCidReferences(std::span<IoHash> Hashes) ZEN_UNUSED(Hashes); } +GcStorageSize +CasGc::TotalStorageSize() const +{ + RwLock::SharedLockScope _(m_Lock); + + GcStorageSize TotalSize; + + for (GcStorage* Storage : m_GcStorage) + { + const auto Size = Storage->StorageSize(); + TotalSize.DiskSize += Size.DiskSize; + TotalSize.MemorySize += Size.MemorySize; + } + + return TotalSize; +} + ////////////////////////////////////////////////////////////////////////// GcScheduler::GcScheduler(CasGc& CasGc) : m_Log(logging::Get("gc")), m_CasGc(CasGc) @@ -287,21 +307,27 @@ GcScheduler::Initialize(const GcSchedulerConfig& Config) m_Config = Config; + if (m_Config.Interval.count() && m_Config.Interval < m_Config.MonitorInterval) + { + m_Config.Interval = m_Config.MonitorInterval; + } + std::filesystem::create_directories(Config.RootDirectory); - GcClock::TimePoint LastGcTime = GcClock::Now(); + m_LastGcTime = GcClock::Now(); if (CbObject SchedulerState = LoadCompactBinaryObject(Config.RootDirectory / "gc_state")) { - LastGcTime = GcClock::TimePoint(GcClock::Duration(SchedulerState["LastGcTime"sv].AsInt64())); + m_LastGcTime = GcClock::TimePoint(GcClock::Duration(SchedulerState["LastGcTime"sv].AsInt64())); - if (LastGcTime + m_Config.Interval < GcClock::Now()) + if (m_LastGcTime + m_Config.Interval < GcClock::Now()) { - LastGcTime = GcClock::Now(); + // TODO: Trigger GC? + m_LastGcTime = GcClock::Now(); } } - m_NextGcTime = NextGcTime(LastGcTime); + m_NextGcTime = NextGcTime(m_LastGcTime); m_GcThread = std::jthread(&GcScheduler::SchedulerThread, this); } @@ -315,64 +341,129 @@ GcScheduler::Shutdown() } } +bool +GcScheduler::Trigger(const GcScheduler::TriggerParams& Params) +{ + if (m_Config.Enabled) + { + std::unique_lock Lock(m_GcMutex); + if (static_cast<uint32_t>(GcSchedulerStatus::kIdle) == m_Status) + { + m_TriggerParams = Params; + m_Status = static_cast<uint32_t>(GcSchedulerStatus::kRunning); + m_GcSignal.notify_one(); + return true; + } + } + + return false; +} + void GcScheduler::SchedulerThread() { + using namespace fmt::literals; + + std::chrono::seconds WaitTime = m_Config.MonitorInterval; + for (;;) { + bool Timeout = false; { + ZEN_ASSERT(WaitTime.count() >= 0); std::unique_lock Lock(m_GcMutex); - if (m_GcSignal.wait_until(Lock, m_NextGcTime, [this]() { return Status() != GcSchedulerStatus::kIdle; })) - { - if (Status() == GcSchedulerStatus::kStopped) - { - break; - } - } - else - { - m_Status.store(static_cast<uint32_t>(GcSchedulerStatus::kRunning)); - } + Timeout = std::cv_status::timeout == m_GcSignal.wait_for(Lock, WaitTime); } - if (!m_Config.Enabled) + if (Status() == GcSchedulerStatus::kStopped) + { + break; + } + + if (!m_Config.Enabled || (!Timeout && Status() == GcSchedulerStatus::kIdle)) { - ZEN_INFO("disabled"); continue; } - Stopwatch Timer; + if (Timeout && Status() == GcSchedulerStatus::kIdle) + { + std::error_code Ec; + DiskSpace Space = DiskSpaceInfo(m_Config.RootDirectory, Ec); + GcStorageSize TotalSize = m_CasGc.TotalStorageSize(); + std::chrono::seconds RemaingTime = std::chrono::duration_cast<std::chrono::seconds>(m_NextGcTime - GcClock::Now()); + + if (RemaingTime < std::chrono::seconds::zero()) + { + RemaingTime = std::chrono::seconds::zero(); + } + + if (Ec) + { + ZEN_WARN("get disk space info FAILED, reason '{}'", Ec.message()); + } + + ZEN_INFO("{} in use, {} of total {} free disk space, {}", + NiceBytes(TotalSize.DiskSize), + NiceBytes(Space.Free), + NiceBytes(Space.Total), + m_Config.Interval.count() + ? "{} until next GC"_format(NiceTimeSpanMs(uint64_t(std::chrono::milliseconds(RemaingTime).count()))) + : std::string("next scheduled GC no set")); + + // TODO: Trigger GC if max disk usage water mark is reached + + if (RemaingTime.count() > 0) + { + WaitTime = m_Config.MonitorInterval < RemaingTime ? m_Config.MonitorInterval : RemaingTime; + continue; + } + + WaitTime = m_Config.MonitorInterval; + m_Status = static_cast<uint32_t>(GcSchedulerStatus::kRunning); + } - DiskSpace Space; - DiskSpaceInfo(m_Config.RootDirectory, Space); - ZEN_INFO("garbage collection STARTING, disk space {}/{} (free/total)", NiceBytes(Space.Free), NiceBytes(Space.Total)); + ZEN_ASSERT(Status() == GcSchedulerStatus::kRunning); GcContext GcCtx; GcCtx.SetDeletionMode(true); GcCtx.CollectSmallObjects(m_Config.CollectSmallObjects); GcCtx.MaxCacheDuration(m_Config.MaxCacheDuration); - ZEN_DEBUG("collecting small objects {}, max cache duration {}s", - m_Config.CollectSmallObjects ? "YES"sv : "NO"sv, - m_Config.MaxCacheDuration.count()); + if (m_TriggerParams) + { + const auto TriggerParams = m_TriggerParams.value(); + m_TriggerParams.reset(); + + GcCtx.CollectSmallObjects(TriggerParams.CollectSmallObjects); + if (TriggerParams.MaxCacheDuration != std::chrono::seconds::max()) + { + GcCtx.MaxCacheDuration(TriggerParams.MaxCacheDuration); + } + } + + Stopwatch Timer; + + ZEN_INFO("garbage collection STARTING, small objects gc {}, max cache duration {}", + GcCtx.CollectSmallObjects() ? "ENABLED"sv : "DISABLED"sv, + NiceTimeSpanMs(uint64_t(std::chrono::duration_cast<std::chrono::milliseconds>(GcCtx.MaxCacheDuration()).count()))); m_CasGc.CollectGarbage(GcCtx); - m_Status = static_cast<uint32_t>(GcSchedulerStatus::kIdle); - m_NextGcTime = NextGcTime(GcClock::Now()); + m_LastGcTime = GcClock::Now(); + m_NextGcTime = NextGcTime(m_LastGcTime); + WaitTime = m_Config.MonitorInterval; { + const fs::path Path = m_Config.RootDirectory / "gc_state"; + ZEN_DEBUG("saving scheduler state to '{}'", Path); CbObjectWriter SchedulderState; - SchedulderState << "LastGcTime"sv << static_cast<int64_t>(GcClock::Now().time_since_epoch().count()); - SaveCompactBinaryObject(m_Config.RootDirectory / "gc_state", SchedulderState.Save()); + SchedulderState << "LastGcTime"sv << static_cast<int64_t>(m_LastGcTime.time_since_epoch().count()); + SaveCompactBinaryObject(Path, SchedulderState.Save()); } - DiskSpaceInfo(m_Config.RootDirectory, Space); + ZEN_INFO("garbage collection DONE after {}", NiceTimeSpanMs(Timer.GetElapsedTimeMs())); - ZEN_INFO("garbage collection DONE after {}, disk space {}/{} (free/total)", - NiceTimeSpanMs(Timer.GetElapsedTimeMs()), - NiceBytes(Space.Free), - NiceBytes(Space.Total)); + m_Status = static_cast<uint32_t>(GcSchedulerStatus::kIdle); } } @@ -389,24 +480,6 @@ GcScheduler::NextGcTime(GcClock::TimePoint CurrentTime) } } -bool -GcScheduler::ScheduleNow() -{ - if (m_Config.Enabled) - { - ZEN_DEBUG("schedule NOW"); - std::unique_lock Lock(m_GcMutex); - if (static_cast<uint32_t>(GcSchedulerStatus::kIdle) == m_Status) - { - m_Status = static_cast<uint32_t>(GcSchedulerStatus::kRunning); - m_GcSignal.notify_one(); - return true; - } - } - - return false; -} - ////////////////////////////////////////////////////////////////////////// } // namespace zen |