diff options
| author | Dan Engelbrecht <[email protected]> | 2024-09-03 20:33:34 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-09-03 20:33:34 +0200 |
| commit | 9a48f6b32f525bbaf8053a4873f663b3e5f820a7 (patch) | |
| tree | 63638ec7a38331846715fdb2efe6ef61958f48fb /src | |
| parent | oplog index snapshots (#140) (diff) | |
| download | zen-9a48f6b32f525bbaf8053a4873f663b3e5f820a7.tar.xz zen-9a48f6b32f525bbaf8053a4873f663b3e5f820a7.zip | |
delay oplog read (#141)
- Improvement: Don't keep all oplogs open after GC, close them when references are fetched unless they are open by client
Diffstat (limited to 'src')
| -rw-r--r-- | src/zenserver/projectstore/projectstore.cpp | 336 | ||||
| -rw-r--r-- | src/zenserver/projectstore/projectstore.h | 22 |
2 files changed, 213 insertions, 145 deletions
diff --git a/src/zenserver/projectstore/projectstore.cpp b/src/zenserver/projectstore/projectstore.cpp index 1952adcd3..c1905228c 100644 --- a/src/zenserver/projectstore/projectstore.cpp +++ b/src/zenserver/projectstore/projectstore.cpp @@ -1143,33 +1143,15 @@ ProjectStore::Oplog::Read() ZEN_TRACE_CPU("Oplog::Read"); ZEN_LOG_SCOPE("Oplog::Read '{}'", m_OplogId); - std::filesystem::path StateFilePath = m_BasePath / "oplog.zcb"sv; - if (std::filesystem::is_regular_file(StateFilePath)) + std::optional<CbObject> Config = ReadStateFile(m_BasePath, [this]() { return Log(); }); + if (Config.has_value()) { - ZEN_INFO("oplog '{}/{}': config read from '{}'", m_OuterProject->Identifier, m_OplogId, StateFilePath); - - BasicFile Blob; - Blob.Open(StateFilePath, BasicFile::Mode::kRead); - - IoBuffer Obj = Blob.ReadAll(); - CbValidateError ValidationError = ValidateCompactBinary(MemoryView(Obj.Data(), Obj.Size()), CbValidateMode::All); - - if (ValidationError != CbValidateError::None) + if (Config.value().GetSize() == 0) { - ZEN_ERROR("validation error {} hit for '{}'", int(ValidationError), StateFilePath); + // Invalid config file return; } - - CbObject Cfg = LoadCompactBinaryObject(Obj); - - m_MarkerPath = Cfg["gcpath"sv].AsU8String(); - } - else - { - ZEN_INFO("oplog '{}/{}': config read not found at '{}', assuming legacy store", - m_OuterProject->Identifier, - m_OplogId, - StateFilePath); + m_MarkerPath = Config.value()["gcpath"sv].AsU8String(); } if (!m_MetaValid) @@ -1283,6 +1265,35 @@ ProjectStore::Oplog::Reset() return true; } +std::optional<CbObject> +ProjectStore::Oplog::ReadStateFile(const std::filesystem::path& BasePath, std::function<LoggerRef()>&& Log) +{ + ZEN_TRACE_CPU("Oplog::ReadStateFile"); + using namespace std::literals; + + std::filesystem::path StateFilePath = BasePath / "oplog.zcb"sv; + if (std::filesystem::is_regular_file(StateFilePath)) + { + // ZEN_INFO("oplog '{}/{}': config read from '{}'", m_OuterProject->Identifier, m_OplogId, StateFilePath); + + BasicFile Blob; + Blob.Open(StateFilePath, BasicFile::Mode::kRead); + + IoBuffer Obj = Blob.ReadAll(); + CbValidateError ValidationError = ValidateCompactBinary(MemoryView(Obj.Data(), Obj.Size()), CbValidateMode::All); + + if (ValidationError != CbValidateError::None) + { + ZEN_ERROR("validation error {} hit for oplog config at '{}'", int(ValidationError), StateFilePath); + return CbObject(); + } + + return LoadCompactBinaryObject(Obj); + } + ZEN_INFO("config for oplog not found at '{}'. Assuming legacy store", StateFilePath); + return {}; +} + void ProjectStore::Oplog::WriteIndexSnapshot() { @@ -3052,7 +3063,7 @@ ProjectStore::Project::ScrubStorage(ScrubContext& Ctx) void ProjectStore::Project::GatherReferences(GcContext& GcCtx) { - ZEN_TRACE_CPU("Store::Project::GatherReferences"); + ZEN_TRACE_CPU("Project::GatherReferences"); Stopwatch Timer; const auto Guard = MakeGuard([&] { @@ -3263,6 +3274,28 @@ ProjectStore::Project::IsOplogTouchedSince(const GcClock::TimePoint TouchTime, s return false; } +bool +ProjectStore::Project::IsExpired(const GcClock::TimePoint ExpireTime, std::string_view OplogId) const +{ + using namespace std::literals; + + { + RwLock::SharedLockScope Lock(m_ProjectLock); + auto OplogIt = m_Oplogs.find(std::string(OplogId)); + + if (OplogIt != m_Oplogs.end()) + { + Lock.ReleaseNow(); + return IsExpired(ExpireTime, *OplogIt->second.get()); + } + } + + std::filesystem::path OplogBasePath = BasePathForOplog(OplogId); + std::optional<CbObject> OplogConfig = Oplog::ReadStateFile(OplogBasePath, [this]() { return Log(); }); + std::filesystem::path MarkerPath = OplogConfig.has_value() ? OplogConfig.value()["gcpath"sv].AsU8String() : std::u8string(); + return IsExpired(std::string(OplogId), MarkerPath, ExpireTime); +} + void ProjectStore::Project::TouchProject() { @@ -4955,16 +4988,14 @@ ProjectStore::GetGcName(GcCtx&) class ProjectStoreGcStoreCompactor : public GcStoreCompactor { public: - ProjectStoreGcStoreCompactor(ProjectStore& ProjectStore, - const std::filesystem::path& BasePath, - std::vector<std::filesystem::path>&& OplogPathsToRemove, - std::vector<std::filesystem::path>&& ProjectPathsToRemove, - std::vector<std::pair<std::string, std::string>>&& OplogsToCompact) + ProjectStoreGcStoreCompactor(ProjectStore& ProjectStore, + const std::filesystem::path& BasePath, + std::vector<std::filesystem::path>&& OplogPathsToRemove, + std::vector<std::filesystem::path>&& ProjectPathsToRemove) : m_ProjectStore(ProjectStore) , m_BasePath(BasePath) , m_OplogPathsToRemove(std::move(OplogPathsToRemove)) , m_ProjectPathsToRemove(std::move(ProjectPathsToRemove)) - , m_OplogsToCompact(std::move(OplogsToCompact)) { } @@ -4984,6 +5015,7 @@ public: NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); + size_t CompactOplogCount = 0; if (Ctx.Settings.IsDeleteMode) { for (const std::filesystem::path& OplogPath : m_OplogPathsToRemove) @@ -5020,23 +5052,29 @@ public: } } } - else - { - ZEN_DEBUG("GCV2: projectstore [COMPACT] '{}': Skipped deleting and compacting of {} oplogs and {} projects", - m_BasePath, - m_OplogPathsToRemove.size(), - m_ProjectPathsToRemove.size()); - } + for (auto ProjectIt : m_ProjectStore.m_Projects) { - for (auto It : m_OplogsToCompact) + Ref<ProjectStore::Project> Project = ProjectIt.second; + std::vector<std::string> OplogsToCompact = Project->GetOplogsToCompact(); + CompactOplogCount += OplogsToCompact.size(); + for (const std::string& OplogId : OplogsToCompact) { - const std::string& ProjectId = It.first; - const std::string& OplogId = It.second; - Ref<ProjectStore::Project> Project = m_ProjectStore.OpenProject(ProjectId); - if (Project) + ProjectStore::Oplog* OpLog = nullptr; { - ProjectStore::Oplog* OpLog = Project->OpenOplog(OplogId, /*AllowCompact*/ false); + RwLock::SharedLockScope __(Project->m_ProjectLock); + if (auto OpIt = Project->m_Oplogs.find(OplogId); OpIt != Project->m_Oplogs.end()) + { + OpLog = OpIt->second.get(); + } + else + { + std::filesystem::path OplogBasePath = Project->BasePathForOplog(OplogId); + OpLog = + new ProjectStore::Oplog(OplogId, Project.Get(), Project->m_CidStore, OplogBasePath, std::filesystem::path{}); + OpLog->Read(); + } + if (OpLog) { const uint64_t PreSize = OpLog->TotalSize(); @@ -5051,23 +5089,35 @@ public: Stats.RemovedDisk += FreedSize; } + + if (auto OpIt = Project->m_Oplogs.find(OplogId); OpIt == Project->m_Oplogs.end()) + { + delete OpLog; + } } } } + if (!Ctx.Settings.IsDeleteMode) + { + ZEN_DEBUG("GCV2: projectstore [COMPACT] '{}': Skipped deleting of {} oplogs and {} projects, skipped compacting {} oplogs", + m_BasePath, + m_OplogPathsToRemove.size(), + m_ProjectPathsToRemove.size(), + CompactOplogCount); + } + m_ProjectPathsToRemove.clear(); m_OplogPathsToRemove.clear(); - m_OplogsToCompact.clear(); } virtual std::string GetGcName(GcCtx&) override { return fmt::format("projectstore: '{}'", m_BasePath.string()); } private: - ProjectStore& m_ProjectStore; - std::filesystem::path m_BasePath; - std::vector<std::filesystem::path> m_OplogPathsToRemove; - std::vector<std::filesystem::path> m_ProjectPathsToRemove; - std::vector<std::pair<std::string, std::string>> m_OplogsToCompact; + ProjectStore& m_ProjectStore; + std::filesystem::path m_BasePath; + std::vector<std::filesystem::path> m_OplogPathsToRemove; + std::vector<std::filesystem::path> m_ProjectPathsToRemove; }; GcStoreCompactor* @@ -5111,21 +5161,6 @@ ProjectStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) } } - for (const Ref<Project>& Project : Projects) - { - std::vector<std::string> OpLogs = Project->ScanForOplogs(); - for (const std::string& OpLogId : OpLogs) - { - Project->OpenOplog(OpLogId, /*AllowCompact*/ false); - if (Ctx.IsCancelledFlag) - { - return nullptr; - } - } - } - - std::vector<std::pair<std::string, std::string>> OplogsToCompact; - size_t ExpiredOplogCount = 0; for (const Ref<Project>& Project : Projects) { @@ -5135,28 +5170,17 @@ ProjectStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) } std::vector<std::string> ExpiredOplogs; + + std::vector<std::string> OpLogs = Project->ScanForOplogs(); + for (const std::string& OplogId : OpLogs) { - Project->IterateOplogs( - [&Ctx, &Stats, &Project, &ExpiredOplogs, &OplogsToCompact](const RwLock::SharedLockScope&, ProjectStore::Oplog& Oplog) { - Stats.CheckedCount++; - if (Project->IsExpired(Ctx.Settings.ProjectStoreExpireTime, Oplog)) - { - ExpiredOplogs.push_back(Oplog.OplogId()); - } - else - { - GcClock::TimePoint CompactExpireTime = GcClock::Now() - std::chrono::minutes(30); - if (!Project->IsOplogTouchedSince(CompactExpireTime, Oplog.OplogId())) - { - const uint32_t CompactUnusedThreshold = 25; - if (Oplog.GetUnusedSpacePercent() >= CompactUnusedThreshold) - { - OplogsToCompact.push_back(std::make_pair(Project->Identifier, Oplog.OplogId())); - } - } - } - }); + Stats.CheckedCount++; + if (Project->IsExpired(Ctx.Settings.ProjectStoreExpireTime, OplogId)) + { + ExpiredOplogs.push_back(OplogId); + } } + std::filesystem::path ProjectPath = BasePathForProject(Project->Identifier); ExpiredOplogCount += ExpiredOplogs.size(); if (Ctx.Settings.IsDeleteMode) @@ -5177,13 +5201,6 @@ ProjectStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) } } - if (ExpiredProjects.empty() && ExpiredOplogCount == 0 && OplogsToCompact.empty()) - { - ZEN_DEBUG("GCV2: projectstore [REMOVE EXPIRED] '{}': no expired projects, expired oplogs or oplogs to compact found", - m_ProjectBasePath); - return nullptr; - } - if (Ctx.Settings.IsDeleteMode) { for (const Ref<Project>& Project : ExpiredProjects) @@ -5222,15 +5239,7 @@ ProjectStore::RemoveExpiredData(GcCtx& Ctx, GcStats& Stats) size_t ExpiredProjectCount = ExpiredProjects.size(); Stats.FoundCount += ExpiredOplogCount + ExpiredProjectCount; - if (!OplogPathsToRemove.empty() || !ProjectPathsToRemove.empty() || !OplogsToCompact.empty()) - { - return new ProjectStoreGcStoreCompactor(*this, - m_ProjectBasePath, - std::move(OplogPathsToRemove), - std::move(ProjectPathsToRemove), - std::move(OplogsToCompact)); - } - return nullptr; + return new ProjectStoreGcStoreCompactor(*this, m_ProjectBasePath, std::move(OplogPathsToRemove), std::move(ProjectPathsToRemove)); } class ProjectStoreReferenceChecker : public GcReferenceChecker @@ -5362,7 +5371,7 @@ public: ProjectStoreOplogReferenceChecker(ProjectStore& InProjectStore, Ref<ProjectStore::Project> InProject, std::string_view InOplog) : m_ProjectStore(InProjectStore) , m_Project(InProject) - , m_OplogName(InOplog) + , m_OplogId(InOplog) { m_Project->EnableUpdateCapture(); } @@ -5372,10 +5381,15 @@ public: try { m_Project->DisableUpdateCapture(); - if (m_OplogCaptureEnabled) + + RwLock::SharedLockScope _(m_Project->m_ProjectLock); + if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) { - ZEN_ASSERT(m_Oplog); - m_Oplog->DisableUpdateCapture(); + ProjectStore::Oplog* Oplog = It->second.get(); + if (Oplog == m_OplogWithUpdateCapture) + { + Oplog->DisableUpdateCapture(); + } } } catch (const std::exception& Ex) @@ -5384,7 +5398,7 @@ public: } } - virtual std::string GetGcName(GcCtx&) override { return fmt::format("oplog: '{}/{}'", m_Project->Identifier, m_OplogName); } + virtual std::string GetGcName(GcCtx&) override { return fmt::format("oplog: '{}/{}'", m_Project->Identifier, m_OplogId); } virtual void PreCache(GcCtx& Ctx) override { @@ -5396,40 +5410,59 @@ public: { return; } - if (m_Oplog) - { - ZEN_INFO("GCV2: projectstore [PRECACHE] '{}': precached {} references in {} from {}/{}", - m_Oplog->m_BasePath, - m_References.size(), - NiceTimeSpanMs(Timer.GetElapsedTimeMs()), - m_Project->Identifier, - m_Oplog->OplogId()); - } + ZEN_INFO("GCV2: projectstore [PRECACHE] '{}': precached {} references in {} from {}/{}", + m_OplogBasePath, + m_References.size(), + NiceTimeSpanMs(Timer.GetElapsedTimeMs()), + m_Project->Identifier, + m_OplogId); }); - if (auto It = m_Project->m_Oplogs.find(m_OplogName); It != m_Project->m_Oplogs.end()) + ProjectStore::Oplog* Oplog = nullptr; + auto __ = MakeGuard([this, Oplog]() { + if (Oplog != nullptr && m_OplogWithUpdateCapture == nullptr) + { + delete Oplog; + } + }); + m_OplogBasePath = m_Project->BasePathForOplog(m_OplogId); + + RwLock::SharedLockScope ___(m_Project->m_ProjectLock); + if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) + { + It->second->EnableUpdateCapture(); + Oplog = It->second.get(); + m_OplogWithUpdateCapture = Oplog; + } + else if (ProjectStore::Oplog::ExistsAt(m_OplogBasePath)) + { + Oplog = new ProjectStore::Oplog(m_OplogId, m_Project.Get(), m_Project->m_CidStore, m_OplogBasePath, std::filesystem::path{}); + Oplog->Read(); + } + + RwLock::SharedLockScope ____(Oplog->m_OplogLock); + if (Ctx.IsCancelledFlag) { - m_Oplog = It->second.get(); - m_Oplog->EnableUpdateCapture(); - m_OplogCaptureEnabled = true; + return; + } - RwLock::SharedLockScope __(m_Oplog->m_OplogLock); - if (Ctx.IsCancelledFlag) + GcClock::TimePoint CompactExpireTime = GcClock::Now() - std::chrono::minutes(30); + if (!m_Project->IsOplogTouchedSince(CompactExpireTime, m_OplogId)) + { + const uint32_t CompactUnusedThreshold = 25; + if (Oplog->GetUnusedSpacePercent() >= CompactUnusedThreshold) { - return; + m_Project->AddOplogToCompact(m_OplogId); } - - m_Oplog->GetAttachmentsLocked(m_References, m_ProjectStore.m_Config.StoreAttachmentMetaData); } + + Oplog->GetAttachmentsLocked(m_References, m_ProjectStore.m_Config.StoreAttachmentMetaData); + m_OplogAccessTime = m_Project->LastOplogAccessTime(m_OplogId); } virtual void UpdateLockedState(GcCtx& Ctx) override { ZEN_TRACE_CPU("Store::Oplog::UpdateLockedState"); - if (!m_Oplog) - { - return; - } Stopwatch Timer; const auto _ = MakeGuard([&] { @@ -5438,28 +5471,37 @@ public: return; } ZEN_INFO("GCV2: projectstore [LOCKSTATE] '{}': found {} references in {} from {}/{}", - m_Oplog->m_BasePath, + m_OplogBasePath, m_References.size(), NiceTimeSpanMs(Timer.GetElapsedTimeMs()), m_Project->Identifier, - m_Oplog->OplogId()); + m_OplogId); }); - m_Oplog->IterateCapturedLSNs([&](const CbObjectView& UpdateOp) -> bool { - UpdateOp.IterateAttachments([&](CbFieldView Visitor) { m_References.emplace_back(Visitor.AsAttachment()); }); - return true; - }); - std::vector<IoHash> AddedAttachments = m_Oplog->GetCapturedAttachments(); - m_References.insert(m_References.end(), AddedAttachments.begin(), AddedAttachments.end()); + if (auto It = m_Project->m_Oplogs.find(m_OplogId); It != m_Project->m_Oplogs.end()) + { + ProjectStore::Oplog* Oplog = It->second.get(); + Oplog->IterateCapturedLSNs([&](const CbObjectView& UpdateOp) -> bool { + UpdateOp.IterateAttachments([&](CbFieldView Visitor) { m_References.emplace_back(Visitor.AsAttachment()); }); + return true; + }); + std::vector<IoHash> AddedAttachments = Oplog->GetCapturedAttachments(); + m_References.insert(m_References.end(), AddedAttachments.begin(), AddedAttachments.end()); + } + else if (m_Project->LastOplogAccessTime(m_OplogId) > m_OplogAccessTime && ProjectStore::Oplog::ExistsAt(m_OplogBasePath)) + { + ProjectStore::Oplog* Oplog = + new ProjectStore::Oplog(m_OplogId, m_Project.Get(), m_Project->m_CidStore, m_OplogBasePath, std::filesystem::path{}); + Oplog->Read(); + RwLock::SharedLockScope __(Oplog->m_OplogLock); + Oplog->GetAttachmentsLocked(m_References, m_ProjectStore.m_Config.StoreAttachmentMetaData); + delete Oplog; + } } virtual void RemoveUsedReferencesFromSet(GcCtx& Ctx, HashSet& IoCids) override { ZEN_TRACE_CPU("Store::Oplog::RemoveUsedReferencesFromSet"); - if (!m_Oplog) - { - return; - } size_t InitialCount = IoCids.size(); Stopwatch Timer; @@ -5468,11 +5510,13 @@ public: { return; } - ZEN_INFO("GCV2: projectstore [FILTER REFERENCES] '{}': filtered out {} used references out of {} in {}", - m_Oplog->m_BasePath, + ZEN_INFO("GCV2: projectstore [FILTER REFERENCES] '{}': filtered out {} used references out of {} in {} from {}/{}", + m_OplogBasePath, InitialCount - IoCids.size(), InitialCount, - NiceTimeSpanMs(Timer.GetElapsedTimeMs())); + NiceTimeSpanMs(Timer.GetElapsedTimeMs()), + m_Project->Identifier, + m_OplogId); }); for (const IoHash& ReferenceHash : m_References) @@ -5488,10 +5532,11 @@ public: } ProjectStore& m_ProjectStore; Ref<ProjectStore::Project> m_Project; - std::string m_OplogName; - ProjectStore::Oplog* m_Oplog = nullptr; + std::string m_OplogId; + std::filesystem::path m_OplogBasePath; + ProjectStore::Oplog* m_OplogWithUpdateCapture = nullptr; std::vector<IoHash> m_References; - bool m_OplogCaptureEnabled = false; + GcClock::TimePoint m_OplogAccessTime; }; std::vector<GcReferenceChecker*> @@ -5559,6 +5604,7 @@ ProjectStore::CreateReferenceCheckers(GcCtx& Ctx) std::vector<RwLock::SharedLockScope> ProjectStore::LockState(GcCtx&) { + ZEN_TRACE_CPU("Store::LockState"); std::vector<RwLock::SharedLockScope> Locks; Locks.emplace_back(RwLock::SharedLockScope(m_ProjectsLock)); for (auto& ProjectIt : m_Projects) diff --git a/src/zenserver/projectstore/projectstore.h b/src/zenserver/projectstore/projectstore.h index 55a3b7dee..7d969874d 100644 --- a/src/zenserver/projectstore/projectstore.h +++ b/src/zenserver/projectstore/projectstore.h @@ -172,6 +172,8 @@ public: Project* GetOuterProject() const { return m_OuterProject; } void CompactIfUnusedExceeds(bool DryRun, uint32_t CompactUnusedThreshold, std::string_view LogPrefix); + static std::optional<CbObject> ReadStateFile(const std::filesystem::path& BasePath, std::function<LoggerRef()>&& Log); + private: struct FileMapEntry { @@ -276,6 +278,7 @@ public: std::vector<std::string> ScanForOplogs() const; bool IsExpired(const GcClock::TimePoint ExpireTime) const; bool IsExpired(const GcClock::TimePoint ExpireTime, const ProjectStore::Oplog& Oplog) const; + bool IsExpired(const GcClock::TimePoint ExpireTime, std::string_view OplogId) const; bool IsOplogTouchedSince(const GcClock::TimePoint TouchTime, std::string_view Oplog) const; void TouchProject(); void TouchOplog(std::string_view Oplog); @@ -301,6 +304,21 @@ public: std::vector<RwLock::SharedLockScope> GetGcReferencerLocks(); + void AddOplogToCompact(std::string_view OplogId) + { + m_OplogsToCompactLock.WithExclusiveLock([&]() { m_OplogsToCompact.insert(std::string(OplogId)); }); + } + std::vector<std::string> GetOplogsToCompact() + { + std::vector<std::string> Result; + m_OplogsToCompactLock.WithExclusiveLock([&]() { + Result.reserve(m_OplogsToCompact.size()); + Result.insert(Result.end(), m_OplogsToCompact.begin(), m_OplogsToCompact.end()); + m_OplogsToCompact.clear(); + }); + return Result; + } + private: ProjectStore* m_ProjectStore; CidStore& m_CidStore; @@ -314,6 +332,9 @@ public: uint32_t m_UpdateCaptureRefCounter = 0; std::unique_ptr<std::vector<std::string>> m_CapturedOplogs; + RwLock m_OplogsToCompactLock; + std::unordered_set<std::string> m_OplogsToCompact; + std::filesystem::path BasePathForOplog(std::string_view OplogId) const; bool IsExpired(const std::string& EntryName, const std::filesystem::path& MarkerPath, const GcClock::TimePoint ExpireTime) const; void WriteAccessTimes(); @@ -321,6 +342,7 @@ public: friend class ProjectStoreOplogReferenceChecker; friend class ProjectStoreReferenceChecker; + friend class ProjectStoreGcStoreCompactor; }; // Oplog* OpenProjectOplog(std::string_view ProjectId, std::string_view OplogId); |