diff options
| author | Dan Engelbrecht <[email protected]> | 2024-08-30 11:26:42 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2024-08-30 11:26:42 +0200 |
| commit | cbb9ed149517cf781bf21bff4650d7d01bd6d567 (patch) | |
| tree | a185fe2a84cd6681caae7a71bb72d398421f97b6 /src/zenserver | |
| parent | zenserver process launch/termination improvements (#138) (diff) | |
| download | zen-cbb9ed149517cf781bf21bff4650d7d01bd6d567.tar.xz zen-cbb9ed149517cf781bf21bff4650d7d01bd6d567.zip | |
meta info store (#75)
- Feature: Added option `--gc-cache-attachment-store` which caches referenced attachments in cache records on disk for faster GC - default is `false`
- Feature: Added option `--gc-projectstore-attachment-store` which caches referenced attachments in project store oplogs on disk for faster GC - default is `false`
Diffstat (limited to 'src/zenserver')
| -rw-r--r-- | src/zenserver/config.cpp | 20 | ||||
| -rw-r--r-- | src/zenserver/config.h | 7 | ||||
| -rw-r--r-- | src/zenserver/projectstore/projectstore.cpp | 146 | ||||
| -rw-r--r-- | src/zenserver/projectstore/projectstore.h | 12 | ||||
| -rw-r--r-- | src/zenserver/zenserver.cpp | 9 |
5 files changed, 173 insertions, 21 deletions
diff --git a/src/zenserver/config.cpp b/src/zenserver/config.cpp index b430db629..6c2bf40d8 100644 --- a/src/zenserver/config.cpp +++ b/src/zenserver/config.cpp @@ -518,6 +518,12 @@ ParseConfigFile(const std::filesystem::path& Path, "gc-compactblock-threshold"sv); LuaOptions.AddOption("gc.verbose"sv, ServerOptions.GcConfig.Verbose, "gc-verbose"sv); LuaOptions.AddOption("gc.single-threaded"sv, ServerOptions.GcConfig.SingleThreaded, "gc-single-threaded"sv); + LuaOptions.AddOption("gc.cache.attachment.store"sv, + ServerOptions.StructuredCacheConfig.StoreAttachmentMetaData, + "gc-cache-attachment-store"); + LuaOptions.AddOption("gc.projectstore.attachment.store"sv, + ServerOptions.ProjectStoreConfig.StoreAttachmentMetaData, + "gc-projectstore-attachment-store"); ////// gc LuaOptions.AddOption("gc.cache.maxdurationseconds"sv, ServerOptions.GcConfig.Cache.MaxDurationSeconds, "gc-cache-duration-seconds"sv); @@ -898,6 +904,20 @@ ParseCliOptions(int argc, char* argv[], ZenServerOptions& ServerOptions) options.add_option("gc", "", + "gc-cache-attachment-store", + "Enable storing attachments referenced by a cache record in block store meta data.", + cxxopts::value<bool>(ServerOptions.StructuredCacheConfig.StoreAttachmentMetaData)->default_value("false"), + ""); + + options.add_option("gc", + "", + "gc-projectstore-attachment-store", + "Enable storing attachments referenced by project oplogs in meta data.", + cxxopts::value<bool>(ServerOptions.ProjectStoreConfig.StoreAttachmentMetaData)->default_value("false"), + ""); + + options.add_option("gc", + "", "gc-enabled", "Whether garbage collection is enabled or not.", cxxopts::value<bool>(ServerOptions.GcConfig.Enabled)->default_value("true"), diff --git a/src/zenserver/config.h b/src/zenserver/config.h index 4c9b9361c..6e45e7230 100644 --- a/src/zenserver/config.h +++ b/src/zenserver/config.h @@ -117,6 +117,12 @@ struct ZenStructuredCacheConfig uint64_t MemTargetFootprintBytes = 512 * 1024 * 1024; uint64_t MemTrimIntervalSeconds = 60; uint64_t MemMaxAgeSeconds = gsl::narrow<uint64_t>(std::chrono::seconds(std::chrono::days(1)).count()); + bool StoreAttachmentMetaData = false; +}; + +struct ZenProjectStoreConfig +{ + bool StoreAttachmentMetaData = false; }; struct ZenWorkspacesConfig @@ -132,6 +138,7 @@ struct ZenServerOptions ZenObjectStoreConfig ObjectStoreConfig; zen::HttpServerConfig HttpServerConfig; ZenStructuredCacheConfig StructuredCacheConfig; + ZenProjectStoreConfig ProjectStoreConfig; ZenStatsConfig StatsConfig; ZenWorkspacesConfig WorksSpacesConfig; std::filesystem::path SystemRootDir; // System root directory (used for machine level config) diff --git a/src/zenserver/projectstore/projectstore.cpp b/src/zenserver/projectstore/projectstore.cpp index bc1020b58..378423100 100644 --- a/src/zenserver/projectstore/projectstore.cpp +++ b/src/zenserver/projectstore/projectstore.cpp @@ -20,6 +20,7 @@ #include <zenstore/scrubcontext.h> #include <zenutil/cache/rpcrecording.h> #include <zenutil/packageformat.h> +#include <zenutil/referencemetadata.h> #include <zenutil/workerpools.h> #include "fileremoteprojectstore.h" @@ -111,6 +112,22 @@ namespace { return false; } + bool IsFileOlderThan(const std::filesystem::path& CheckPath, const std::filesystem::path& ReferencePath) + { + std::error_code Ec; + std::filesystem::file_time_type CheckWriteTime = std::filesystem::last_write_time(CheckPath, Ec); + if (Ec) + { + return true; + } + std::filesystem::file_time_type ReferenceWriteTime = std::filesystem::last_write_time(ReferencePath, Ec); + if (Ec) + { + return true; + } + return CheckWriteTime < ReferenceWriteTime; + } + struct CreateRemoteStoreResult { std::shared_ptr<RemoteProjectStore> Store; @@ -308,6 +325,7 @@ struct ProjectStore::OplogStorage : public RefCounted { if (Exists(BasePath)) { + std::error_code DummyEc; return std::filesystem::file_size(GetLogPath(BasePath)) + std::filesystem::file_size(GetBlobsPath(BasePath)); } return 0; @@ -845,6 +863,7 @@ ProjectStore::Oplog::Oplog(std::string_view Id, , m_CidStore(Store) , m_BasePath(BasePath) , m_MarkerPath(MarkerPath) +, m_MetaValid(false) , m_OplogId(Id) { using namespace std::literals; @@ -852,8 +871,9 @@ ProjectStore::Oplog::Oplog(std::string_view Id, m_Storage = new OplogStorage(this, m_BasePath); const bool StoreExists = m_Storage->Exists(); m_Storage->Open(/* IsCreate */ !StoreExists); - - m_TempPath = m_BasePath / "temp"sv; + m_TempPath = m_BasePath / "temp"sv; + m_MetaPath = m_BasePath / "ops.meta"sv; + m_MetaValid = !IsFileOlderThan(m_MetaPath, m_Storage->GetBlobsPath()); CleanDirectory(m_TempPath); } @@ -871,6 +891,11 @@ ProjectStore::Oplog::Flush() { ZEN_ASSERT(m_Storage); m_Storage->Flush(); + if (!m_MetaValid) + { + std::error_code DummyEc; + std::filesystem::remove(m_MetaPath, DummyEc); + } } void @@ -923,12 +948,15 @@ ProjectStore::Oplog::ScrubStorage(ScrubContext& Ctx) // Actually perform some clean-up RwLock::ExclusiveLockScope _(m_OplogLock); - for (const auto& Key : BadEntryKeys) { m_LatestOpMap.erase(Key); m_Storage->AppendTombstone(Key); } + if (!BadEntryKeys.empty()) + { + m_MetaValid = false; + } } else { @@ -970,6 +998,11 @@ ProjectStore::Oplog::TotalSize(const std::filesystem::path& BasePath) { Size += std::filesystem::file_size(StateFilePath); } + std::filesystem::path MetaFilePath = BasePath / "ops.meta"sv; + if (std::filesystem::exists(MetaFilePath)) + { + Size += std::filesystem::file_size(MetaFilePath); + } return Size; } @@ -1097,6 +1130,7 @@ ProjectStore::Oplog::Reset() m_Storage = new OplogStorage(this, m_BasePath); const bool StoreExists = m_Storage->Exists(); m_Storage->Open(/* IsCreate */ !StoreExists); + m_MetaValid = !IsFileOlderThan(m_MetaPath, m_Storage->GetBlobsPath()); return false; } m_ChunkMap.clear(); @@ -1106,6 +1140,7 @@ ProjectStore::Oplog::Reset() m_LatestOpMap.clear(); m_Storage = new OplogStorage(this, m_BasePath); m_Storage->Open(true); + m_MetaValid = false; CleanDirectory(m_TempPath); Write(); } @@ -1491,6 +1526,80 @@ ProjectStore::Oplog::IterateOplogLocked(std::function<void(CbObjectView)>&& Hand m_Storage->ReplayLogEntries(Entries, [&](CbObjectView Op) { Handler(Op); }); } +static constexpr uint32_t OplogMetaDataExpectedMagic = 0x6f'74'6d'62; // 'omta'; + +void +ProjectStore::Oplog::GetAttachmentsLocked(std::vector<IoHash>& OutAttachments, bool StoreMetaDataOnDisk) +{ + if (!m_Storage) + { + return; + } + + if (StoreMetaDataOnDisk && m_MetaValid) + { + IoBuffer MetadataPayload = IoBufferBuilder::MakeFromFile(m_MetaPath); + if (MetadataPayload) + { + if (GetAttachmentsFromMetaData<Oid, IoHash>( + MetadataPayload, + OplogMetaDataExpectedMagic, + [&](std::span<const Oid> Keys, std::span<const uint32_t> AttachmentCounts, std::span<const IoHash> Attachments) { + ZEN_UNUSED(Keys); + ZEN_UNUSED(AttachmentCounts); + OutAttachments.insert(OutAttachments.end(), Attachments.begin(), Attachments.end()); + })) + { + return; + } + } + } + + std::vector<Oid> Keys; + std::vector<uint32_t> AttachmentCounts; + size_t AttachmentOffset = OutAttachments.size(); + + IterateOplogLocked([&](CbObjectView Op) { + using namespace std::literals; + size_t CurrentAttachmentCount = OutAttachments.size(); + Op.IterateAttachments([&](CbFieldView Visitor) { OutAttachments.emplace_back(Visitor.AsAttachment()); }); + if (StoreMetaDataOnDisk) + { + XXH3_128Stream KeyHasher; + Op["key"sv].WriteToStream([&](const void* Data, size_t Size) { KeyHasher.Append(Data, Size); }); + XXH3_128 KeyHash128 = KeyHasher.GetHash(); + Oid KeyHash; + memcpy(&KeyHash, KeyHash128.Hash, sizeof KeyHash); + Keys.push_back(KeyHash); + AttachmentCounts.push_back(gsl::narrow<uint32_t>(OutAttachments.size() - CurrentAttachmentCount)); + } + }); + if (StoreMetaDataOnDisk) + { + const IoHash* FirstAttachment = OutAttachments.data() + AttachmentOffset; + size_t AttachmentCount = OutAttachments.size() - AttachmentOffset; + IoBuffer MetaPayload = BuildReferenceMetaData<Oid>(OplogMetaDataExpectedMagic, + Keys, + AttachmentCounts, + std::span<const IoHash>(FirstAttachment, AttachmentCount)) + .Flatten() + .AsIoBuffer(); + + const std::filesystem::path MetaPath = m_MetaPath; + std::error_code Ec; + TemporaryFile::SafeWriteFile(MetaPath, MetaPayload.GetView(), Ec); + if (Ec) + { + m_MetaValid = false; + ZEN_WARN("Unable to set meta data for oplog '{}' at meta path: '{}'. Reason: '{}'", OplogId(), MetaPath, Ec.message()); + } + else + { + m_MetaValid = true; + } + } +} + size_t ProjectStore::Oplog::GetOplogEntryCount() const { @@ -1954,7 +2063,7 @@ ProjectStore::Oplog::AppendNewOplogEntry(CbObjectView Core) using namespace std::literals; RefPtr<OplogStorage> Storage = GetStorage(); - if (!m_Storage) + if (!Storage) { return 0xffffffffu; } @@ -1962,11 +2071,12 @@ ProjectStore::Oplog::AppendNewOplogEntry(CbObjectView Core) OplogEntryMapping Mapping = GetMapping(Core); OplogStorage::AppendOpData OpData = OplogStorage::GetAppendOpData(Core); - const OplogEntry OpEntry = m_Storage->AppendOp(OpData); + const OplogEntry OpEntry = Storage->AppendOp(OpData); RwLock::ExclusiveLockScope OplogLock(m_OplogLock); const uint32_t EntryId = RegisterOplogEntry(OplogLock, Mapping, OpEntry); CaptureUpdatedLSNs(std::array<uint32_t, 1u>({EntryId})); + m_MetaValid = false; return EntryId; } @@ -1979,7 +2089,7 @@ ProjectStore::Oplog::AppendNewOplogEntries(std::span<CbObjectView> Cores) using namespace std::literals; RefPtr<OplogStorage> Storage = GetStorage(); - if (!m_Storage) + if (!Storage) { return std::vector<uint32_t>(Cores.size(), 0xffffffffu); } @@ -2008,6 +2118,7 @@ ProjectStore::Oplog::AppendNewOplogEntries(std::span<CbObjectView> Cores) EntryIds[OpIndex] = RegisterOplogEntry(OplogLock, Mappings[OpIndex], OpEntries[OpIndex]); } CaptureUpdatedLSNs(EntryIds); + m_MetaValid = false; } return EntryIds; } @@ -2664,12 +2775,13 @@ ProjectStore::Project::LastOplogAccessTime(std::string_view Oplog) const ////////////////////////////////////////////////////////////////////////// -ProjectStore::ProjectStore(CidStore& Store, std::filesystem::path BasePath, GcManager& Gc, JobQueue& JobQueue) +ProjectStore::ProjectStore(CidStore& Store, std::filesystem::path BasePath, GcManager& Gc, JobQueue& JobQueue, const Configuration& Config) : m_Log(logging::Get("project")) , m_Gc(Gc) , m_CidStore(Store) , m_JobQueue(JobQueue) , m_ProjectBasePath(BasePath) +, m_Config(Config) , m_DiskWriteBlocker(Gc.GetDiskWriteBlocker()) { ZEN_INFO("initializing project store at '{}'", m_ProjectBasePath); @@ -4675,10 +4787,7 @@ public: Oplog->OplogId()); }); - Oplog->IterateOplogLocked([&](const CbObjectView& UpdateOp) -> bool { - UpdateOp.IterateAttachments([&](CbFieldView Visitor) { m_References.emplace_back(Visitor.AsAttachment()); }); - return true; - }); + Oplog->GetAttachmentsLocked(m_References, m_ProjectStore.m_Config.StoreAttachmentMetaData); } } @@ -4779,9 +4888,8 @@ public: { return; } - m_Oplog->IterateOplog([&](CbObjectView Op) { - Op.IterateAttachments([&](CbFieldView Visitor) { m_References.emplace_back(Visitor.AsAttachment()); }); - }); + + m_Oplog->GetAttachmentsLocked(m_References, m_ProjectStore.m_Config.StoreAttachmentMetaData); } } @@ -5021,7 +5129,7 @@ TEST_CASE("project.store.create") std::string_view ProjectName("proj1"sv); std::filesystem::path BasePath = TempDir.Path() / "projectstore"; - ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue); + ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue, ProjectStore::Configuration{.StoreAttachmentMetaData = true}); std::filesystem::path RootDir = TempDir.Path() / "root"; std::filesystem::path EngineRootDir = TempDir.Path() / "engine"; std::filesystem::path ProjectRootDir = TempDir.Path() / "game"; @@ -5050,7 +5158,7 @@ TEST_CASE("project.store.lifetimes") CidStore.Initialize(CidConfig); std::filesystem::path BasePath = TempDir.Path() / "projectstore"; - ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue); + ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue, ProjectStore::Configuration{.StoreAttachmentMetaData = true}); std::filesystem::path RootDir = TempDir.Path() / "root"; std::filesystem::path EngineRootDir = TempDir.Path() / "engine"; std::filesystem::path ProjectRootDir = TempDir.Path() / "game"; @@ -5112,7 +5220,7 @@ TEST_CASE_TEMPLATE("project.store.export", CidStore.Initialize(CidConfig); std::filesystem::path BasePath = TempDir.Path() / "projectstore"; - ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue); + ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue, ProjectStore::Configuration{.StoreAttachmentMetaData = true}); std::filesystem::path RootDir = TempDir.Path() / "root"; std::filesystem::path EngineRootDir = TempDir.Path() / "engine"; std::filesystem::path ProjectRootDir = TempDir.Path() / "game"; @@ -5193,7 +5301,7 @@ TEST_CASE("project.store.gc") CidStore.Initialize(CidConfig); std::filesystem::path BasePath = TempDir.Path() / "projectstore"; - ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue); + ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue, ProjectStore::Configuration{.StoreAttachmentMetaData = true}); std::filesystem::path RootDir = TempDir.Path() / "root"; std::filesystem::path EngineRootDir = TempDir.Path() / "engine"; @@ -5477,7 +5585,7 @@ TEST_CASE("project.store.partial.read") CidStore.Initialize(CidConfig); std::filesystem::path BasePath = TempDir.Path() / "projectstore"sv; - ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue); + ProjectStore ProjectStore(CidStore, BasePath, Gc, *JobQueue, ProjectStore::Configuration{.StoreAttachmentMetaData = true}); std::filesystem::path RootDir = TempDir.Path() / "root"sv; std::filesystem::path EngineRootDir = TempDir.Path() / "engine"sv; diff --git a/src/zenserver/projectstore/projectstore.h b/src/zenserver/projectstore/projectstore.h index bb042e331..bed3c83b7 100644 --- a/src/zenserver/projectstore/projectstore.h +++ b/src/zenserver/projectstore/projectstore.h @@ -63,7 +63,12 @@ class ProjectStore : public RefCounted, public GcStorage, public GcContributor, struct OplogStorage; public: - ProjectStore(CidStore& Store, std::filesystem::path BasePath, GcManager& Gc, JobQueue& JobQueue); + struct Configuration + { + bool StoreAttachmentMetaData = false; + }; + + ProjectStore(CidStore& Store, std::filesystem::path BasePath, GcManager& Gc, JobQueue& JobQueue, const Configuration& Config); ~ProjectStore(); struct Project; @@ -162,6 +167,8 @@ public: uint32_t GetUnusedSpacePercent() const; void Compact(bool DryRun, bool RetainLSNs, std::string_view LogPrefix); + void GetAttachmentsLocked(std::vector<IoHash>& OutAttachments, bool StoreMetaDataOnDisk); + private: struct FileMapEntry { @@ -177,6 +184,7 @@ public: std::filesystem::path m_BasePath; std::filesystem::path m_MarkerPath; std::filesystem::path m_TempPath; + std::filesystem::path m_MetaPath; mutable RwLock m_OplogLock; OidMap<IoHash> m_ChunkMap; // output data chunk id -> CAS address @@ -185,6 +193,7 @@ public: int32_t m_ManifestVersion; // File system manifest version tsl::robin_map<uint32_t, OplogEntryAddress> m_OpAddressMap; // Index LSN -> op data in ops blob file OidMap<uint32_t> m_LatestOpMap; // op key -> latest op LSN for key + std::atomic<bool> m_MetaValid = false; mutable RwLock m_UpdateCaptureLock; uint32_t m_UpdateCaptureRefCounter = 0; @@ -429,6 +438,7 @@ private: CidStore& m_CidStore; JobQueue& m_JobQueue; std::filesystem::path m_ProjectBasePath; + const Configuration m_Config; mutable RwLock m_ProjectsLock; std::map<std::string, Ref<Project>> m_Projects; const DiskWriteBlocker* m_DiskWriteBlocker = nullptr; diff --git a/src/zenserver/zenserver.cpp b/src/zenserver/zenserver.cpp index dcfef7530..ebc56d7d9 100644 --- a/src/zenserver/zenserver.cpp +++ b/src/zenserver/zenserver.cpp @@ -232,7 +232,12 @@ ZenServer::Initialize(const ZenServerOptions& ServerOptions, ZenServerState::Zen ZEN_INFO("instantiating project service"); - m_ProjectStore = new ProjectStore(*m_CidStore, m_DataRoot / "projects", m_GcManager, *m_JobQueue); + m_ProjectStore = + new ProjectStore(*m_CidStore, + m_DataRoot / "projects", + m_GcManager, + *m_JobQueue, + ProjectStore::Configuration{.StoreAttachmentMetaData = ServerOptions.ProjectStoreConfig.StoreAttachmentMetaData}); m_HttpProjectService.reset(new HttpProjectService{*m_CidStore, m_ProjectStore, m_StatsService, *m_AuthMgr}); if (ServerOptions.WorksSpacesConfig.Enabled) @@ -513,6 +518,8 @@ ZenServer::InitializeStructuredCache(const ZenServerOptions& ServerOptions) Config.NamespaceConfig.DiskLayerConfig.MemCacheTargetFootprintBytes = ServerOptions.StructuredCacheConfig.MemTargetFootprintBytes; Config.NamespaceConfig.DiskLayerConfig.MemCacheTrimIntervalSeconds = ServerOptions.StructuredCacheConfig.MemTrimIntervalSeconds; Config.NamespaceConfig.DiskLayerConfig.MemCacheMaxAgeSeconds = ServerOptions.StructuredCacheConfig.MemMaxAgeSeconds; + Config.NamespaceConfig.DiskLayerConfig.BucketConfig.StoreAttachmentMetaData = + ServerOptions.StructuredCacheConfig.StoreAttachmentMetaData; if (ServerOptions.IsDedicated) { |