diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/admin_cmd.cpp | 17 | ||||
| -rw-r--r-- | src/zen/cmds/admin_cmd.h | 2 | ||||
| -rw-r--r-- | src/zenserver/admin/admin.cpp | 16 | ||||
| -rw-r--r-- | src/zenserver/cache/cachedisklayer.cpp | 29 | ||||
| -rw-r--r-- | src/zenserver/config.cpp | 19 | ||||
| -rw-r--r-- | src/zenserver/config.h | 2 | ||||
| -rw-r--r-- | src/zenserver/projectstore/projectstore.cpp | 18 | ||||
| -rw-r--r-- | src/zenserver/zenserver.cpp | 4 | ||||
| -rw-r--r-- | src/zenstore/compactcas.cpp | 20 | ||||
| -rw-r--r-- | src/zenstore/filecas.cpp | 2 | ||||
| -rw-r--r-- | src/zenstore/gc.cpp | 356 | ||||
| -rw-r--r-- | src/zenstore/include/zenstore/gc.h | 10 |
12 files changed, 332 insertions, 163 deletions
diff --git a/src/zen/cmds/admin_cmd.cpp b/src/zen/cmds/admin_cmd.cpp index b041aa46e..5786ae6c5 100644 --- a/src/zen/cmds/admin_cmd.cpp +++ b/src/zen/cmds/admin_cmd.cpp @@ -99,6 +99,15 @@ GcCommand::GcCommand() .add_option("", "", "usegcv1", "Force use of GC version 1", cxxopts::value(m_ForceUseGCV1)->default_value("false"), "<usegcv2>"); m_Options .add_option("", "", "usegcv2", "Force use of GC version 2", cxxopts::value(m_ForceUseGCV2)->default_value("false"), "<usegcv2>"); + m_Options.add_option("", + "", + "compactblockthreshold", + "How much of a compact block should be used to skip compacting the block. 0 - compact only empty eligible blocks, " + "100 - compact all non-full eligible blocks.", + cxxopts::value(m_CompactBlockThreshold)->default_value("60"), + "<compactblockthreshold>"); + m_Options + .add_option("", "", "verbose", "Enable verbose logging for GC", cxxopts::value(m_Verbose)->default_value("false"), "<verbose>"); } GcCommand::~GcCommand() @@ -155,6 +164,14 @@ GcCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) { Params.Add({"forceusegcv2", "true"}); } + if (m_CompactBlockThreshold) + { + Params.Add({"compactblockthreshold", fmt::format("{}", m_CompactBlockThreshold)}); + } + if (m_Verbose) + { + Params.Add({"verbose", "true"}); + } cpr::Session Session; Session.SetHeader(cpr::Header{{"Accept", "application/json"}}); diff --git a/src/zen/cmds/admin_cmd.h b/src/zen/cmds/admin_cmd.h index 1938ad1fa..084c2f654 100644 --- a/src/zen/cmds/admin_cmd.h +++ b/src/zen/cmds/admin_cmd.h @@ -45,6 +45,8 @@ private: uint64_t m_DiskSizeSoftLimit{0}; bool m_ForceUseGCV1{false}; bool m_ForceUseGCV2{false}; + uint32_t m_CompactBlockThreshold = 90; + bool m_Verbose{false}; }; class GcStatusCommand : public StorageCommand diff --git a/src/zenserver/admin/admin.cpp b/src/zenserver/admin/admin.cpp index 0b302c36e..f1d9f8d7c 100644 --- a/src/zenserver/admin/admin.cpp +++ b/src/zenserver/admin/admin.cpp @@ -219,6 +219,9 @@ HttpAdminService::HttpAdminService(GcScheduler& Scheduler, Response << "DiskSizeSoftLimit" << NiceBytes(State.Config.DiskSizeSoftLimit); Response << "MinimumFreeDiskSpaceToAllowWrites" << NiceBytes(State.Config.MinimumFreeDiskSpaceToAllowWrites); Response << "LightweightInterval" << ToTimeSpan(State.Config.LightweightInterval); + Response << "UseGCVersion" << ((State.Config.UseGCVersion == GcVersion::kV1) ? "1" : "2"); + Response << "CompactBlockUsageThresholdPercent" << State.Config.CompactBlockUsageThresholdPercent; + Response << "Verbose" << State.Config.Verbose; } Response.EndObject(); Response << "AreDiskWritesBlocked" << State.AreDiskWritesBlocked; @@ -326,6 +329,19 @@ HttpAdminService::HttpAdminService(GcScheduler& Scheduler, GcParams.ForceGCVersion = GcVersion::kV2; } + if (auto Param = Params.GetValue("compactblockthreshold"); Param.empty() == false) + { + if (auto Value = ParseInt<uint32_t>(Param)) + { + GcParams.CompactBlockUsageThresholdPercent = Value.value(); + } + } + + if (auto Param = Params.GetValue("verbose"); Param.empty() == false) + { + GcParams.Verbose = Param == "true"sv; + } + const bool Started = m_GcScheduler.TriggerGc(GcParams); CbObjectWriter Response; diff --git a/src/zenserver/cache/cachedisklayer.cpp b/src/zenserver/cache/cachedisklayer.cpp index 32ef420d1..2be32e372 100644 --- a/src/zenserver/cache/cachedisklayer.cpp +++ b/src/zenserver/cache/cachedisklayer.cpp @@ -2502,7 +2502,10 @@ public: if (Ctx.Settings.IsDeleteMode) { - ZEN_DEBUG("GCV2: cachebucket [COMPACT] '{}': compacting {} blocks", m_Bucket.m_BucketDir, BlocksToCompact.size()); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: cachebucket [COMPACT] '{}': compacting {} blocks", m_Bucket.m_BucketDir, BlocksToCompact.size()); + } m_Bucket.m_BlockStore.CompactBlocks( BlockCompactState, @@ -2539,9 +2542,12 @@ public: } else { - ZEN_DEBUG("GCV2: cachebucket [COMPACT] '{}': skipped compacting of {} eligible blocks", - m_Bucket.m_BucketDir, - BlocksToCompact.size()); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: cachebucket [COMPACT] '{}': skipped compacting of {} eligible blocks", + m_Bucket.m_BucketDir, + BlocksToCompact.size()); + } } } } @@ -2712,9 +2718,22 @@ public: } } - virtual void RemoveUsedReferencesFromSet(GcCtx&, HashSet& IoCids) override + virtual void RemoveUsedReferencesFromSet(GcCtx& Ctx, HashSet& IoCids) override { ZEN_ASSERT(m_IndexLock); + size_t InitialCount = IoCids.size(); + Stopwatch Timer; + const auto _ = MakeGuard([&] { + if (!Ctx.Settings.Verbose) + { + return; + } + ZEN_INFO("GCV2: cachebucket [FILTER REFERENCES] '{}': filtered out {} used references out of {} in {}", + m_CacheBucket.m_BucketDir, + InitialCount - IoCids.size(), + InitialCount, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + }); for (const IoHash& ReferenceHash : m_CacheBucket.m_ReferenceHashes) { diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp index e33c6ff70..3640abfaf 100644 --- a/src/zenserver/config.cpp +++ b/src/zenserver/config.cpp @@ -887,6 +887,10 @@ ParseConfigFile(const std::filesystem::path& Path, LuaOptions.AddOption("gc.lightweightntervalseconds"sv, ServerOptions.GcConfig.LightweightIntervalSeconds, "gc-lightweight-interval-seconds"sv); + LuaOptions.AddOption("gc.compactblockthreshold"sv, + ServerOptions.GcConfig.CompactBlockUsageThresholdPercent, + "gc-compactblock-threshold"sv); + LuaOptions.AddOption("gc.verbose"sv, ServerOptions.GcConfig.Verbose, "gc-verbose"sv); ////// gc LuaOptions.AddOption("gc.cache.maxdurationseconds"sv, ServerOptions.GcConfig.Cache.MaxDurationSeconds, "gc-cache-duration-seconds"sv); @@ -1317,6 +1321,21 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) cxxopts::value<uint64_t>(ServerOptions.GcConfig.DiskSizeSoftLimit)->default_value("0"), ""); + options.add_option("gc", + "", + "gc-compactblock-threshold", + "Garbage collection - how much of a compact block should be used to skip compacting the block. 0 - compact only " + "empty eligible blocks, 100 - compact all non-full eligible blocks.", + cxxopts::value<uint32_t>(ServerOptions.GcConfig.CompactBlockUsageThresholdPercent)->default_value("60"), + ""); + + options.add_option("gc", + "", + "gc-verbose", + "Enable verbose logging for GC.", + cxxopts::value<bool>(ServerOptions.GcConfig.Verbose)->default_value("false"), + ""); + options.add_option("objectstore", "", "objectstore-enabled", diff --git a/src/zenserver/config.h b/src/zenserver/config.h index 406fb0b70..c7f6e1e2a 100644 --- a/src/zenserver/config.h +++ b/src/zenserver/config.h @@ -72,6 +72,8 @@ struct ZenGcConfig int32_t LightweightIntervalSeconds = 0; uint64_t MinimumFreeDiskSpaceToAllowWrites = 1ul << 28; bool UseGCV2 = false; + uint32_t CompactBlockUsageThresholdPercent = 90; + bool Verbose = false; }; struct ZenOpenIdProviderConfig diff --git a/src/zenserver/projectstore/projectstore.cpp b/src/zenserver/projectstore/projectstore.cpp index 617104ddc..21aa6b44a 100644 --- a/src/zenserver/projectstore/projectstore.cpp +++ b/src/zenserver/projectstore/projectstore.cpp @@ -3341,8 +3341,24 @@ public: } } - virtual void RemoveUsedReferencesFromSet(GcCtx&, HashSet& IoCids) override + virtual void RemoveUsedReferencesFromSet(GcCtx& Ctx, HashSet& IoCids) override { + ZEN_ASSERT(m_OplogLock); + + size_t InitialCount = IoCids.size(); + Stopwatch Timer; + const auto _ = MakeGuard([&] { + if (!Ctx.Settings.Verbose) + { + return; + } + ZEN_INFO("GCV2: projectstore [FILTER REFERENCES] '{}': filtered out {} used references out of {} in {}", + m_Oplog.m_BasePath, + InitialCount - IoCids.size(), + InitialCount, + NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + }); + for (const IoHash& ReferenceHash : m_References) { IoCids.erase(ReferenceHash); diff --git a/src/zenserver/zenserver.cpp b/src/zenserver/zenserver.cpp index e6724e40a..a50ff1b53 100644 --- a/src/zenserver/zenserver.cpp +++ b/src/zenserver/zenserver.cpp @@ -288,7 +288,9 @@ ZenServer::Initialize(const ZenServerOptions& ServerOptions, ZenServerState::Zen .DiskSizeSoftLimit = ServerOptions.GcConfig.DiskSizeSoftLimit, .MinimumFreeDiskSpaceToAllowWrites = ServerOptions.GcConfig.MinimumFreeDiskSpaceToAllowWrites, .LightweightInterval = std::chrono::seconds(ServerOptions.GcConfig.LightweightIntervalSeconds), - .UseGCVersion = ServerOptions.GcConfig.UseGCV2 ? GcVersion::kV2 : GcVersion::kV1}; + .UseGCVersion = ServerOptions.GcConfig.UseGCV2 ? GcVersion::kV2 : GcVersion::kV1, + .CompactBlockUsageThresholdPercent = ServerOptions.GcConfig.CompactBlockUsageThresholdPercent, + .Verbose = ServerOptions.GcConfig.Verbose}; m_GcScheduler.Initialize(GcConfig); // Create and register admin interface last to make sure all is properly initialized diff --git a/src/zenstore/compactcas.cpp b/src/zenstore/compactcas.cpp index 7b8e930b3..1d608be8d 100644 --- a/src/zenstore/compactcas.cpp +++ b/src/zenstore/compactcas.cpp @@ -621,9 +621,12 @@ public: if (Ctx.Settings.IsDeleteMode) { - ZEN_DEBUG("GCV2: compactcas [COMPACT] '{}': compacting {} blocks", - m_CasContainerStrategy.m_RootDirectory / m_CasContainerStrategy.m_ContainerBaseName, - BlocksToCompact.size()); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: compactcas [COMPACT] '{}': compacting {} blocks", + m_CasContainerStrategy.m_RootDirectory / m_CasContainerStrategy.m_ContainerBaseName, + BlocksToCompact.size()); + } m_CasContainerStrategy.m_BlockStore.CompactBlocks( BlockCompactState, @@ -660,9 +663,12 @@ public: } else { - ZEN_DEBUG("GCV2: compactcas [COMPACT] '{}': skipped compacting of {} eligible blocks", - m_CasContainerStrategy.m_RootDirectory / m_CasContainerStrategy.m_ContainerBaseName, - BlocksToCompact.size()); + if (Ctx.Settings.Verbose) + { + ZEN_INFO("GCV2: compactcas [COMPACT] '{}': skipped compacting of {} eligible blocks", + m_CasContainerStrategy.m_RootDirectory / m_CasContainerStrategy.m_ContainerBaseName, + BlocksToCompact.size()); + } } } } @@ -763,7 +769,7 @@ CasContainerStrategy::CreateReferencePruner(GcCtx& Ctx, GcReferenceStoreStats&) { return; } - ZEN_INFO("GCV2: compactcas [CREATE PRUNERS] '{}' in {}", + ZEN_INFO("GCV2: compactcas [CREATE PRUNER] '{}' in {}", m_RootDirectory / m_ContainerBaseName, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); diff --git a/src/zenstore/filecas.cpp b/src/zenstore/filecas.cpp index 6e432bc9d..2623e157d 100644 --- a/src/zenstore/filecas.cpp +++ b/src/zenstore/filecas.cpp @@ -1512,7 +1512,7 @@ FileCasStrategy::CreateReferencePruner(GcCtx& Ctx, GcReferenceStoreStats&) { return; } - ZEN_INFO("GCV2: filecas [CREATE PRUNERS] '{}' in {}", m_RootDirectory, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + ZEN_INFO("GCV2: filecas [CREATE PRUNER] '{}' in {}", m_RootDirectory, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); std::vector<IoHash> CidsToCheck; { diff --git a/src/zenstore/gc.cpp b/src/zenstore/gc.cpp index b78b23350..64958cdea 100644 --- a/src/zenstore/gc.cpp +++ b/src/zenstore/gc.cpp @@ -620,27 +620,34 @@ GcManager::CollectGarbage(const GcSettings& Settings) if (!m_GcReferencers.empty()) { Latch WorkLeft(1); - // First remove any cache keys that may own references - SCOPED_TIMER(Result.RemoveExpiredDataMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - for (size_t Index = 0; Index < m_GcReferencers.size(); Index++) { - GcReferencer* Owner = m_GcReferencers[Index]; - std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; - WorkLeft.AddCount(1); - ThreadPool.ScheduleWork([&Ctx, &WorkLeft, Owner, &Stats, &StoreCompactorsLock, &StoreCompactors]() { - auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); - Stats.first = Owner->GetGcName(Ctx); - SCOPED_TIMER(Stats.second.RemoveExpiredDataStats.ElapsedMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - std::unique_ptr<GcStoreCompactor> StoreCompactor(Owner->RemoveExpiredData(Ctx, Stats.second.RemoveExpiredDataStats)); - if (StoreCompactor) - { - RwLock::ExclusiveLockScope __(StoreCompactorsLock); - StoreCompactors.insert_or_assign(std::move(StoreCompactor), &Stats.second.CompactStoreStats); - } + // First remove any cache keys that may own references + SCOPED_TIMER(Result.RemoveExpiredDataMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Removed epxired data for {} referenceners in {}", + m_GcReferencers.size(), + NiceTimeSpanMs(Result.RemoveExpiredDataMS.count())); }); + for (size_t Index = 0; Index < m_GcReferencers.size(); Index++) + { + GcReferencer* Owner = m_GcReferencers[Index]; + std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; + WorkLeft.AddCount(1); + ThreadPool.ScheduleWork([&Ctx, &WorkLeft, Owner, &Stats, &StoreCompactorsLock, &StoreCompactors]() { + auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); + Stats.first = Owner->GetGcName(Ctx); + SCOPED_TIMER(Stats.second.RemoveExpiredDataStats.ElapsedMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + std::unique_ptr<GcStoreCompactor> StoreCompactor( + Owner->RemoveExpiredData(Ctx, Stats.second.RemoveExpiredDataStats)); + if (StoreCompactor) + { + RwLock::ExclusiveLockScope __(StoreCompactorsLock); + StoreCompactors.insert_or_assign(std::move(StoreCompactor), &Stats.second.CompactStoreStats); + } + }); + } + WorkLeft.CountDown(); + WorkLeft.Wait(); } - WorkLeft.CountDown(); - WorkLeft.Wait(); } if (!Ctx.Settings.SkipCidDelete) @@ -654,31 +661,42 @@ GcManager::CollectGarbage(const GcSettings& Settings) ReferencePruners.reserve(m_GcReferenceStores.size()); Latch WorkLeft(1); RwLock ReferencePrunersLock; - // CreateReferencePruner is usually not very heavy but big data sets change that - SCOPED_TIMER(Result.CreateReferencePrunersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - for (size_t Index = 0; Index < m_GcReferenceStores.size(); Index++) { - GcReferenceStore* ReferenceStore = m_GcReferenceStores[Index]; - std::pair<std::string, GcReferenceStoreStats>& Stats = Result.ReferenceStoreStats[Index]; - WorkLeft.AddCount(1); - ThreadPool.ScheduleWork([&Ctx, ReferenceStore, &Stats, Index, &WorkLeft, &ReferencePrunersLock, &ReferencePruners]() { - auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); - Stats.first = ReferenceStore->GetGcName(Ctx); - std::unique_ptr<GcReferencePruner> ReferencePruner; - { - SCOPED_TIMER(Stats.second.CreateReferencePrunersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - // The ReferenceStore will pick a list of CId entries to check, returning a collector - ReferencePruner = std::unique_ptr<GcReferencePruner>(ReferenceStore->CreateReferencePruner(Ctx, Stats.second)); - } - if (ReferencePruner) - { - RwLock::ExclusiveLockScope __(ReferencePrunersLock); - ReferencePruners.insert_or_assign(Index, std::move(ReferencePruner)); - } - }); + // CreateReferencePruner is usually not very heavy but big data sets change that + SCOPED_TIMER(Result.CreateReferencePrunersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); + if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Created {} reference pruners using {} referencer stores in {}", + ReferencePruners.size(), + m_GcReferenceStores.size(), + NiceTimeSpanMs(Result.CreateReferencePrunersMS.count())); + }); + for (size_t Index = 0; Index < m_GcReferenceStores.size(); Index++) + { + GcReferenceStore* ReferenceStore = m_GcReferenceStores[Index]; + std::pair<std::string, GcReferenceStoreStats>& Stats = Result.ReferenceStoreStats[Index]; + WorkLeft.AddCount(1); + ThreadPool.ScheduleWork( + [&Ctx, ReferenceStore, &Stats, Index, &WorkLeft, &ReferencePrunersLock, &ReferencePruners]() { + auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); + Stats.first = ReferenceStore->GetGcName(Ctx); + std::unique_ptr<GcReferencePruner> ReferencePruner; + { + SCOPED_TIMER(Stats.second.CreateReferencePrunersMS = + std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + // The ReferenceStore will pick a list of CId entries to check, returning a collector + ReferencePruner = + std::unique_ptr<GcReferencePruner>(ReferenceStore->CreateReferencePruner(Ctx, Stats.second)); + } + if (ReferencePruner) + { + RwLock::ExclusiveLockScope __(ReferencePrunersLock); + ReferencePruners.insert_or_assign(Index, std::move(ReferencePruner)); + } + }); + } + WorkLeft.CountDown(); + WorkLeft.Wait(); } - WorkLeft.CountDown(); - WorkLeft.Wait(); } if (!ReferencePruners.empty()) @@ -690,47 +708,57 @@ GcManager::CollectGarbage(const GcSettings& Settings) ReferenceCheckers.reserve(m_GcReferencers.size()); Latch WorkLeft(1); RwLock ReferenceCheckersLock; - SCOPED_TIMER(Result.CreateReferenceCheckersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - // Lock all reference owners from changing the reference data and get access to check for referenced data - for (size_t Index = 0; Index < m_GcReferencers.size(); Index++) { - GcReferencer* Referencer = m_GcReferencers[Index]; - std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; - WorkLeft.AddCount(1); - ThreadPool.ScheduleWork([&Ctx, &WorkLeft, Referencer, Index, &Stats, &ReferenceCheckersLock, &ReferenceCheckers]() { - auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); - // The Referencer will create a reference checker that guarrantees that the references do not change as long as - // it lives - std::vector<GcReferenceChecker*> Checkers; - { - SCOPED_TIMER(Stats.second.CreateReferenceCheckersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - Checkers = Referencer->CreateReferenceCheckers(Ctx); - } - try - { - if (!Checkers.empty()) - { - RwLock::ExclusiveLockScope __(ReferenceCheckersLock); - for (auto& Checker : Checkers) + SCOPED_TIMER(Result.CreateReferenceCheckersMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); + if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Created {} reference checkers using {} referencers in {}", + ReferenceCheckers.size(), + m_GcReferencers.size(), + NiceTimeSpanMs(Result.CreateReferenceCheckersMS.count())); + }); + // Lock all reference owners from changing the reference data and get access to check for referenced data + for (size_t Index = 0; Index < m_GcReferencers.size(); Index++) + { + GcReferencer* Referencer = m_GcReferencers[Index]; + std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; + WorkLeft.AddCount(1); + ThreadPool.ScheduleWork( + [&Ctx, &WorkLeft, Referencer, Index, &Stats, &ReferenceCheckersLock, &ReferenceCheckers]() { + auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); + // The Referencer will create a reference checker that guarrantees that the references do not change as + // long as it lives + std::vector<GcReferenceChecker*> Checkers; + { + SCOPED_TIMER(Stats.second.CreateReferenceCheckersMS = + std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + Checkers = Referencer->CreateReferenceCheckers(Ctx); + } + try { - ReferenceCheckers.insert_or_assign(std::unique_ptr<GcReferenceChecker>(Checker), Index); - Checker = nullptr; + if (!Checkers.empty()) + { + RwLock::ExclusiveLockScope __(ReferenceCheckersLock); + for (auto& Checker : Checkers) + { + ReferenceCheckers.insert_or_assign(std::unique_ptr<GcReferenceChecker>(Checker), Index); + Checker = nullptr; + } + } } - } - } - catch (std::exception&) - { - while (!Checkers.empty()) - { - delete Checkers.back(); - Checkers.pop_back(); - } - throw; - } - }); + catch (std::exception&) + { + while (!Checkers.empty()) + { + delete Checkers.back(); + Checkers.pop_back(); + } + throw; + } + }); + } + WorkLeft.CountDown(); + WorkLeft.Wait(); } - WorkLeft.CountDown(); - WorkLeft.Wait(); } ZEN_INFO("GCV2: Locking state for {} reference checkers", ReferenceCheckers.size()); @@ -744,21 +772,28 @@ GcManager::CollectGarbage(const GcSettings& Settings) // we delete the ReferenceCheckers Latch WorkLeft(1); - SCOPED_TIMER(Result.LockStateMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - for (auto& It : ReferenceCheckers) { - GcReferenceChecker* Checker = It.first.get(); - size_t Index = It.second; - std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; - WorkLeft.AddCount(1); - ThreadPool.ScheduleWork([&Ctx, Checker, Index, &Stats, &WorkLeft]() { - auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); - SCOPED_TIMER(Stats.second.LockStateMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - Checker->LockState(Ctx); - }); + SCOPED_TIMER(Result.LockStateMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); + if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Locked state using {} reference checkers in {}", + ReferenceCheckers.size(), + NiceTimeSpanMs(Result.LockStateMS.count())); + }); + for (auto& It : ReferenceCheckers) + { + GcReferenceChecker* Checker = It.first.get(); + size_t Index = It.second; + std::pair<std::string, GcReferencerStats>& Stats = Result.ReferencerStats[Index]; + WorkLeft.AddCount(1); + ThreadPool.ScheduleWork([&Ctx, Checker, Index, &Stats, &WorkLeft]() { + auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); + SCOPED_TIMER(Stats.second.LockStateMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + Checker->LockState(Ctx); + }); + } + WorkLeft.CountDown(); + WorkLeft.Wait(); } - WorkLeft.CountDown(); - WorkLeft.Wait(); } ZEN_INFO("GCV2: Removing unreferenced data for {} reference pruners", ReferencePruners.size()); @@ -783,34 +818,43 @@ GcManager::CollectGarbage(const GcSettings& Settings) Latch WorkLeft(1); - SCOPED_TIMER(Result.RemoveUnreferencedDataMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - for (auto& It : ReferencePruners) { - GcReferencePruner* Pruner = It.second.get(); - size_t Index = It.first; - GcReferenceStoreStats& Stats = Result.ReferenceStoreStats[Index].second; - WorkLeft.AddCount(1); - ThreadPool.ScheduleWork( - [&Ctx, Pruner, &Stats, &WorkLeft, &GetUnusedReferences, &StoreCompactorsLock, &StoreCompactors]() { - auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); - // Go through all the ReferenceCheckers to see if the list of Cids the collector selected are referenced - // or not. - std::unique_ptr<GcStoreCompactor> StoreCompactor; - { - SCOPED_TIMER(Stats.RemoveUnreferencedDataStats.ElapsedMS = - std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - StoreCompactor = std::unique_ptr<GcStoreCompactor>( - Pruner->RemoveUnreferencedData(Ctx, Stats.RemoveUnreferencedDataStats, GetUnusedReferences)); - } - if (StoreCompactor) - { - RwLock::ExclusiveLockScope __(StoreCompactorsLock); - StoreCompactors.insert_or_assign(std::move(StoreCompactor), &Stats.CompactStoreStats); - } - }); + SCOPED_TIMER(Result.RemoveUnreferencedDataMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); + if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Removed unused data using {} pruners in {}", + ReferencePruners.size(), + NiceTimeSpanMs(Result.RemoveUnreferencedDataMS.count())); + }); + for (auto& It : ReferencePruners) + { + GcReferencePruner* Pruner = It.second.get(); + size_t Index = It.first; + GcReferenceStoreStats& Stats = Result.ReferenceStoreStats[Index].second; + WorkLeft.AddCount(1); + ThreadPool.ScheduleWork( + [&Ctx, Pruner, &Stats, &WorkLeft, &GetUnusedReferences, &StoreCompactorsLock, &StoreCompactors]() { + auto _ = MakeGuard([&WorkLeft]() { WorkLeft.CountDown(); }); + // Go through all the ReferenceCheckers to see if the list of Cids the collector selected are + // referenced or not. + std::unique_ptr<GcStoreCompactor> StoreCompactor; + { + SCOPED_TIMER(Stats.RemoveUnreferencedDataStats.ElapsedMS = + std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + StoreCompactor = std::unique_ptr<GcStoreCompactor>( + Pruner->RemoveUnreferencedData(Ctx, + Stats.RemoveUnreferencedDataStats, + GetUnusedReferences)); + } + if (StoreCompactor) + { + RwLock::ExclusiveLockScope __(StoreCompactorsLock); + StoreCompactors.insert_or_assign(std::move(StoreCompactor), &Stats.CompactStoreStats); + } + }); + } + WorkLeft.CountDown(); + WorkLeft.Wait(); } - WorkLeft.CountDown(); - WorkLeft.Wait(); } // Let the GcReferencers add new data, we will only change on-disk data at this point, adding new data is allowed ReferenceCheckers.clear(); @@ -819,7 +863,7 @@ GcManager::CollectGarbage(const GcSettings& Settings) } } - ZEN_INFO("GCV2: Compacting reference stores for {} store compactors", StoreCompactors.size()); + ZEN_INFO("GCV2: Compacting using {} store compactors", StoreCompactors.size()); if (!StoreCompactors.empty()) { auto ClaimDiskReserve = [&]() -> uint64_t { @@ -836,15 +880,19 @@ GcManager::CollectGarbage(const GcSettings& Settings) }; // Remove the stuff we deemed unreferenced from disk - may be heavy operation // Don't do in parallel, we don't want to steal CPU/Disk from regular operation - SCOPED_TIMER(Result.CompactStoresMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - for (auto& It : StoreCompactors) { - GcStoreCompactor* Compactor = It.first.get(); - GcCompactStoreStats& Stats = *It.second; + SCOPED_TIMER(Result.CompactStoresMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs()); if (Ctx.Settings.Verbose) { + ZEN_INFO("GCV2: Compacted {} stores in {}", StoreCompactors.size(), NiceTimeSpanMs(Result.CompactStoresMS.count())); + }); + for (auto& It : StoreCompactors) { - // Go through all the ReferenceCheckers to see if the list of Cids the collector selected are referenced or not. - SCOPED_TIMER(Stats.ElapsedMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); - Compactor->CompactStore(Ctx, Stats, ClaimDiskReserve); + GcStoreCompactor* Compactor = It.first.get(); + GcCompactStoreStats& Stats = *It.second; + { + // Go through all the ReferenceCheckers to see if the list of Cids the collector selected are referenced or not. + SCOPED_TIMER(Stats.ElapsedMS = std::chrono::milliseconds(Timer.GetElapsedTimeMs());); + Compactor->CompactStore(Ctx, Stats, ClaimDiskReserve); + } } } StoreCompactors.clear(); @@ -1445,18 +1493,20 @@ GcScheduler::SchedulerThread() try { - bool DoGc = m_Config.Enabled; - bool DoScrubbing = false; - std::chrono::seconds ScrubTimeslice = std::chrono::seconds::max(); - bool DoDelete = true; - bool CollectSmallObjects = m_Config.CollectSmallObjects; - std::chrono::seconds GcInterval = m_Config.Interval; - std::chrono::seconds LightweightGcInterval = m_Config.LightweightInterval; - std::chrono::seconds MaxCacheDuration = m_Config.MaxCacheDuration; - std::chrono::seconds MaxProjectStoreDuration = m_Config.MaxProjectStoreDuration; - uint64_t DiskSizeSoftLimit = m_Config.DiskSizeSoftLimit; - bool SkipCid = false; - GcVersion UseGCVersion = m_Config.UseGCVersion; + bool DoGc = m_Config.Enabled; + bool DoScrubbing = false; + std::chrono::seconds ScrubTimeslice = std::chrono::seconds::max(); + bool DoDelete = true; + bool CollectSmallObjects = m_Config.CollectSmallObjects; + std::chrono::seconds GcInterval = m_Config.Interval; + std::chrono::seconds LightweightGcInterval = m_Config.LightweightInterval; + std::chrono::seconds MaxCacheDuration = m_Config.MaxCacheDuration; + std::chrono::seconds MaxProjectStoreDuration = m_Config.MaxProjectStoreDuration; + uint64_t DiskSizeSoftLimit = m_Config.DiskSizeSoftLimit; + bool SkipCid = false; + GcVersion UseGCVersion = m_Config.UseGCVersion; + uint32_t CompactBlockUsageThresholdPercent = m_Config.CompactBlockUsageThresholdPercent; + bool Verbose = m_Config.Verbose; bool DiskSpaceGCTriggered = false; bool TimeBasedGCTriggered = false; @@ -1491,7 +1541,10 @@ GcScheduler::SchedulerThread() DoDelete = false; } UseGCVersion = TriggerParams.ForceGCVersion.value_or(UseGCVersion); - DoGc = true; + CompactBlockUsageThresholdPercent = + TriggerParams.CompactBlockUsageThresholdPercent.value_or(CompactBlockUsageThresholdPercent); + Verbose = TriggerParams.Verbose.value_or(Verbose); + DoGc = true; } if (m_TriggerScrubParams) @@ -1696,7 +1749,14 @@ GcScheduler::SchedulerThread() } } - CollectGarbage(CacheExpireTime, ProjectStoreExpireTime, DoDelete, CollectSmallObjects, SkipCid, UseGCVersion); + CollectGarbage(CacheExpireTime, + ProjectStoreExpireTime, + DoDelete, + CollectSmallObjects, + SkipCid, + UseGCVersion, + CompactBlockUsageThresholdPercent, + Verbose); uint32_t RunningState = static_cast<uint32_t>(GcSchedulerStatus::kRunning); if (!m_Status.compare_exchange_strong(RunningState, static_cast<uint32_t>(GcSchedulerStatus::kIdle))) @@ -1778,7 +1838,9 @@ GcScheduler::CollectGarbage(const GcClock::TimePoint& CacheExpireTime, bool Delete, bool CollectSmallObjects, bool SkipCid, - GcVersion UseGCVersion) + GcVersion UseGCVersion, + uint32_t CompactBlockUsageThresholdPercent, + bool Verbose) { ZEN_TRACE_CPU("GcScheduler::CollectGarbage"); @@ -1841,12 +1903,14 @@ GcScheduler::CollectGarbage(const GcClock::TimePoint& CacheExpireTime, break; case GcVersion::kV2: { - const GcSettings Settings = {.CacheExpireTime = CacheExpireTime, - .ProjectStoreExpireTime = ProjectStoreExpireTime, - .CollectSmallObjects = CollectSmallObjects, - .IsDeleteMode = Delete, - .SkipCidDelete = SkipCid, - .DiskReservePath = m_Config.RootDirectory / "reserve.gc"}; + const GcSettings Settings = {.CacheExpireTime = CacheExpireTime, + .ProjectStoreExpireTime = ProjectStoreExpireTime, + .CollectSmallObjects = CollectSmallObjects, + .IsDeleteMode = Delete, + .SkipCidDelete = SkipCid, + .Verbose = Verbose, + .CompactBlockUsageThresholdPercent = CompactBlockUsageThresholdPercent, + .DiskReservePath = m_Config.RootDirectory / "reserve.gc"}; GcClock::TimePoint GcStartTime = GcClock::Now(); GcResult Result = m_GcManager.CollectGarbage(Settings); diff --git a/src/zenstore/include/zenstore/gc.h b/src/zenstore/include/zenstore/gc.h index 4cd01bc2c..f50af8006 100644 --- a/src/zenstore/include/zenstore/gc.h +++ b/src/zenstore/include/zenstore/gc.h @@ -397,7 +397,9 @@ struct GcSchedulerConfig uint64_t DiskSizeSoftLimit = 0; uint64_t MinimumFreeDiskSpaceToAllowWrites = 1ul << 28; std::chrono::seconds LightweightInterval{}; - GcVersion UseGCVersion = GcVersion::kV1; + GcVersion UseGCVersion = GcVersion::kV1; + uint32_t CompactBlockUsageThresholdPercent = 90; + bool Verbose = false; }; struct GcSchedulerState @@ -469,6 +471,8 @@ public: bool SkipCid = false; bool SkipDelete = false; std::optional<GcVersion> ForceGCVersion; + std::optional<uint32_t> CompactBlockUsageThresholdPercent; + std::optional<bool> Verbose; }; bool TriggerGc(const TriggerGcParams& Params); @@ -488,7 +492,9 @@ private: bool Delete, bool CollectSmallObjects, bool SkipCid, - GcVersion UseGCVersion); + GcVersion UseGCVersion, + uint32_t CompactBlockUsageThresholdPercent, + bool Verbose); void ScrubStorage(bool DoDelete, std::chrono::seconds TimeSlice); LoggerRef Log() { return m_Log; } virtual bool AreDiskWritesAllowed() const override { return !m_AreDiskWritesBlocked.load(); } |