aboutsummaryrefslogtreecommitdiff
path: root/zenutil
diff options
context:
space:
mode:
authorPer Larsson <[email protected]>2022-01-28 13:07:36 +0100
committerPer Larsson <[email protected]>2022-01-28 13:07:36 +0100
commitbd43839e042425d72b584b33c7dbb86dabc95e12 (patch)
tree1e663395ac626f3863ef92e95952b3c4245abf76 /zenutil
parentGet access token from auth mgr. (diff)
parentCompile fix (diff)
downloadzen-bd43839e042425d72b584b33c7dbb86dabc95e12.tar.xz
zen-bd43839e042425d72b584b33c7dbb86dabc95e12.zip
Merged main.
Diffstat (limited to 'zenutil')
-rw-r--r--zenutil/cache/cachepolicy.cpp299
-rw-r--r--zenutil/include/zenutil/cache/cachekey.h6
-rw-r--r--zenutil/include/zenutil/cache/cachepolicy.h141
-rw-r--r--zenutil/include/zenutil/zenserverprocess.h9
-rw-r--r--zenutil/zenserverprocess.cpp28
5 files changed, 322 insertions, 161 deletions
diff --git a/zenutil/cache/cachepolicy.cpp b/zenutil/cache/cachepolicy.cpp
index e1c31d885..3bf7a0c67 100644
--- a/zenutil/cache/cachepolicy.cpp
+++ b/zenutil/cache/cachepolicy.cpp
@@ -4,64 +4,115 @@
#include <zencore/compactbinary.h>
#include <zencore/compactbinarybuilder.h>
+#include <zencore/enumflags.h>
#include <zencore/string.h>
+#include <algorithm>
+#include <unordered_map>
+
namespace zen {
using namespace std::literals;
-namespace detail { namespace cacheopt {
- constexpr std::string_view Local = "local"sv;
- constexpr std::string_view Remote = "remote"sv;
- constexpr std::string_view Data = "data"sv;
- constexpr std::string_view Meta = "meta"sv;
- constexpr std::string_view Value = "value"sv;
- constexpr std::string_view Attachments = "attachments"sv;
-}} // namespace detail::cacheopt
+namespace detail::CachePolicyImpl {
+ constexpr char DelimiterChar = ',';
+ constexpr std::string_view None = "None"sv;
+ constexpr std::string_view QueryLocal = "QueryLocal"sv;
+ constexpr std::string_view QueryRemote = "QueryRemote"sv;
+ constexpr std::string_view Query = "Query"sv;
+ constexpr std::string_view StoreLocal = "StoreLocal"sv;
+ constexpr std::string_view StoreRemote = "StoreRemote"sv;
+ constexpr std::string_view Store = "Store"sv;
+ constexpr std::string_view SkipMeta = "SkipMeta"sv;
+ constexpr std::string_view SkipData = "SkipData"sv;
+ constexpr std::string_view PartialRecord = "PartialRecord"sv;
+ constexpr std::string_view KeepAlive = "KeepAlive"sv;
+ constexpr std::string_view Local = "Local"sv;
+ constexpr std::string_view Remote = "Remote"sv;
+ constexpr std::string_view Default = "Default"sv;
+ constexpr std::string_view Disable = "Disable"sv;
-CachePolicy
-ParseQueryCachePolicy(std::string_view QueryPolicy, CachePolicy Default)
-{
- if (QueryPolicy.empty())
- {
- return Default;
- }
+ using TextToPolicyMap = std::unordered_map<std::string_view, CachePolicy>;
+ const TextToPolicyMap TextToPolicy = {{None, CachePolicy::None},
+ {QueryLocal, CachePolicy::QueryLocal},
+ {QueryRemote, CachePolicy::QueryRemote},
+ {Query, CachePolicy::Query},
+ {StoreLocal, CachePolicy::StoreLocal},
+ {StoreRemote, CachePolicy::StoreRemote},
+ {Store, CachePolicy::Store},
+ {SkipMeta, CachePolicy::SkipMeta},
+ {SkipData, CachePolicy::SkipData},
+ {PartialRecord, CachePolicy::PartialRecord},
+ {KeepAlive, CachePolicy::KeepAlive},
+ {Local, CachePolicy::Local},
+ {Remote, CachePolicy::Remote},
+ {Default, CachePolicy::Default},
+ {Disable, CachePolicy::Disable}};
- CachePolicy Result = CachePolicy::None;
+ using PolicyTextPair = std::pair<CachePolicy, std::string_view>;
+ const PolicyTextPair FlagsToString[]{
+ // Order of these Flags is important: we want the aliases before the atomic values,
+ // and the bigger aliases first, to reduce the number of tokens we add
+ {CachePolicy::Default, Default},
+ {CachePolicy::Remote, Remote},
+ {CachePolicy::Local, Local},
+ {CachePolicy::Store, Store},
+ {CachePolicy::Query, Query},
- ForEachStrTok(QueryPolicy, ',', [&Result](const std::string_view& Token) {
- if (Token == detail::cacheopt::Local)
- {
- Result |= CachePolicy::QueryLocal;
- }
- if (Token == detail::cacheopt::Remote)
+ // Order of Atomics doesn't matter, so arbitrarily we list them in enum order
+ {CachePolicy::QueryLocal, QueryLocal},
+ {CachePolicy::QueryRemote, QueryRemote},
+ {CachePolicy::StoreLocal, StoreLocal},
+ {CachePolicy::StoreRemote, StoreRemote},
+ {CachePolicy::SkipMeta, SkipMeta},
+ {CachePolicy::SkipData, SkipData},
+ {CachePolicy::PartialRecord, PartialRecord},
+ {CachePolicy::KeepAlive, KeepAlive},
+
+ // None must come at the end of the array, to write out only if no others exist
+ {CachePolicy::None, None},
+ };
+ constexpr CachePolicy KnownFlags =
+ CachePolicy::Default | CachePolicy::SkipMeta | CachePolicy::SkipData | CachePolicy::KeepAlive | CachePolicy::PartialRecord;
+} // namespace detail::CachePolicyImpl
+
+StringBuilderBase&
+AppendToBuilderImpl(StringBuilderBase& Builder, CachePolicy Policy)
+{
+ // Remove any bits we don't recognize; write None if there are not any bits we recognize
+ Policy = Policy & detail::CachePolicyImpl::KnownFlags;
+ for (const detail::CachePolicyImpl::PolicyTextPair& Pair : detail::CachePolicyImpl::FlagsToString)
+ {
+ if (EnumHasAllFlags(Policy, Pair.first))
{
- Result |= CachePolicy::QueryRemote;
+ EnumRemoveFlags(Policy, Pair.first);
+ Builder << Pair.second << detail::CachePolicyImpl::DelimiterChar;
+ if (Policy == CachePolicy::None)
+ {
+ break;
+ }
}
- return true;
- });
-
- return Result;
+ }
+ Builder.RemoveSuffix(1); // Text will have been added by CachePolicy::None if not by anything else
+ return Builder;
+}
+StringBuilderBase&
+operator<<(StringBuilderBase& Builder, CachePolicy Policy)
+{
+ return AppendToBuilderImpl(Builder, Policy);
}
CachePolicy
-ParseStoreCachePolicy(std::string_view StorePolicy, CachePolicy Default)
+ParseCachePolicy(std::string_view Text)
{
- if (StorePolicy.empty())
- {
- return Default;
- }
+ ZEN_ASSERT(!Text.empty()); // Empty string is not valid input to ParseCachePolicy
CachePolicy Result = CachePolicy::None;
-
- ForEachStrTok(StorePolicy, ',', [&Result](const std::string_view& Token) {
- if (Token == detail::cacheopt::Local)
- {
- Result |= CachePolicy::StoreLocal;
- }
- if (Token == detail::cacheopt::Remote)
+ ForEachStrTok(Text, detail::CachePolicyImpl::DelimiterChar, [&Result](const std::string_view& Token) {
+ auto it = detail::CachePolicyImpl::TextToPolicy.find(Token);
+ if (it != detail::CachePolicyImpl::TextToPolicy.end())
{
- Result |= CachePolicy::StoreRemote;
+ Result |= it->second;
}
return true;
});
@@ -69,101 +120,139 @@ ParseStoreCachePolicy(std::string_view StorePolicy, CachePolicy Default)
return Result;
}
-CachePolicy
-ParseSkipCachePolicy(std::string_view SkipPolicy, CachePolicy Default)
-{
- if (SkipPolicy.empty())
+namespace Private {
+
+ class CacheRecordPolicyShared final : public ICacheRecordPolicyShared
{
- return Default;
- }
+ public:
+ inline std::span<const CacheValuePolicy> GetValuePolicies() const final { return Values; }
- CachePolicy Result = CachePolicy::None;
+ inline void AddValuePolicy(const CacheValuePolicy& Policy) final { Values.push_back(Policy); }
- ForEachStrTok(SkipPolicy, ',', [&Result](const std::string_view& Token) {
- if (Token == detail::cacheopt::Meta)
- {
- Result |= CachePolicy::SkipMeta;
- }
- if (Token == detail::cacheopt::Value)
+ inline void Build() final
{
- Result |= CachePolicy::SkipValue;
+ std::sort(Values.begin(), Values.end(), [](const CacheValuePolicy& A, const CacheValuePolicy& B) { return A.Id < B.Id; });
}
- if (Token == detail::cacheopt::Attachments)
- {
- Result |= CachePolicy::SkipAttachments;
- }
- if (Token == detail::cacheopt::Data)
- {
- Result |= CachePolicy::SkipData;
- }
- return true;
- });
- return Result;
-}
+ private:
+ std::vector<CacheValuePolicy> Values;
+ };
-CacheRecordPolicy::CacheRecordPolicy(const CachePolicy RecordPolicy, const CachePolicy PayloadPolicy)
-: m_RecordPolicy(RecordPolicy)
-, m_DefaultPayloadPolicy(PayloadPolicy)
-{
-}
+} // namespace Private
CachePolicy
-CacheRecordPolicy::GetPayloadPolicy(const Oid& PayloadId) const
+CacheRecordPolicy::GetValuePolicy(const Oid& Id) const
{
- if (const auto It = m_PayloadPolicies.find(PayloadId); It != m_PayloadPolicies.end())
+ if (Shared)
{
- return It->second;
+ if (std::span<const CacheValuePolicy> Values = Shared->GetValuePolicies(); !Values.empty())
+ {
+ auto Iter =
+ std::lower_bound(Values.begin(), Values.end(), Id, [](const CacheValuePolicy& A, const Oid& B) { return A.Id < B; });
+ if (Iter != Values.end() && Iter->Id == Id)
+ {
+ return Iter->Policy;
+ }
+ }
}
+ return DefaultValuePolicy;
+}
- return m_DefaultPayloadPolicy;
+void
+CacheRecordPolicy::Save(CbWriter& Writer) const
+{
+ Writer.BeginObject();
+ {
+ // The RecordPolicy is calculated from the ValuePolicies and does not need to be saved separately.
+ Writer << "DefaultValuePolicy"sv << WriteToString<128>(GetDefaultValuePolicy());
+ if (!IsUniform())
+ {
+ // FCacheRecordPolicyBuilder guarantees IsUniform -> non-empty GetValuePolicies. Small size penalty here if not.
+ Writer.BeginArray("ValuePolicies"sv);
+ {
+ for (const CacheValuePolicy& ValuePolicy : GetValuePolicies())
+ {
+ // FCacheRecordPolicyBuilder is responsible for ensuring that each ValuePolicy != DefaultValuePolicy
+ // If it lets any duplicates through we will incur a small serialization size penalty here
+ Writer.BeginObject();
+ Writer << "Id"sv << ValuePolicy.Id;
+ Writer << "Policy"sv << WriteToString<128>(ValuePolicy.Policy);
+ Writer.EndObject();
+ }
+ }
+ Writer.EndArray();
+ }
+ }
+ Writer.EndObject();
}
-bool
-CacheRecordPolicy::Load(CbObjectView RecordPolicyObject, CacheRecordPolicy& OutRecordPolicy)
+CacheRecordPolicy
+CacheRecordPolicy::Load(CbObjectView Object, CachePolicy DefaultPolicy)
{
- using namespace std::literals;
+ std::string_view PolicyText = Object["DefaultValuePolicy"sv].AsString();
+ CachePolicy DefaultValuePolicy = !PolicyText.empty() ? ParseCachePolicy(PolicyText) : DefaultPolicy;
- const uint32_t RecordPolicy = RecordPolicyObject["RecordPolicy"sv].AsUInt32(static_cast<uint32_t>(CachePolicy::Default));
- const uint32_t DefaultPayloadPolicy =
- RecordPolicyObject["DefaultPayloadPolicy"sv].AsUInt32(static_cast<uint32_t>(CachePolicy::Default));
+ CacheRecordPolicyBuilder Builder(DefaultValuePolicy);
+ for (CbFieldView ValueObjectField : Object["ValuePolicies"sv])
+ {
+ CbObjectView ValueObject = ValueObjectField.AsObjectView();
+ const Oid ValueId = ValueObject["Id"sv].AsObjectId();
+ PolicyText = ValueObject["Policy"sv].AsString();
+ CachePolicy ValuePolicy = !PolicyText.empty() ? ParseCachePolicy(PolicyText) : DefaultValuePolicy;
+ // FCacheRecordPolicyBuilder should guarantee that FValueId(ValueId).IsValid and ValuePolicy != DefaultValuePolicy
+ // If it lets any through we will have unused data in the record we create.
+ Builder.AddValuePolicy(ValueId, ValuePolicy);
+ }
- OutRecordPolicy.m_RecordPolicy = static_cast<CachePolicy>(RecordPolicy);
- OutRecordPolicy.m_DefaultPayloadPolicy = static_cast<CachePolicy>(DefaultPayloadPolicy);
+ return Builder.Build();
+}
- for (CbFieldView PayloadPolicyView : RecordPolicyObject["PayloadPolicies"sv])
+CacheRecordPolicy
+CacheRecordPolicy::ConvertToUpstream() const
+{
+ auto DownstreamToUpstream = [](CachePolicy P) {
+ // Remote|Local -> Set Remote
+ // Delete Skip Flags
+ // Maintain Remaining Flags
+ return (EnumHasAllFlags(P, CachePolicy::QueryRemote) ? CachePolicy::QueryLocal : CachePolicy::None) |
+ (EnumHasAllFlags(P, CachePolicy::StoreRemote) ? CachePolicy::StoreLocal : CachePolicy::None) |
+ (P & ~(CachePolicy::SkipData | CachePolicy::SkipMeta));
+ };
+ CacheRecordPolicyBuilder Builder(DownstreamToUpstream(GetDefaultValuePolicy()));
+ for (const CacheValuePolicy& ValuePolicy : GetValuePolicies())
{
- CbObjectView PayloadPolicyObject = PayloadPolicyView.AsObjectView();
- const Oid PayloadId = PayloadPolicyObject["Id"sv].AsObjectId();
- const uint32_t PayloadPolicy = PayloadPolicyObject["Policy"sv].AsUInt32();
-
- if (PayloadId != Oid::Zero && PayloadPolicy != 0)
- {
- OutRecordPolicy.m_PayloadPolicies.emplace(PayloadId, static_cast<CachePolicy>(PayloadPolicy));
- }
+ Builder.AddValuePolicy(ValuePolicy.Id, DownstreamToUpstream(ValuePolicy.Policy));
}
-
- return true;
+ return Builder.Build();
}
void
-CacheRecordPolicy::Save(const CacheRecordPolicy& Policy, CbWriter& Writer)
+CacheRecordPolicyBuilder::AddValuePolicy(const CacheValuePolicy& Policy)
{
- Writer << "RecordPolicy"sv << static_cast<uint32_t>(Policy.GetRecordPolicy());
- Writer << "DefaultPayloadPolicy"sv << static_cast<uint32_t>(Policy.GetDefaultPayloadPolicy());
+ if (!Shared)
+ {
+ Shared = new Private::CacheRecordPolicyShared;
+ }
+ Shared->AddValuePolicy(Policy);
+}
- if (!Policy.m_PayloadPolicies.empty())
+CacheRecordPolicy
+CacheRecordPolicyBuilder::Build()
+{
+ CacheRecordPolicy Policy(BasePolicy);
+ if (Shared)
{
- Writer.BeginArray("PayloadPolicies"sv);
- for (const auto& Kv : Policy.m_PayloadPolicies)
+ Shared->Build();
+ const auto PolicyOr = [](CachePolicy A, CachePolicy B) { return A | (B & ~CachePolicy::SkipData); };
+ const std::span<const CacheValuePolicy> Values = Shared->GetValuePolicies();
+ Policy.RecordPolicy = BasePolicy;
+ for (const CacheValuePolicy& ValuePolicy : Values)
{
- Writer.BeginObject();
- Writer.AddObjectId("Id"sv, Kv.first);
- Writer << "Policy"sv << static_cast<uint32_t>(Kv.second);
- Writer.EndObject();
+ Policy.RecordPolicy = PolicyOr(Policy.RecordPolicy, ValuePolicy.Policy);
}
- Writer.EndArray();
+ Policy.Shared = std::move(Shared);
}
+ return Policy;
}
} // namespace zen
diff --git a/zenutil/include/zenutil/cache/cachekey.h b/zenutil/include/zenutil/cache/cachekey.h
index fb36c7759..a0a83a883 100644
--- a/zenutil/include/zenutil/cache/cachekey.h
+++ b/zenutil/include/zenutil/cache/cachekey.h
@@ -44,7 +44,7 @@ struct CacheChunkRequest
{
CacheKey Key;
IoHash ChunkId;
- Oid PayloadId;
+ Oid ValueId;
uint64_t RawOffset = 0ull;
uint64_t RawSize = ~uint64_t(0);
CachePolicy Policy = CachePolicy::Default;
@@ -69,11 +69,11 @@ operator<(const CacheChunkRequest& A, const CacheChunkRequest& B)
{
return false;
}
- if (A.PayloadId < B.PayloadId)
+ if (A.ValueId < B.ValueId)
{
return true;
}
- if (B.PayloadId < A.PayloadId)
+ if (B.ValueId < A.ValueId)
{
return false;
}
diff --git a/zenutil/include/zenutil/cache/cachepolicy.h b/zenutil/include/zenutil/cache/cachepolicy.h
index 5675ccf4d..b3602edbd 100644
--- a/zenutil/include/zenutil/cache/cachepolicy.h
+++ b/zenutil/include/zenutil/cache/cachepolicy.h
@@ -2,10 +2,14 @@
#pragma once
+#include <zencore/compactbinary.h>
+#include <zencore/enumflags.h>
+#include <zencore/refcount.h>
#include <zencore/string.h>
#include <zencore/uid.h>
#include <gsl/gsl-lite.hpp>
+#include <span>
#include <unordered_map>
namespace zen {
@@ -34,16 +38,21 @@ enum class CachePolicy : uint32_t
/** Skip fetching the metadata for record requests. */
SkipMeta = 1 << 4,
- /** Skip fetching the value for record, chunk, or value requests. */
- SkipValue = 1 << 5,
- /** Skip fetching the attachments for record requests. */
- SkipAttachments = 1 << 6,
+ /** Skip fetching the data for values. */
+ SkipData = 1 << 5,
+
/**
- * Skip fetching the data for any requests.
+ * Partial output will be provided with the error status when a required value is missing.
+ *
+ * This is meant for cases when the missing values can be individually recovered, or rebuilt,
+ * without rebuilding the whole record. The cache automatically adds this flag when there are
+ * other cache stores that it may be able to recover missing values from.
+ *
+ * Missing values will be returned in the records or chunks, but with only the hash and size.
*
- * Put requests with skip flags may assume that record existence implies payload existence.
+ * Applying this flag for a put of a record allows a partial record to be stored.
*/
- SkipData = SkipMeta | SkipValue | SkipAttachments,
+ PartialRecord = 1 << 6,
/**
* Keep records in the cache for at least the duration of the session.
@@ -53,18 +62,6 @@ enum class CachePolicy : uint32_t
*/
KeepAlive = 1 << 7,
- /**
- * Partial output will be provided with the error status when a required payload is missing.
- *
- * This is meant for cases when the missing payloads can be individually recovered or rebuilt
- * without rebuilding the whole record. The cache automatically adds this flag when there are
- * other cache stores that it may be able to recover missing payloads from.
- *
- * Requests for records would return records where the missing payloads have a hash and size,
- * but no data. Requests for chunks or values would return the hash and size, but no data.
- */
- PartialOnError = 1 << 8,
-
/** Allow cache requests to query and store records and values in local caches. */
Local = QueryLocal | StoreLocal,
/** Allow cache requests to query and store records and values in remote caches. */
@@ -78,35 +75,107 @@ enum class CachePolicy : uint32_t
};
gsl_DEFINE_ENUM_BITMASK_OPERATORS(CachePolicy);
+/** Serialize Policy to text and append to Builder. Appended text will not be empty. */
+StringBuilderBase& operator<<(StringBuilderBase& Builder, CachePolicy Policy);
+/** Parse text written by operator<< back into an ECachePolicy. Text must not be empty. */
+CachePolicy ParseCachePolicy(std::string_view Text);
-CachePolicy ParseQueryCachePolicy(std::string_view QueryPolicy, CachePolicy Default = CachePolicy::Query);
-
-CachePolicy ParseStoreCachePolicy(std::string_view StorePolicy, CachePolicy Default = CachePolicy::Store);
-
-CachePolicy ParseSkipCachePolicy(std::string_view SkipPolicy, CachePolicy Default = CachePolicy::None);
+/** A value ID and the cache policy to use for that value. */
+struct CacheValuePolicy
+{
+ Oid Id;
+ CachePolicy Policy = CachePolicy::Default;
+};
+namespace Private {
+ /** Interface for the private implementation of the cache record policy. */
+ class ICacheRecordPolicyShared : public RefCounted
+ {
+ public:
+ virtual ~ICacheRecordPolicyShared() = default;
+ virtual std::span<const CacheValuePolicy> GetValuePolicies() const = 0;
+ virtual void AddValuePolicy(const CacheValuePolicy& Policy) = 0;
+ virtual void Build() = 0;
+ };
+} // namespace Private
+
+/**
+ * Flags to control the behavior of cache record requests, with optional overrides by value.
+ *
+ * Examples:
+ * - A base policy of Disable, with value policy overrides of Default, will fetch those values if
+ * they exist in the record, and skip data for any other values.
+ * - A base policy of Default, with value policy overrides of (Query | SkipData), will skip those
+ * values, but still check if they exist, and will load any other values.
+ */
class CacheRecordPolicy
{
public:
+ /** Construct a cache record policy that uses the default policy. */
CacheRecordPolicy() = default;
- CacheRecordPolicy(const CachePolicy RecordPolicy, const CachePolicy DefaultPayloadPolicy = CachePolicy::Default);
- CachePolicy GetRecordPolicy() const { return m_RecordPolicy; }
- CachePolicy GetPayloadPolicy(const Oid& PayloadId) const;
- CachePolicy GetDefaultPayloadPolicy() const { return m_DefaultPayloadPolicy; }
+ /** Construct a cache record policy with a uniform policy for the record and every value. */
+ inline CacheRecordPolicy(CachePolicy Policy) : RecordPolicy(Policy), DefaultValuePolicy(Policy) {}
+
+ /** Returns true if the record and every value use the same cache policy. */
+ inline bool IsUniform() const { return !Shared && RecordPolicy == DefaultValuePolicy; }
+
+ /** Returns the cache policy to use for the record. */
+ inline CachePolicy GetRecordPolicy() const { return RecordPolicy; }
- bool HasRecordPolicy(const CachePolicy Policy) const { return (m_RecordPolicy & Policy) == Policy; }
- bool HasPayloadPolicy(const Oid& PayloadId, const CachePolicy Policy) const { return (GetPayloadPolicy(PayloadId) & Policy) == Policy; }
+ /** Returns the cache policy to use for the value. */
+ CachePolicy GetValuePolicy(const Oid& Id) const;
- static bool Load(CbObjectView RecordPolicyObject, CacheRecordPolicy& OutRecordPolicy);
- static void Save(const CacheRecordPolicy& Policy, CbWriter& Writer);
+ /** Returns the cache policy to use for values with no override. */
+ inline CachePolicy GetDefaultValuePolicy() const { return DefaultValuePolicy; }
+
+ /** Returns the array of cache policy overrides for values, sorted by ID. */
+ inline std::span<const CacheValuePolicy> GetValuePolicies() const
+ {
+ return Shared ? Shared->GetValuePolicies() : std::span<const CacheValuePolicy>();
+ }
+
+ /** Save the values from *this into the given writer. */
+ void Save(CbWriter& Writer) const;
+
+ /**
+ * Returns a policy loaded from values on Object.
+ * Invalid data will result in a uniform CacheRecordPolicy with defaultValuePolicy == DefaultPolicy.
+ */
+ static CacheRecordPolicy Load(CbObjectView Object, CachePolicy DefaultPolicy = CachePolicy::Default);
+
+ /** Return *this converted into the equivalent policy that the upstream should use when forwarding a put or get to an upstream server.
+ */
+ CacheRecordPolicy ConvertToUpstream() const;
private:
- using PayloadPolicyMap = std::unordered_map<Oid, CachePolicy, Oid::Hasher>;
+ friend class CacheRecordPolicyBuilder;
+
+ CachePolicy RecordPolicy = CachePolicy::Default;
+ CachePolicy DefaultValuePolicy = CachePolicy::Default;
+ RefPtr<const Private::ICacheRecordPolicyShared> Shared;
+};
+
+/** A cache record policy builder is used to construct a cache record policy. */
+class CacheRecordPolicyBuilder
+{
+public:
+ /** Construct a policy builder that uses the default policy as its base policy. */
+ CacheRecordPolicyBuilder() = default;
- CachePolicy m_RecordPolicy = CachePolicy::Default;
- CachePolicy m_DefaultPayloadPolicy = CachePolicy::Default;
- PayloadPolicyMap m_PayloadPolicies;
+ /** Construct a policy builder that uses the provided policy for the record and values with no override. */
+ inline explicit CacheRecordPolicyBuilder(CachePolicy Policy) : BasePolicy(Policy) {}
+
+ /** Adds a cache policy override for a value. */
+ void AddValuePolicy(const CacheValuePolicy& Policy);
+ inline void AddValuePolicy(const Oid& Id, CachePolicy Policy) { AddValuePolicy({Id, Policy}); }
+
+ /** Build a cache record policy, which makes this builder subsequently unusable. */
+ CacheRecordPolicy Build();
+
+private:
+ CachePolicy BasePolicy = CachePolicy::Default;
+ RefPtr<Private::ICacheRecordPolicyShared> Shared;
};
} // namespace zen
diff --git a/zenutil/include/zenutil/zenserverprocess.h b/zenutil/include/zenutil/zenserverprocess.h
index 55b9a50cd..2a3146e2d 100644
--- a/zenutil/include/zenutil/zenserverprocess.h
+++ b/zenutil/include/zenutil/zenserverprocess.h
@@ -100,11 +100,12 @@ public:
// additional state. For example, you can use the session ID
// to introduce additional named objects
std::atomic<uint32_t> Pid;
- std::atomic<uint16_t> ListenPort;
+ std::atomic<uint16_t> DesiredListenPort;
std::atomic<uint16_t> Flags;
uint8_t SessionId[12];
std::atomic<uint32_t> SponsorPids[8];
- uint8_t Padding[12];
+ std::atomic<uint16_t> EffectiveListenPort;
+ uint8_t Padding[10];
enum class FlagsEnum : uint16_t
{
@@ -125,8 +126,8 @@ public:
void Initialize();
[[nodiscard]] bool InitializeReadOnly();
- [[nodiscard]] ZenServerEntry* Lookup(int ListenPort);
- ZenServerEntry* Register(int ListenPort);
+ [[nodiscard]] ZenServerEntry* Lookup(int DesiredListenPort);
+ ZenServerEntry* Register(int DesiredListenPort);
void Sweep();
void Snapshot(std::function<void(const ZenServerEntry&)>&& Callback);
inline bool IsReadOnly() const { return m_IsReadOnly; }
diff --git a/zenutil/zenserverprocess.cpp b/zenutil/zenserverprocess.cpp
index fe6236d18..5bddc72bc 100644
--- a/zenutil/zenserverprocess.cpp
+++ b/zenutil/zenserverprocess.cpp
@@ -230,11 +230,11 @@ ZenServerState::InitializeReadOnly()
}
ZenServerState::ZenServerEntry*
-ZenServerState::Lookup(int ListenPort)
+ZenServerState::Lookup(int DesiredListenPort)
{
for (int i = 0; i < m_MaxEntryCount; ++i)
{
- if (m_Data[i].ListenPort == ListenPort)
+ if (m_Data[i].DesiredListenPort == DesiredListenPort)
{
return &m_Data[i];
}
@@ -244,7 +244,7 @@ ZenServerState::Lookup(int ListenPort)
}
ZenServerState::ZenServerEntry*
-ZenServerState::Register(int ListenPort)
+ZenServerState::Register(int DesiredListenPort)
{
if (m_Data == nullptr)
{
@@ -259,17 +259,18 @@ ZenServerState::Register(int ListenPort)
{
ZenServerEntry& Entry = m_Data[i];
- if (Entry.ListenPort.load(std::memory_order_relaxed) == 0)
+ if (Entry.DesiredListenPort.load(std::memory_order_relaxed) == 0)
{
uint16_t Expected = 0;
- if (Entry.ListenPort.compare_exchange_strong(Expected, uint16_t(ListenPort)))
+ if (Entry.DesiredListenPort.compare_exchange_strong(Expected, uint16_t(DesiredListenPort)))
{
// Successfully allocated entry
m_OurEntry = &Entry;
- Entry.Pid = Pid;
- Entry.Flags = 0;
+ Entry.Pid = Pid;
+ Entry.EffectiveListenPort = 0;
+ Entry.Flags = 0;
const Oid SesId = GetSessionId();
memcpy(Entry.SessionId, &SesId, sizeof SesId);
@@ -296,11 +297,11 @@ ZenServerState::Sweep()
{
ZenServerEntry& Entry = m_Data[i];
- if (Entry.ListenPort)
+ if (Entry.DesiredListenPort)
{
if (IsProcessRunning(Entry.Pid) == false)
{
- ZEN_DEBUG("Sweep - pid {} not running, reclaiming entry (port {})", Entry.Pid, Entry.ListenPort);
+ ZEN_DEBUG("Sweep - pid {} not running, reclaiming entry (port {})", Entry.Pid, Entry.DesiredListenPort);
Entry.Reset();
}
@@ -320,7 +321,7 @@ ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback)
{
ZenServerEntry& Entry = m_Data[i];
- if (Entry.ListenPort)
+ if (Entry.DesiredListenPort)
{
Callback(Entry);
}
@@ -330,9 +331,10 @@ ZenServerState::Snapshot(std::function<void(const ZenServerEntry&)>&& Callback)
void
ZenServerState::ZenServerEntry::Reset()
{
- Pid = 0;
- ListenPort = 0;
- Flags = 0;
+ Pid = 0;
+ DesiredListenPort = 0;
+ Flags = 0;
+ EffectiveListenPort = 0;
}
void