1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
|
#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;
});
}
}
|