osb/source/core/StarTtlCache.hpp
2024-02-25 15:46:47 +01:00

201 lines
6.2 KiB
C++

#pragma once
#include "StarLruCache.hpp"
#include "StarTime.hpp"
#include "StarRandom.hpp"
namespace Star {
template <typename LruCacheType>
class TtlCacheBase {
public:
typedef typename LruCacheType::Key Key;
typedef typename LruCacheType::Value::second_type Value;
typedef function<Value(Key const&)> ProducerFunction;
TtlCacheBase(int64_t timeToLive = 10000, int timeSmear = 1000, size_t maxSize = NPos, bool ttlUpdateEnabled = true);
int64_t timeToLive() const;
void setTimeToLive(int64_t timeToLive);
int timeSmear() const;
void setTimeSmear(int timeSmear);
// If a max size is set, this cache also acts as an LRU cache with the given
// maximum size.
size_t maxSize() const;
void setMaxSize(size_t maxSize = NPos);
size_t currentSize() const;
List<Key> keys() const;
List<Value> values() const;
// If ttlUpdateEnabled is false, then the time to live for entries will not
// be updated on access.
bool ttlUpdateEnabled() const;
void setTtlUpdateEnabled(bool enabled);
// If the value is in the cache, returns it and updates the access time,
// otherwise returns nullptr.
Value* ptr(Key const& key);
// Put the given value into the cache.
void set(Key const& key, Value value);
// Removes the given value from the cache. If found and removed, returns
// true.
bool remove(Key const& key);
// Remove all key / value pairs matching a filter.
void removeWhere(function<bool(Key const&, Value&)> filter);
// If the value for the key is not found in the cache, produce it with the
// given producer. Producer should take the key as an argument and return
// the Value.
template <typename Producer>
Value& get(Key const& key, Producer producer);
void clear();
// Cleanup any cached entries that are older than their time to live, if the
// refreshFilter is given, things that match the refreshFilter instead have
// their ttl refreshed rather than being removed.
void cleanup(function<bool(Key const&, Value const&)> refreshFilter = {});
private:
LruCacheType m_cache;
int64_t m_timeToLive;
int m_timeSmear;
bool m_ttlUpdateEnabled;
};
template <typename Key, typename Value, typename Compare = std::less<Key>, typename Allocator = BlockAllocator<pair<Key const, pair<int64_t, Value>>, 1024>>
using TtlCache = TtlCacheBase<LruCache<Key, pair<int64_t, Value>, Compare, Allocator>>;
template <typename Key, typename Value, typename Hash = Star::hash<Key>, typename Equals = std::equal_to<Key>, typename Allocator = BlockAllocator<pair<Key const, pair<int64_t, Value>>, 1024>>
using HashTtlCache = TtlCacheBase<HashLruCache<Key, pair<int64_t, Value>, Hash, Equals, Allocator>>;
template <typename LruCacheType>
TtlCacheBase<LruCacheType>::TtlCacheBase(int64_t timeToLive, int timeSmear, size_t maxSize, bool ttlUpdateEnabled) {
m_cache.setMaxSize(maxSize);
m_timeToLive = timeToLive;
m_timeSmear = timeSmear;
m_ttlUpdateEnabled = ttlUpdateEnabled;
}
template <typename LruCacheType>
int64_t TtlCacheBase<LruCacheType>::timeToLive() const {
return m_timeToLive;
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::setTimeToLive(int64_t timeToLive) {
m_timeToLive = timeToLive;
}
template <typename LruCacheType>
int TtlCacheBase<LruCacheType>::timeSmear() const {
return m_timeSmear;
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::setTimeSmear(int timeSmear) {
m_timeSmear = timeSmear;
}
template <typename LruCacheType>
bool TtlCacheBase<LruCacheType>::ttlUpdateEnabled() const {
return m_ttlUpdateEnabled;
}
template <typename LruCacheType>
size_t TtlCacheBase<LruCacheType>::maxSize() const {
return m_cache.maxSize();
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::setMaxSize(size_t maxSize) {
m_cache.setMaxSize(maxSize);
}
template <typename LruCacheType>
size_t TtlCacheBase<LruCacheType>::currentSize() const {
return m_cache.currentSize();
}
template <typename LruCacheType>
auto TtlCacheBase<LruCacheType>::keys() const -> List<Key> {
return m_cache.keys();
}
template <typename LruCacheType>
auto TtlCacheBase<LruCacheType>::values() const -> List<Value> {
List<Value> values;
for (auto& p : m_cache.values())
values.append(std::move(p.second));
return values;
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::setTtlUpdateEnabled(bool enabled) {
m_ttlUpdateEnabled = enabled;
}
template <typename LruCacheType>
auto TtlCacheBase<LruCacheType>::ptr(Key const& key) -> Value * {
if (auto p = m_cache.ptr(key)) {
if (m_ttlUpdateEnabled)
p->first = Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear);
return &p->second;
}
return nullptr;
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::set(Key const& key, Value value) {
m_cache.set(key, make_pair(Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear), value));
}
template <typename LruCacheType>
bool TtlCacheBase<LruCacheType>::remove(Key const& key) {
return m_cache.remove(key);
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::removeWhere(function<bool(Key const&, Value&)> filter) {
m_cache.removeWhere([&filter](auto const& key, auto& value) { return filter(key, value.second); });
}
template <typename LruCacheType>
template <typename Producer>
auto TtlCacheBase<LruCacheType>::get(Key const& key, Producer producer) -> Value & {
auto& value = m_cache.get(key, [producer](Key const& key) {
return pair<int64_t, Value>(0, producer(key));
});
if (value.first == 0 || m_ttlUpdateEnabled)
value.first = Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear);
return value.second;
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::clear() {
m_cache.clear();
}
template <typename LruCacheType>
void TtlCacheBase<LruCacheType>::cleanup(function<bool(Key const&, Value const&)> refreshFilter) {
int64_t currentTime = Time::monotonicMilliseconds();
m_cache.removeWhere([&](auto const& key, auto& value) {
if (refreshFilter && refreshFilter(key, value.second)) {
value.first = currentTime;
} else {
if (currentTime - value.first > m_timeToLive)
return true;
}
return false;
});
}
}