This commit is contained in:
7
rediska/cache/BaseCacheConfig.hpp
vendored
Normal file
7
rediska/cache/BaseCacheConfig.hpp
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace cache {
|
||||
struct BaseCacheConfig {
|
||||
bool resetTTLOnAccess = true;
|
||||
};
|
||||
}
|
||||
20
rediska/cache/BaseItemMetadata.cpp
vendored
Normal file
20
rediska/cache/BaseItemMetadata.cpp
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "BaseItemMetadata.hpp"
|
||||
|
||||
namespace cache {
|
||||
BaseItemMetadata::BaseItemMetadata(TTL ttl) : ttl_(ttl) {
|
||||
resetExpirationTime();
|
||||
}
|
||||
|
||||
void BaseItemMetadata::updateTTL(TTL ttl) {
|
||||
ttl_ = ttl;
|
||||
resetExpirationTime();
|
||||
}
|
||||
|
||||
bool BaseItemMetadata::isExpired() const {
|
||||
return expirationTime_ < std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void BaseItemMetadata::resetExpirationTime() {
|
||||
expirationTime_ = std::chrono::steady_clock::now() + std::chrono::seconds(ttl_);
|
||||
}
|
||||
}
|
||||
20
rediska/cache/BaseItemMetadata.hpp
vendored
Normal file
20
rediska/cache/BaseItemMetadata.hpp
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace cache {
|
||||
class BaseItemMetadata {
|
||||
public:
|
||||
BaseItemMetadata(TTL ttl);
|
||||
|
||||
void updateTTL(TTL ttl);
|
||||
|
||||
void resetExpirationTime();
|
||||
|
||||
bool isExpired() const;
|
||||
|
||||
private:
|
||||
TTL ttl_;
|
||||
Timestamp expirationTime_;
|
||||
};
|
||||
}
|
||||
9
rediska/cache/CMakeLists.txt
vendored
Normal file
9
rediska/cache/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
add_library(cache STATIC
|
||||
lru/LRU.cpp
|
||||
lru/LRUItemMetadata.cpp
|
||||
BaseItemMetadata.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(cache PUBLIC
|
||||
common
|
||||
)
|
||||
21
rediska/cache/CachePolicy.hpp
vendored
Normal file
21
rediska/cache/CachePolicy.hpp
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "rediska/common/MessageArguments.hpp"
|
||||
#include "rediska/common/enums.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace cache {
|
||||
class CachePolicy {
|
||||
public:
|
||||
virtual ~CachePolicy() = default;
|
||||
|
||||
virtual void get(CacheKey&& key) = 0;
|
||||
|
||||
virtual void set(CacheKey&& key, CacheValue&& value, TTL ttl) = 0;
|
||||
|
||||
virtual void applyTo(CacheKey&& key, OperationId op, MessageArguments&& args) = 0;
|
||||
|
||||
protected:
|
||||
virtual void evict() = 0;
|
||||
|
||||
[[nodiscard]] virtual inline bool isFull() const = 0;
|
||||
};
|
||||
}
|
||||
5
rediska/cache/constants.hpp
vendored
Normal file
5
rediska/cache/constants.hpp
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
constexpr int16_t MAX_ALLOWED_WORKERS = INT8_MAX + 1;
|
||||
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;
|
||||
}
|
||||
}
|
||||
52
rediska/cache/lru/LRU.hpp
vendored
Normal file
52
rediska/cache/lru/LRU.hpp
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include "rediska/cache/lru/LRUConfig.hpp"
|
||||
#include "rediska/cache/lru/LRUItemMetadata.hpp"
|
||||
#include "rediska/cache/types.hpp"
|
||||
#include "rediska/common/MessageArguments.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
#include "rediska/cache/CachePolicy.hpp"
|
||||
|
||||
namespace cache {
|
||||
class LRU : public CachePolicy {
|
||||
public:
|
||||
using ItemMetadata = LRUItemMetadata;
|
||||
using ItemHandle = struct {
|
||||
CacheValue value;
|
||||
ItemMetadata metadata;
|
||||
};
|
||||
using CacheNode = struct {
|
||||
CacheKey key;
|
||||
ItemHandle location;
|
||||
};
|
||||
|
||||
LRU(LRUConfig config, CacheOpCallback callback);
|
||||
~LRU() = default;
|
||||
|
||||
void get(CacheKey&& key) override;
|
||||
|
||||
void set(CacheKey&& key, CacheValue&& value, TTL ttl) override;
|
||||
|
||||
void applyTo(CacheKey&& key, OperationId op, MessageArguments&& args) override;
|
||||
|
||||
private:
|
||||
std::list<CacheNode> lru_list_;
|
||||
std::unordered_map<CacheKey, std::list<CacheNode>::iterator> keyToItem_;
|
||||
|
||||
std::shared_mutex mutex_;
|
||||
|
||||
LRUConfig config_;
|
||||
CacheOpCallback callback_;
|
||||
|
||||
void evict() override;
|
||||
|
||||
void evict(const std::unordered_map<CacheKey, std::list<CacheNode>::iterator>::iterator node);
|
||||
|
||||
inline bool isFull() const override;
|
||||
|
||||
void resetTTLIfEnabled(CacheNode& key);
|
||||
};
|
||||
}
|
||||
12
rediska/cache/lru/LRUConfig.hpp
vendored
Normal file
12
rediska/cache/lru/LRUConfig.hpp
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include "rediska/cache/BaseCacheConfig.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace cache {
|
||||
struct LRUConfig : BaseCacheConfig {
|
||||
size_t maxCapacity;
|
||||
TTL ttl;
|
||||
};
|
||||
}
|
||||
9
rediska/cache/lru/LRUItemMetadata.cpp
vendored
Normal file
9
rediska/cache/lru/LRUItemMetadata.cpp
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "rediska/cache/BaseItemMetadata.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace cache {
|
||||
class LRUItemMetadata : public BaseItemMetadata {
|
||||
public:
|
||||
LRUItemMetadata(TTL ttl) : BaseItemMetadata(ttl) {}
|
||||
};
|
||||
}
|
||||
10
rediska/cache/lru/LRUItemMetadata.hpp
vendored
Normal file
10
rediska/cache/lru/LRUItemMetadata.hpp
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "rediska/cache/BaseItemMetadata.hpp"
|
||||
|
||||
namespace cache {
|
||||
class LRUItemMetadata : public BaseItemMetadata {
|
||||
public:
|
||||
LRUItemMetadata(TTL ttl);
|
||||
};
|
||||
}
|
||||
10
rediska/cache/types.hpp
vendored
Normal file
10
rediska/cache/types.hpp
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace cache {
|
||||
using CacheOpCallback = std::function<void(std::expected<std::optional<CacheValue>, RediskaReturnCode>)>;
|
||||
}
|
||||
Reference in New Issue
Block a user