diff options
| author | Dan Engelbrecht <[email protected]> | 2023-01-11 23:52:55 -0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-01-11 23:52:55 -0800 |
| commit | a24820cbbc3c032f2cfc7b4c3bf9cac0bbaaeb6c (patch) | |
| tree | 4c756826edc1839cdf894a995a8dc4e927f6bb65 /zenserver | |
| parent | 0.2.1 (diff) | |
| download | zen-a24820cbbc3c032f2cfc7b4c3bf9cac0bbaaeb6c.tar.xz zen-a24820cbbc3c032f2cfc7b4c3bf9cac0bbaaeb6c.zip | |
Add info (GET) endpoints for structured cache (#211)
* Add GET requests on cache/namespace/bucket level
* Add root route for project store requests (same as /list)
* Add markerpath to oplog info object
* Add totalsize, opcount and expired to oplog info
* Changelog
Diffstat (limited to 'zenserver')
| -rw-r--r-- | zenserver/cache/structuredcache.cpp | 160 | ||||
| -rw-r--r-- | zenserver/cache/structuredcache.h | 25 | ||||
| -rw-r--r-- | zenserver/cache/structuredcachestore.cpp | 159 | ||||
| -rw-r--r-- | zenserver/cache/structuredcachestore.h | 119 | ||||
| -rw-r--r-- | zenserver/projectstore.cpp | 11 | ||||
| -rw-r--r-- | zenserver/projectstore.h | 1 |
6 files changed, 415 insertions, 60 deletions
diff --git a/zenserver/cache/structuredcache.cpp b/zenserver/cache/structuredcache.cpp index d273bc88c..7550ce111 100644 --- a/zenserver/cache/structuredcache.cpp +++ b/zenserver/cache/structuredcache.cpp @@ -334,13 +334,15 @@ namespace { bool HttpRequestParseRelativeUri(std::string_view Key, HttpRequestData& Data) { std::vector<std::string_view> Tokens; - uint32_t TokenCount = zen::ForEachStrTok(Key, '/', [&](const std::string_view& Token) { + uint32_t TokenCount = ForEachStrTok(Key, '/', [&](const std::string_view& Token) { Tokens.push_back(Token); return true; }); switch (TokenCount) { + case 0: + return true; case 1: Data.Namespace = GetValidNamespaceName(Tokens[0]); return Data.Namespace.has_value(); @@ -624,26 +626,114 @@ HttpStructuredCacheService::HandleRequest(HttpServerRequest& Request) return HandleCacheBucketRequest(Request, RequestData.Namespace.value(), RequestData.Bucket.value()); } - ZEN_ASSERT(RequestData.Namespace.has_value()); - return HandleCacheNamespaceRequest(Request, RequestData.Namespace.value()); + if (RequestData.Namespace.has_value()) + { + return HandleCacheNamespaceRequest(Request, RequestData.Namespace.value()); + } + return HandleCacheRequest(Request); +} + +void +HttpStructuredCacheService::HandleCacheRequest(HttpServerRequest& Request) +{ + switch (Request.RequestVerb()) + { + case HttpVerb::kHead: + case HttpVerb::kGet: + { + ZenCacheStore::Info Info = m_CacheStore.GetInfo(); + + CbObjectWriter ResponseWriter; + + ResponseWriter.BeginObject("Configuration"); + { + ExtendableStringBuilder<128> BasePathString; + BasePathString << Info.Config.BasePath.u8string(); + ResponseWriter.AddString("BasePath"sv, BasePathString.ToView()); + ResponseWriter.AddBool("AllowAutomaticCreationOfNamespaces", Info.Config.AllowAutomaticCreationOfNamespaces); + } + ResponseWriter.EndObject(); + + std::sort(begin(Info.NamespaceNames), end(Info.NamespaceNames), [](std::string_view L, std::string_view R) { + return L.compare(R) < 0; + }); + ResponseWriter.BeginArray("Namespaces"); + for (const std::string& NamespaceName : Info.NamespaceNames) + { + ResponseWriter.AddString(NamespaceName); + } + ResponseWriter.EndArray(); + ResponseWriter.BeginObject("StorageSize"); + { + ResponseWriter.AddInteger("DiskSize", Info.StorageSize.DiskSize); + ResponseWriter.AddInteger("MemorySize", Info.StorageSize.MemorySize); + } + + ResponseWriter.EndObject(); + + ResponseWriter.AddInteger("DiskEntryCount", Info.DiskEntryCount); + ResponseWriter.AddInteger("MemoryEntryCount", Info.MemoryEntryCount); + + return Request.WriteResponse(HttpResponseCode::OK, ResponseWriter.Save()); + } + break; + } } void -HttpStructuredCacheService::HandleCacheNamespaceRequest(zen::HttpServerRequest& Request, std::string_view Namespace) +HttpStructuredCacheService::HandleCacheNamespaceRequest(HttpServerRequest& Request, std::string_view NamespaceName) { switch (Request.RequestVerb()) { case HttpVerb::kHead: case HttpVerb::kGet: { - // Query stats + std::optional<ZenCacheNamespace::Info> Info = m_CacheStore.GetNamespaceInfo(NamespaceName); + if (!Info.has_value()) + { + return Request.WriteResponse(HttpResponseCode::NotFound); + } + + CbObjectWriter ResponseWriter; + + ResponseWriter.BeginObject("Configuration"); + { + ExtendableStringBuilder<128> BasePathString; + BasePathString << Info->Config.RootDir.u8string(); + ResponseWriter.AddString("RootDir"sv, BasePathString.ToView()); + ResponseWriter.AddInteger("DiskLayerThreshold"sv, Info->Config.DiskLayerThreshold); + } + ResponseWriter.EndObject(); + + std::sort(begin(Info->BucketNames), end(Info->BucketNames), [](std::string_view L, std::string_view R) { + return L.compare(R) < 0; + }); + + ResponseWriter.BeginArray("Buckets"sv); + for (const std::string& BucketName : Info->BucketNames) + { + ResponseWriter.AddString(BucketName); + } + ResponseWriter.EndArray(); + + ResponseWriter.BeginObject("StorageSize"sv); + { + ResponseWriter.AddInteger("DiskSize"sv, Info->DiskLayerInfo.TotalSize); + ResponseWriter.AddInteger("MemorySize"sv, Info->MemoryLayerInfo.TotalSize); + } + ResponseWriter.EndObject(); + + ResponseWriter.AddInteger("DiskEntryCount", Info->DiskLayerInfo.EntryCount); + ResponseWriter.AddInteger("MemoryEntryCount", Info->MemoryLayerInfo.EntryCount); + + return Request.WriteResponse(HttpResponseCode::OK, ResponseWriter.Save()); } break; case HttpVerb::kDelete: // Drop namespace { - if (m_CacheStore.DropNamespace(Namespace)) + if (m_CacheStore.DropNamespace(NamespaceName)) { return Request.WriteResponse(HttpResponseCode::OK); } @@ -660,21 +750,41 @@ HttpStructuredCacheService::HandleCacheNamespaceRequest(zen::HttpServerRequest& } void -HttpStructuredCacheService::HandleCacheBucketRequest(HttpServerRequest& Request, std::string_view Namespace, std::string_view Bucket) +HttpStructuredCacheService::HandleCacheBucketRequest(HttpServerRequest& Request, + std::string_view NamespaceName, + std::string_view BucketName) { switch (Request.RequestVerb()) { case HttpVerb::kHead: case HttpVerb::kGet: { - // Query stats + std::optional<ZenCacheNamespace::BucketInfo> Info = m_CacheStore.GetBucketInfo(NamespaceName, BucketName); + if (!Info.has_value()) + { + return Request.WriteResponse(HttpResponseCode::NotFound); + } + + CbObjectWriter ResponseWriter; + + ResponseWriter.BeginObject("StorageSize"); + { + ResponseWriter.AddInteger("DiskSize", Info->DiskLayerInfo.TotalSize); + ResponseWriter.AddInteger("MemorySize", Info->MemoryLayerInfo.TotalSize); + } + ResponseWriter.EndObject(); + + ResponseWriter.AddInteger("DiskEntryCount", Info->DiskLayerInfo.EntryCount); + ResponseWriter.AddInteger("MemoryEntryCount", Info->MemoryLayerInfo.EntryCount); + + return Request.WriteResponse(HttpResponseCode::OK, ResponseWriter.Save()); } break; case HttpVerb::kDelete: // Drop bucket { - if (m_CacheStore.DropBucket(Namespace, Bucket)) + if (m_CacheStore.DropBucket(NamespaceName, BucketName)) { return Request.WriteResponse(HttpResponseCode::OK); } @@ -711,7 +821,7 @@ HttpStructuredCacheService::HandleCacheRecordRequest(HttpServerRequest& Request, } void -HttpStructuredCacheService::HandleGetCacheRecord(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) +HttpStructuredCacheService::HandleGetCacheRecord(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) { const ZenContentType AcceptType = Request.AcceptContentType(); const bool SkipData = EnumHasAllFlags(PolicyFromUrl, CachePolicy::SkipData); @@ -1027,7 +1137,7 @@ HttpStructuredCacheService::HandleGetCacheRecord(zen::HttpServerRequest& Request } void -HttpStructuredCacheService::HandlePutCacheRecord(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) +HttpStructuredCacheService::HandlePutCacheRecord(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) { IoBuffer Body = Request.ReadPayload(); @@ -1233,7 +1343,7 @@ HttpStructuredCacheService::HandleCacheChunkRequest(HttpServerRequest& Request, } void -HttpStructuredCacheService::HandleGetCacheChunk(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) +HttpStructuredCacheService::HandleGetCacheChunk(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) { Stopwatch Timer; @@ -1311,7 +1421,7 @@ HttpStructuredCacheService::HandleGetCacheChunk(zen::HttpServerRequest& Request, } void -HttpStructuredCacheService::HandlePutCacheChunk(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) +HttpStructuredCacheService::HandlePutCacheChunk(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl) { // Note: Individual cacherecord values are not propagated upstream until a valid cache record has been stored ZEN_UNUSED(PolicyFromUrl); @@ -1369,7 +1479,7 @@ HttpStructuredCacheService::HandleRpcRequest(const ZenContentType ContentType, CbObject ObjectBuffer; if (ContentType == ZenContentType::kCbObject) { - ObjectBuffer = zen::LoadCompactBinaryObject(std::move(Body)); + ObjectBuffer = LoadCompactBinaryObject(std::move(Body)); Object = ObjectBuffer; } else @@ -1454,7 +1564,7 @@ HttpStructuredCacheService::ReplayRequestRecorder(cache::detail::IRequestReplaye } void -HttpStructuredCacheService::HandleRpcRequest(zen::HttpServerRequest& Request) +HttpStructuredCacheService::HandleRpcRequest(HttpServerRequest& Request) { switch (Request.RequestVerb()) { @@ -2800,7 +2910,7 @@ HttpStructuredCacheService::WriteGetCacheChunksResponse(std::string_view Namespa } void -HttpStructuredCacheService::HandleStatsRequest(zen::HttpServerRequest& Request) +HttpStructuredCacheService::HandleStatsRequest(HttpServerRequest& Request) { CbObjectWriter Cbo; @@ -2843,7 +2953,7 @@ HttpStructuredCacheService::HandleStatsRequest(zen::HttpServerRequest& Request) } void -HttpStructuredCacheService::HandleStatusRequest(zen::HttpServerRequest& Request) +HttpStructuredCacheService::HandleStatusRequest(HttpServerRequest& Request) { CbObjectWriter Cbo; Cbo << "ok" << true; @@ -2854,6 +2964,20 @@ HttpStructuredCacheService::HandleStatusRequest(zen::HttpServerRequest& Request) TEST_CASE("z$service.parse.relative.Uri") { + HttpRequestData RootRequest; + CHECK(HttpRequestParseRelativeUri("", RootRequest)); + CHECK(!RootRequest.Namespace.has_value()); + CHECK(!RootRequest.Bucket.has_value()); + CHECK(!RootRequest.HashKey.has_value()); + CHECK(!RootRequest.ValueContentId.has_value()); + + RootRequest = {}; + CHECK(HttpRequestParseRelativeUri("/", RootRequest)); + CHECK(!RootRequest.Namespace.has_value()); + CHECK(!RootRequest.Bucket.has_value()); + CHECK(!RootRequest.HashKey.has_value()); + CHECK(!RootRequest.ValueContentId.has_value()); + HttpRequestData LegacyBucketRequestBecomesNamespaceRequest; CHECK(HttpRequestParseRelativeUri("test", LegacyBucketRequestBecomesNamespaceRequest)); CHECK(LegacyBucketRequestBecomesNamespaceRequest.Namespace == "test"sv); @@ -2921,8 +3045,6 @@ TEST_CASE("z$service.parse.relative.Uri") CHECK(V2ValueContentIdRequest.ValueContentId == IoHash::FromHexString("56789abcdef12345678956789abcdef123456789"sv)); HttpRequestData Invalid; - CHECK(!HttpRequestParseRelativeUri("", Invalid)); - CHECK(!HttpRequestParseRelativeUri("/", Invalid)); CHECK(!HttpRequestParseRelativeUri("bad\2_namespace", Invalid)); CHECK(!HttpRequestParseRelativeUri("nice/\2\1bucket", Invalid)); CHECK(!HttpRequestParseRelativeUri("namespace/bucket/0123456789a", Invalid)); diff --git a/zenserver/cache/structuredcache.h b/zenserver/cache/structuredcache.h index a2a4a940c..6a4cedef0 100644 --- a/zenserver/cache/structuredcache.h +++ b/zenserver/cache/structuredcache.h @@ -94,7 +94,7 @@ public: ~HttpStructuredCacheService(); virtual const char* BaseUri() const override; - virtual void HandleRequest(zen::HttpServerRequest& Request) override; + virtual void HandleRequest(HttpServerRequest& Request) override; void Flush(); void Scrub(ScrubContext& Ctx); @@ -121,13 +121,13 @@ private: Invalid, }; - void HandleCacheRecordRequest(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandleGetCacheRecord(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandlePutCacheRecord(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandleCacheChunkRequest(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandleGetCacheChunk(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandlePutCacheChunk(zen::HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); - void HandleRpcRequest(zen::HttpServerRequest& Request); + void HandleCacheRecordRequest(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandleGetCacheRecord(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandlePutCacheRecord(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandleCacheChunkRequest(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandleGetCacheChunk(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandlePutCacheChunk(HttpServerRequest& Request, const CacheRef& Ref, CachePolicy PolicyFromUrl); + void HandleRpcRequest(HttpServerRequest& Request); CbPackage HandleRpcPutCacheRecords(const CbPackage& BatchRequest); CbPackage HandleRpcGetCacheRecords(CbObjectView BatchRequest); @@ -139,10 +139,11 @@ private: uint32_t& OutAcceptMagic, RpcAcceptOptions& OutAcceptFlags); - void HandleCacheNamespaceRequest(zen::HttpServerRequest& Request, std::string_view Namespace); - void HandleCacheBucketRequest(zen::HttpServerRequest& Request, std::string_view Namespace, std::string_view Bucket); - virtual void HandleStatsRequest(zen::HttpServerRequest& Request) override; - virtual void HandleStatusRequest(zen::HttpServerRequest& Request) override; + void HandleCacheRequest(HttpServerRequest& Request); + void HandleCacheNamespaceRequest(HttpServerRequest& Request, std::string_view Namespace); + void HandleCacheBucketRequest(HttpServerRequest& Request, std::string_view Namespace, std::string_view Bucket); + virtual void HandleStatsRequest(HttpServerRequest& Request) override; + virtual void HandleStatusRequest(HttpServerRequest& Request) override; PutResult PutCacheRecord(PutRequestData& Request, const CbPackage* Package); /** HandleRpcGetCacheChunks Helper: Parse the Body object into RecordValue Requests and Value Requests. */ diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp index de1243ddd..b32497a30 100644 --- a/zenserver/cache/structuredcachestore.cpp +++ b/zenserver/cache/structuredcachestore.cpp @@ -334,6 +334,38 @@ ZenCacheNamespace::StorageSize() const return {.DiskSize = m_DiskLayer.TotalSize(), .MemorySize = m_MemLayer.TotalSize()}; } +ZenCacheNamespace::Info +ZenCacheNamespace::GetInfo() const +{ + ZenCacheNamespace::Info Info = {.Config = {.RootDir = m_RootDir, .DiskLayerThreshold = m_DiskLayerSizeThreshold}, + .DiskLayerInfo = m_DiskLayer.GetInfo(), + .MemoryLayerInfo = m_MemLayer.GetInfo()}; + std::unordered_set<std::string> BucketNames; + for (const std::string& BucketName : Info.DiskLayerInfo.BucketNames) + { + BucketNames.insert(BucketName); + } + for (const std::string& BucketName : Info.MemoryLayerInfo.BucketNames) + { + BucketNames.insert(BucketName); + } + Info.BucketNames.insert(Info.BucketNames.end(), BucketNames.begin(), BucketNames.end()); + return Info; +} + +std::optional<ZenCacheNamespace::BucketInfo> +ZenCacheNamespace::GetBucketInfo(std::string_view Bucket) const +{ + std::optional<ZenCacheDiskLayer::BucketInfo> DiskBucketInfo = m_DiskLayer.GetBucketInfo(Bucket); + if (!DiskBucketInfo.has_value()) + { + return {}; + } + ZenCacheNamespace::BucketInfo Info = {.DiskLayerInfo = *DiskBucketInfo, + .MemoryLayerInfo = m_MemLayer.GetBucketInfo(Bucket).value_or(ZenCacheMemoryLayer::BucketInfo{})}; + return Info; +} + ////////////////////////////////////////////////////////////////////////// ZenCacheMemoryLayer::ZenCacheMemoryLayer() @@ -484,6 +516,33 @@ ZenCacheMemoryLayer::TotalSize() const return TotalSize; } +ZenCacheMemoryLayer::Info +ZenCacheMemoryLayer::GetInfo() const +{ + ZenCacheMemoryLayer::Info Info = {.Config = m_Configuration, .TotalSize = TotalSize()}; + + RwLock::SharedLockScope _(m_Lock); + Info.BucketNames.reserve(m_Buckets.size()); + for (auto& Kv : m_Buckets) + { + Info.BucketNames.push_back(Kv.first); + Info.EntryCount += Kv.second->EntryCount(); + } + return Info; +} + +std::optional<ZenCacheMemoryLayer::BucketInfo> +ZenCacheMemoryLayer::GetBucketInfo(std::string_view Bucket) const +{ + RwLock::ExclusiveLockScope _(m_Lock); + + if (auto It = m_Buckets.find(std::string(Bucket)); It != m_Buckets.end()) + { + return ZenCacheMemoryLayer::BucketInfo{.EntryCount = It->second->EntryCount(), .TotalSize = It->second->TotalSize()}; + } + return {}; +} + void ZenCacheMemoryLayer::CacheBucket::Scrub(ScrubContext& Ctx) { @@ -582,6 +641,13 @@ ZenCacheMemoryLayer::CacheBucket::Drop() m_TotalSize.store(0); } +uint64_t +ZenCacheMemoryLayer::CacheBucket::EntryCount() const +{ + RwLock::SharedLockScope _(m_BucketLock); + return static_cast<uint64_t>(m_CacheMap.size()); +} + ////////////////////////////////////////////////////////////////////////// ZenCacheDiskLayer::CacheBucket::CacheBucket(std::string BucketName) : m_BucketName(std::move(BucketName)), m_BucketId(Oid::Zero) @@ -1601,6 +1667,13 @@ ZenCacheDiskLayer::CacheBucket::UpdateAccessTimes(const std::vector<zen::access_ } } +uint64_t +ZenCacheDiskLayer::CacheBucket::EntryCount() const +{ + RwLock::SharedLockScope _(m_IndexLock); + return static_cast<uint64_t>(m_Index.size()); +} + void ZenCacheDiskLayer::CollectGarbage(GcContext& GcCtx) { @@ -2043,15 +2116,38 @@ ZenCacheDiskLayer::TotalSize() const return TotalSize; } +ZenCacheDiskLayer::Info +ZenCacheDiskLayer::GetInfo() const +{ + ZenCacheDiskLayer::Info Info = {.Config = {.RootDir = m_RootDir}, .TotalSize = TotalSize()}; + + RwLock::SharedLockScope _(m_Lock); + Info.BucketNames.reserve(m_Buckets.size()); + for (auto& Kv : m_Buckets) + { + Info.BucketNames.push_back(Kv.first); + Info.EntryCount += Kv.second->EntryCount(); + } + return Info; +} + +std::optional<ZenCacheDiskLayer::BucketInfo> +ZenCacheDiskLayer::GetBucketInfo(std::string_view Bucket) const +{ + RwLock::ExclusiveLockScope _(m_Lock); + + if (auto It = m_Buckets.find(std::string(Bucket)); It != m_Buckets.end()) + { + return ZenCacheDiskLayer::BucketInfo{.EntryCount = It->second->EntryCount(), .TotalSize = It->second->TotalSize()}; + } + return {}; +} + //////////////////////////// ZenCacheStore static constexpr std::string_view UE4DDCNamespaceName = "ue4.ddc"; -ZenCacheStore::ZenCacheStore(GcManager& Gc, const Configuration& Configuration) -//: GcStorage(Gc) -//, GcContributor(Gc) -: m_Gc(Gc) -, m_Configuration(Configuration) +ZenCacheStore::ZenCacheStore(GcManager& Gc, const Configuration& Configuration) : m_Gc(Gc), m_Configuration(Configuration) { CreateDirectories(m_Configuration.BasePath); @@ -2186,6 +2282,24 @@ ZenCacheStore::GetNamespace(std::string_view Namespace) return NewNamespace.first->second.get(); } +const ZenCacheNamespace* +ZenCacheStore::FindNamespace(std::string_view Namespace) const +{ + RwLock::SharedLockScope _(m_NamespacesLock); + if (auto It = m_Namespaces.find(std::string(Namespace)); It != m_Namespaces.end()) + { + return It->second.get(); + } + if (Namespace == DefaultNamespace) + { + if (auto It = m_Namespaces.find(std::string(UE4DDCNamespaceName)); It != m_Namespaces.end()) + { + return It->second.get(); + } + } + return nullptr; +} + void ZenCacheStore::IterateNamespaces(const std::function<void(std::string_view Namespace, ZenCacheNamespace& Store)>& Callback) const { @@ -2220,6 +2334,41 @@ ZenCacheStore::StorageSize() const return Size; } +ZenCacheStore::Info +ZenCacheStore::GetInfo() const +{ + ZenCacheStore::Info Info = {.Config = m_Configuration, .StorageSize = StorageSize()}; + + IterateNamespaces([&Info](std::string_view NamespaceName, ZenCacheNamespace& Namespace) { + Info.NamespaceNames.push_back(std::string(NamespaceName)); + ZenCacheNamespace::Info NamespaceInfo = Namespace.GetInfo(); + Info.DiskEntryCount += NamespaceInfo.DiskLayerInfo.EntryCount; + Info.MemoryEntryCount += NamespaceInfo.MemoryLayerInfo.EntryCount; + }); + + return Info; +} + +std::optional<ZenCacheNamespace::Info> +ZenCacheStore::GetNamespaceInfo(std::string_view NamespaceName) +{ + if (const ZenCacheNamespace* Namespace = FindNamespace(NamespaceName); Namespace) + { + return Namespace->GetInfo(); + } + return {}; +} + +std::optional<ZenCacheNamespace::BucketInfo> +ZenCacheStore::GetBucketInfo(std::string_view NamespaceName, std::string_view BucketName) +{ + if (const ZenCacheNamespace* Namespace = FindNamespace(NamespaceName); Namespace) + { + return Namespace->GetBucketInfo(BucketName); + } + return {}; +} + ////////////////////////////////////////////////////////////////////////// #if ZEN_WITH_TESTS diff --git a/zenserver/cache/structuredcachestore.h b/zenserver/cache/structuredcachestore.h index e6b849c0a..e6e9942bb 100644 --- a/zenserver/cache/structuredcachestore.h +++ b/zenserver/cache/structuredcachestore.h @@ -143,6 +143,26 @@ static_assert(sizeof(DiskIndexEntry) == 32); class ZenCacheMemoryLayer { public: + struct Configuration + { + uint64_t TargetFootprintBytes = 16 * 1024 * 1024; + uint64_t ScavengeThreshold = 4 * 1024 * 1024; + }; + + struct BucketInfo + { + uint64_t EntryCount = 0; + uint64_t TotalSize = 0; + }; + + struct Info + { + Configuration Config; + std::vector<std::string> BucketNames; + uint64_t EntryCount = 0; + uint64_t TotalSize = 0; + }; + ZenCacheMemoryLayer(); ~ZenCacheMemoryLayer(); @@ -155,11 +175,8 @@ public: void Reset(); uint64_t TotalSize() const; - struct Configuration - { - uint64_t TargetFootprintBytes = 16 * 1024 * 1024; - uint64_t ScavengeThreshold = 4 * 1024 * 1024; - }; + Info GetInfo() const; + std::optional<BucketInfo> GetBucketInfo(std::string_view Bucket) const; const Configuration& GetConfiguration() const { return m_Configuration; } void SetConfiguration(const Configuration& NewConfig) { m_Configuration = NewConfig; } @@ -186,7 +203,7 @@ private: } }; - RwLock m_BucketLock; + mutable RwLock m_BucketLock; tsl::robin_map<IoHash, BucketValue> m_CacheMap; std::atomic_uint64_t m_TotalSize{}; @@ -196,6 +213,7 @@ private: void Scrub(ScrubContext& Ctx); void GatherAccessTimes(std::vector<zen::access_tracking::KeyAccessTime>& AccessTimes); inline uint64_t TotalSize() const { return m_TotalSize; } + uint64_t EntryCount() const; }; mutable RwLock m_Lock; @@ -210,6 +228,25 @@ private: class ZenCacheDiskLayer { public: + struct Configuration + { + std::filesystem::path RootDir; + }; + + struct BucketInfo + { + uint64_t EntryCount = 0; + uint64_t TotalSize = 0; + }; + + struct Info + { + Configuration Config; + std::vector<std::string> BucketNames; + uint64_t EntryCount = 0; + uint64_t TotalSize = 0; + }; + explicit ZenCacheDiskLayer(const std::filesystem::path& RootDir); ~ZenCacheDiskLayer(); @@ -222,10 +259,14 @@ public: void GatherReferences(GcContext& GcCtx); void CollectGarbage(GcContext& GcCtx); void UpdateAccessTimes(const zen::access_tracking::AccessTimes& AccessTimes); + // void IterateBuckets(const std::function<void(std::string_view Bucket)>& Callback) const; void DiscoverBuckets(); uint64_t TotalSize() const; + Info GetInfo() const; + std::optional<BucketInfo> GetBucketInfo(std::string_view Bucket) const; + private: /** A cache bucket manages a single directory containing metadata and data for that bucket @@ -246,6 +287,7 @@ private: void UpdateAccessTimes(const std::vector<zen::access_tracking::KeyAccessTime>& AccessTimes); inline uint64_t TotalSize() const { return m_TotalStandaloneSize.load(std::memory_order::relaxed) + m_BlockStore.TotalSize(); } + uint64_t EntryCount() const; private: const uint64_t MaxBlockSize = 1ull << 30; @@ -286,8 +328,8 @@ private: using IndexMap = tsl::robin_map<IoHash, IndexEntry, IoHash::Hasher>; - RwLock m_IndexLock; - IndexMap m_Index; + mutable RwLock m_IndexLock; + IndexMap m_Index; std::atomic_uint64_t m_TotalStandaloneSize{}; @@ -325,19 +367,39 @@ private: class ZenCacheNamespace final : public RefCounted, public GcStorage, public GcContributor { public: + struct Configuration + { + std::filesystem::path RootDir; + uint64_t DiskLayerThreshold = 0; + }; + struct BucketInfo + { + ZenCacheDiskLayer::BucketInfo DiskLayerInfo; + ZenCacheMemoryLayer::BucketInfo MemoryLayerInfo; + }; + struct Info + { + Configuration Config; + std::vector<std::string> BucketNames; + ZenCacheDiskLayer::Info DiskLayerInfo; + ZenCacheMemoryLayer::Info MemoryLayerInfo; + }; + ZenCacheNamespace(GcManager& Gc, const std::filesystem::path& RootDir); ~ZenCacheNamespace(); - bool Get(std::string_view Bucket, const IoHash& HashKey, ZenCacheValue& OutValue); - void Put(std::string_view Bucket, const IoHash& HashKey, const ZenCacheValue& Value); - bool Drop(); - bool DropBucket(std::string_view Bucket); - void Flush(); - void Scrub(ScrubContext& Ctx); - uint64_t DiskLayerThreshold() const { return m_DiskLayerSizeThreshold; } - virtual void GatherReferences(GcContext& GcCtx) override; - virtual void CollectGarbage(GcContext& GcCtx) override; - virtual GcStorageSize StorageSize() const override; + bool Get(std::string_view Bucket, const IoHash& HashKey, ZenCacheValue& OutValue); + void Put(std::string_view Bucket, const IoHash& HashKey, const ZenCacheValue& Value); + bool Drop(); + bool DropBucket(std::string_view Bucket); + void Flush(); + void Scrub(ScrubContext& Ctx); + uint64_t DiskLayerThreshold() const { return m_DiskLayerSizeThreshold; } + virtual void GatherReferences(GcContext& GcCtx) override; + virtual void CollectGarbage(GcContext& GcCtx) override; + virtual GcStorageSize StorageSize() const override; + Info GetInfo() const; + std::optional<BucketInfo> GetBucketInfo(std::string_view Bucket) const; private: std::filesystem::path m_RootDir; @@ -364,7 +426,16 @@ public: struct Configuration { std::filesystem::path BasePath; - bool AllowAutomaticCreationOfNamespaces = true; + bool AllowAutomaticCreationOfNamespaces = false; + }; + + struct Info + { + Configuration Config; + std::vector<std::string> NamespaceNames; + uint64_t DiskEntryCount = 0; + uint64_t MemoryEntryCount = 0; + GcStorageSize StorageSize; }; ZenCacheStore(GcManager& Gc, const Configuration& Configuration); @@ -378,10 +449,16 @@ public: void Scrub(ScrubContext& Ctx); GcStorageSize StorageSize() const; + // const Configuration& GetConfiguration() const { return m_Configuration; } + + Info GetInfo() const; + std::optional<ZenCacheNamespace::Info> GetNamespaceInfo(std::string_view Namespace); + std::optional<ZenCacheNamespace::BucketInfo> GetBucketInfo(std::string_view Namespace, std::string_view Bucket); private: - ZenCacheNamespace* GetNamespace(std::string_view Namespace); - void IterateNamespaces(const std::function<void(std::string_view Namespace, ZenCacheNamespace& Store)>& Callback) const; + const ZenCacheNamespace* FindNamespace(std::string_view Namespace) const; + ZenCacheNamespace* GetNamespace(std::string_view Namespace); + void IterateNamespaces(const std::function<void(std::string_view Namespace, ZenCacheNamespace& Store)>& Callback) const; typedef std::unordered_map<std::string, std::unique_ptr<ZenCacheNamespace>> NamespaceMap; diff --git a/zenserver/projectstore.cpp b/zenserver/projectstore.cpp index 5c7de2a43..d1033dea1 100644 --- a/zenserver/projectstore.cpp +++ b/zenserver/projectstore.cpp @@ -1717,8 +1717,11 @@ HttpProjectService::HttpProjectService(CidStore& Store, ProjectStore* Projects) m_Router.AddPattern("chunk", "([[:xdigit:]]{24})"); m_Router.AddPattern("hash", "([[:xdigit:]]{40})"); - // This would ideally just be the response for the root /prj endpoint but this is - // currently not possible for (arbitrary, external) technical reasons + m_Router.RegisterRoute( + "", + [this](HttpRouterRequest& Req) { Req.ServerRequest().WriteResponse(HttpResponseCode::OK, m_ProjectStore->GetProjectsList()); }, + HttpVerb::kGet); + m_Router.RegisterRoute( "list", [this](HttpRouterRequest& Req) { Req.ServerRequest().WriteResponse(HttpResponseCode::OK, m_ProjectStore->GetProjectsList()); }, @@ -2485,7 +2488,9 @@ HttpProjectService::HttpProjectService(CidStore& Store, ProjectStore* Projects) ProjectStore::Oplog& Log = *OplogIt; CbObjectWriter Cb; - Cb << "id"sv << Log.OplogId() << "project"sv << Project->Identifier << "tempdir"sv << Log.TempPath().c_str(); + Cb << "id"sv << Log.OplogId() << "project"sv << Project->Identifier << "tempdir"sv << Log.TempPath().c_str() + << "markerpath"sv << Log.MarkerPath().c_str() << "totalsize"sv << Log.TotalSize() << "opcount" + << Log.OplogCount() << "expired"sv << Log.IsExpired(); Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Cb.Save()); } diff --git a/zenserver/projectstore.h b/zenserver/projectstore.h index 8cebc5f6c..f30845fd1 100644 --- a/zenserver/projectstore.h +++ b/zenserver/projectstore.h @@ -108,6 +108,7 @@ public: const std::string& OplogId() const { return m_OplogId; } const std::filesystem::path& TempPath() const { return m_TempPath; } + const std::filesystem::path& MarkerPath() const { return m_MarkerPath; } spdlog::logger& Log() { return m_OuterProject->Log(); } void Flush(); |