This commit is contained in:
118
rediska/cache/lru/LRU.cpp
vendored
Normal file
118
rediska/cache/lru/LRU.cpp
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "rediska/cache/lru/LRU.hpp"
|
||||
#include <expected>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include "rediska/cache/lru/LRUConfig.hpp"
|
||||
#include "rediska/cache/types.hpp"
|
||||
#include "rediska/common/MessageArguments.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
#include "rediska/common/utils.hpp"
|
||||
|
||||
namespace cache {
|
||||
LRU::LRU(LRUConfig config, CacheOpCallback callback)
|
||||
: config_(std::move(config)), callback_(callback) {}
|
||||
|
||||
void LRU::get(CacheKey&& key) {
|
||||
std::shared_lock lock(mutex_);
|
||||
auto it = keyToItem_.find(key);
|
||||
if (it == keyToItem_.end()) {
|
||||
return callback_(std::unexpected<RediskaReturnCode>(RediskaReturnCode::NOT_FOUND));
|
||||
}
|
||||
auto& metadata = it->second->location.metadata;
|
||||
|
||||
if (metadata.isExpired()) {
|
||||
evict(it);
|
||||
return callback_(std::unexpected<RediskaReturnCode>(RediskaReturnCode::KEY_EXPIRED));
|
||||
}
|
||||
if (config_.resetTTLOnAccess) metadata.resetExpirationTime();
|
||||
|
||||
// Move to start
|
||||
lru_list_.splice(lru_list_.begin(), lru_list_, it->second);
|
||||
keyToItem_[key] = lru_list_.begin();
|
||||
callback_(std::make_optional(it->second->location.value));
|
||||
}
|
||||
|
||||
void LRU::set(CacheKey&& key, CacheValue&& value, TTL ttl) {
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = keyToItem_.find(key);
|
||||
if (it != keyToItem_.end()) {
|
||||
it->second->location.value = std::move(value);
|
||||
it->second->location.metadata.resetExpirationTime();
|
||||
lru_list_.splice(lru_list_.begin(), lru_list_, it->second);
|
||||
callback_(std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFull()) evict();
|
||||
|
||||
lru_list_.push_front(
|
||||
CacheNode {
|
||||
.key = std::move(key),
|
||||
.location = ItemHandle{
|
||||
.value = std::move(value),
|
||||
.metadata = LRU::ItemMetadata(ttl)
|
||||
}
|
||||
}
|
||||
);
|
||||
keyToItem_[key] = lru_list_.begin();
|
||||
callback_(std::nullopt);
|
||||
}
|
||||
|
||||
void LRU::applyTo(CacheKey&& key, OperationId op, MessageArguments&& args) {
|
||||
std::unique_lock lock(mutex_);
|
||||
auto it = keyToItem_.find(key);
|
||||
if (it == keyToItem_.end()) {
|
||||
return callback_(std::unexpected<RediskaReturnCode>(RediskaReturnCode::NOT_FOUND));
|
||||
}
|
||||
auto& metadata = it->second->location.metadata;
|
||||
|
||||
std::expected<std::optional<CacheValue>, RediskaReturnCode> value;
|
||||
std::visit([&value, op](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (is_any_of_v<T, int64_t, bool, double, std::string>) {
|
||||
value = std::unexpected(RediskaReturnCode::INCOMPATIBLE_OPERATION);
|
||||
} else if constexpr (std::is_same_v<T, std::shared_ptr<ListDataStructure>>) {
|
||||
// TODO: Replace with real data argument
|
||||
DSValue dummy_data{};
|
||||
|
||||
std::expected<std::optional<DSValue>, DSReturnCode> res = arg->handle(op, std::move(dummy_data));
|
||||
if (!res) {
|
||||
value = std::unexpected(DSReturnCodeToRediskaReturnCode(res.error()));
|
||||
return;
|
||||
}
|
||||
if (!res.value().has_value()) {
|
||||
value = std::nullopt;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Explore implicit conversion
|
||||
// Attempts to covert failed
|
||||
value = std::visit([](auto&& arg) -> CacheValue {
|
||||
return CacheValue{arg};
|
||||
}, std::move(res->value()));
|
||||
}
|
||||
}, it->second->location.value);
|
||||
|
||||
lru_list_.splice(lru_list_.begin(), lru_list_, it->second);
|
||||
callback_(std::move(value));
|
||||
return;
|
||||
}
|
||||
|
||||
void LRU::evict() {
|
||||
if (lru_list_.empty()) return;
|
||||
|
||||
keyToItem_.erase(lru_list_.back().key);
|
||||
lru_list_.pop_back();
|
||||
}
|
||||
|
||||
void LRU::evict(const std::unordered_map<CacheKey, std::list<CacheNode>::iterator>::iterator node) {
|
||||
lru_list_.erase(node->second);
|
||||
keyToItem_.erase(node);
|
||||
}
|
||||
|
||||
|
||||
inline bool LRU::isFull() const {
|
||||
return lru_list_.size() >= config_.maxCapacity;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user