aboutsummaryrefslogtreecommitdiff
path: root/zenserver
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2023-01-11 23:52:55 -0800
committerGitHub <[email protected]>2023-01-11 23:52:55 -0800
commita24820cbbc3c032f2cfc7b4c3bf9cac0bbaaeb6c (patch)
tree4c756826edc1839cdf894a995a8dc4e927f6bb65 /zenserver
parent0.2.1 (diff)
downloadzen-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.cpp160
-rw-r--r--zenserver/cache/structuredcache.h25
-rw-r--r--zenserver/cache/structuredcachestore.cpp159
-rw-r--r--zenserver/cache/structuredcachestore.h119
-rw-r--r--zenserver/projectstore.cpp11
-rw-r--r--zenserver/projectstore.h1
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();