#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; }); } }