Files
scylladb/utils/loading_cache.hh
Calle Wilund ead1c882f8 utils::loading_cache: Version of the LoadingCache type used in origin
Simple, expiring, cache of potentially limited number of entries.
2016-04-19 11:49:05 +00:00

134 lines
4.3 KiB
C++

/*
* Copyright (C) 2016 ScyllaDB
*/
/*
* This file is part of Scylla.
*
* Scylla is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Scylla is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Scylla. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <chrono>
#include <unordered_map>
#include <seastar/core/timer.hh>
namespace utils {
// Simple variant of the "LoadingCache" used for permissions in origin.
typedef steady_clock_type loading_cache_clock_type;
template<typename _Tp>
struct timestamped_val {
_Tp value;
typename loading_cache_clock_type::time_point loaded;
};
template<typename _Key, typename _Tp, typename _Hash = std::hash<_Key>,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, timestamped_val<_Tp>> > >
class loading_cache {
private:
typedef timestamped_val<_Tp> ts_value_type;
typedef std::unordered_map<_Key, ts_value_type, _Hash, _Pred, _Alloc> map_type;
typedef loading_cache<_Key, _Tp, _Hash, _Pred, _Alloc> _MyType;
public:
typedef _Tp value_type;
typedef typename map_type::key_type key_type;
typedef typename map_type::allocator_type allocator_type;
typedef typename map_type::hasher hasher;
typedef typename map_type::key_equal key_equal;
template<typename Func>
loading_cache(size_t max_size, std::chrono::milliseconds expiry,
std::chrono::milliseconds refresh, Func && load,
const hasher& hf = hasher(), const key_equal& eql =
key_equal(), const allocator_type& a =
allocator_type())
: _map(10, hf, eql, a), _max_size(max_size), _expiry(
expiry), _refresh(refresh), _load(
std::forward<Func>(load)) {
auto period = _expiry;
if (expiry == std::chrono::milliseconds() && _max_size != 0) {
period = std::chrono::milliseconds(5000);
}
if (period != std::chrono::milliseconds()) {
_timer.set_callback(std::bind(&_MyType::on_timer, this));
_timer.arm_periodic(period);
}
}
future<_Tp> get(const _Key & k) {
auto i = _map.find(k);
if (i == _map.end()
|| (i->second.loaded + _refresh)
< loading_cache_clock_type::now()) {
return _load(k).then([this, k](_Tp t) {
_map[k] = ts_value_type{t, loading_cache_clock_type::now()};
return make_ready_future<_Tp>(std::move(t));
});
}
return make_ready_future<_Tp>(i->second.value);
}
private:
void on_timer() {
auto i = _map.begin();
auto e = _map.end();
auto now = loading_cache_clock_type::now();
while (i != e) {
if ((i->second.loaded + _expiry) < now) {
i = _map.erase(i);
continue;
}
++i;
}
if (_max_size != 0 && _map.size() > _max_size) {
typedef typename map_type::iterator iterator;
std::vector<iterator> tmp;
for (i = _map.begin(); i != e; ++i) {
tmp.emplace_back(i);
}
std::sort(tmp.begin(), tmp.end(), [](iterator i1, iterator i2) {
return i1->second.loaded < i2->second.loaded;
});
for (auto& e : tmp) {
_map.erase(e);
if (_map.size() < _max_size) {
break;
}
}
}
}
std::unordered_map<_Key, ts_value_type, _Hash, _Pred, _Alloc> _map;
size_t _max_size;
std::chrono::milliseconds _expiry;
std::chrono::milliseconds _refresh;
std::function<future<_Tp>(_Key)> _load;
timer<> _timer;
};
}