#ifndef STAR_TTL_CACHE_HPP #define STAR_TTL_CACHE_HPP #include "StarLruCache.hpp" #include "StarTime.hpp" #include "StarRandom.hpp" namespace Star { template class TtlCacheBase { public: typedef typename LruCacheType::Key Key; typedef typename LruCacheType::Value::second_type Value; typedef function 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 keys() const; List 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 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 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 refreshFilter = {}); private: LruCacheType m_cache; int64_t m_timeToLive; int m_timeSmear; bool m_ttlUpdateEnabled; }; template , typename Allocator = BlockAllocator>, 1024>> using TtlCache = TtlCacheBase, Compare, Allocator>>; template , typename Equals = std::equal_to, typename Allocator = BlockAllocator>, 1024>> using HashTtlCache = TtlCacheBase, Hash, Equals, Allocator>>; template TtlCacheBase::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 int64_t TtlCacheBase::timeToLive() const { return m_timeToLive; } template void TtlCacheBase::setTimeToLive(int64_t timeToLive) { m_timeToLive = timeToLive; } template int TtlCacheBase::timeSmear() const { return m_timeSmear; } template void TtlCacheBase::setTimeSmear(int timeSmear) { m_timeSmear = timeSmear; } template bool TtlCacheBase::ttlUpdateEnabled() const { return m_ttlUpdateEnabled; } template size_t TtlCacheBase::maxSize() const { return m_cache.maxSize(); } template void TtlCacheBase::setMaxSize(size_t maxSize) { m_cache.setMaxSize(maxSize); } template size_t TtlCacheBase::currentSize() const { return m_cache.currentSize(); } template auto TtlCacheBase::keys() const -> List { return m_cache.keys(); } template auto TtlCacheBase::values() const -> List { List values; for (auto& p : m_cache.values()) values.append(std::move(p.second)); return values; } template void TtlCacheBase::setTtlUpdateEnabled(bool enabled) { m_ttlUpdateEnabled = enabled; } template auto TtlCacheBase::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 void TtlCacheBase::set(Key const& key, Value value) { m_cache.set(key, make_pair(Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear), value)); } template bool TtlCacheBase::remove(Key const& key) { return m_cache.remove(key); } template void TtlCacheBase::removeWhere(function filter) { m_cache.removeWhere([&filter](auto const& key, auto& value) { return filter(key, value.second); }); } template template auto TtlCacheBase::get(Key const& key, Producer producer) -> Value & { auto& value = m_cache.get(key, [producer](Key const& key) { return pair(0, producer(key)); }); if (value.first == 0 || m_ttlUpdateEnabled) value.first = Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear); return value.second; } template void TtlCacheBase::clear() { m_cache.clear(); } template void TtlCacheBase::cleanup(function 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; }); } } #endif