Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion include/xrpl/basics/TaggedCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <type_traits>
Expand Down Expand Up @@ -74,11 +75,25 @@ class TaggedCache
using shared_pointer_type = SharedPointerType;

public:
/**
* A byte budget for the strongly-cached entries. Each entry is charged
* `cost(ptr)` bytes when it becomes strong; growth past `bytes` evicts
* like the entry cap. The cost should include the value's heap payload
* plus per-entry overhead.
*/
struct ByteBudget
{
std::size_t bytes = 0;
std::function<std::size_t(SharedPointerType const&)> cost;
};

/**
* @param cacheHardCap When positive, a hard upper bound on the number of
* strongly-cached entries, enforced by demoting the approximately
* oldest entry whenever growth would exceed it. 0 disables the cap
* (the periodic sweep alone bounds the cache).
* @param byteBudget When set, a hard upper bound on the charged bytes of
* strongly-cached entries, enforced the same way.
*/
TaggedCache(
std::string const& name,
Expand All @@ -87,7 +102,8 @@ class TaggedCache
clock_type& clock,
beast::Journal journal,
beast::insight::Collector::ptr const& collector = beast::insight::NullCollector::make(),
int cacheHardCap = 0);
int cacheHardCap = 0,
std::optional<ByteBudget> byteBudget = std::nullopt);

public:
/**
Expand All @@ -108,6 +124,13 @@ class TaggedCache
int
getTrackSize() const;

/**
* Returns the charged bytes of strongly-cached entries; 0 unless a
* byte budget with a cost function is configured.
*/
std::size_t
getCacheBytes() const;

float
getHitRate();

Expand Down Expand Up @@ -322,6 +345,10 @@ class TaggedCache
shared_weak_combo_pointer_type ptr;
clock_type::time_point lastAccess;

// Bytes charged against the byte budget while strong; 0 when weak
// or when no budget is configured.
std::uint32_t costBytes{0};

ValueEntry(clock_type::time_point const& lastAccess, shared_pointer_type const& ptr)
: ptr(ptr), lastAccess(lastAccess)
{
Expand Down Expand Up @@ -378,6 +405,7 @@ class TaggedCache
KeyValueCacheType::map_type& partition,
SweptPointersVector& stuffToSweep,
std::atomic<int>& allRemovals,
std::atomic<std::uint64_t>& allBytesRemoved,
std::scoped_lock<std::recursive_mutex> const&);

[[nodiscard]] std::thread
Expand All @@ -387,6 +415,7 @@ class TaggedCache
KeyOnlyCacheType::map_type& partition,
SweptPointersVector&,
std::atomic<int>& allRemovals,
std::atomic<std::uint64_t>& allBytesRemoved,
std::scoped_lock<std::recursive_mutex> const&);

beast::Journal journal_;
Expand All @@ -409,13 +438,31 @@ class TaggedCache
// weak-to-strong revivals). 0 disables it (sweep-only sizing).
int const cacheHardCap_;

// Byte-denominated bound on strongly-cached entries, enforced the same
// way; each entry is charged by the budget's cost function while strong.
std::optional<ByteBudget> const byteBudget_;

// Charged bytes of strongly-cached entries (under mutex_).
std::size_t cacheBytes_{0};

// Total hard-cap evictions (under mutex_); the first marks saturation
// onset for logging.
std::uint64_t hardCapEvictions_{0};

// Number of items cached
int cacheCount_{0};

// Charge or release an entry's bytes against the byte budget. No-ops
// without a configured budget; callers hold mutex_.
void
chargeEntry(ValueEntry& entry, SharedPointerType const& ptr);
void
dischargeEntry(ValueEntry& entry);

// True when either the entry cap or the byte budget is exceeded.
[[nodiscard]] bool
overHardCap() const;

// Rotating bucket cursor for evictForHardCap so successive over-cap
// evictions sweep the whole partition (CLOCK hand) instead of repeatedly
// sampling the head buckets. Advanced under mutex_.
Expand Down
Loading