This commit is contained in:
25
rediska/CMakeLists.txt
Normal file
25
rediska/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(data-structures)
|
||||
|
||||
add_subdirectory(worker)
|
||||
add_subdirectory(cache)
|
||||
|
||||
add_subdirectory(frontend)
|
||||
|
||||
|
||||
add_executable(Rediska main.cpp)
|
||||
target_link_libraries(Rediska PRIVATE
|
||||
frontend
|
||||
worker
|
||||
cache
|
||||
common
|
||||
data-structures
|
||||
)
|
||||
|
||||
# Convenience target
|
||||
add_custom_target(run
|
||||
COMMAND $<TARGET_FILE:Rediska>
|
||||
DEPENDS Rediska
|
||||
USES_TERMINAL
|
||||
COMMENT "Building and running Rediska"
|
||||
)
|
||||
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>)>;
|
||||
}
|
||||
10
rediska/common/CMakeLists.txt
Normal file
10
rediska/common/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
add_library(common STATIC
|
||||
tmp.cpp
|
||||
)
|
||||
|
||||
find_package(concurrentqueue REQUIRED)
|
||||
|
||||
target_link_libraries(common
|
||||
PUBLIC
|
||||
spdlog::spdlog
|
||||
)
|
||||
46
rediska/common/MessageArguments.hpp
Normal file
46
rediska/common/MessageArguments.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
struct PrimitiveSetArgs {
|
||||
CacheValue value;
|
||||
TTL ttl_seconds;
|
||||
};
|
||||
|
||||
struct ListCreateArgs {
|
||||
CacheValueId element_kind;
|
||||
TTL ttl_seconds;
|
||||
};
|
||||
|
||||
struct ListIndexArgs {
|
||||
int64_t index;
|
||||
};
|
||||
|
||||
struct ListSetArgs {
|
||||
int64_t index;
|
||||
CacheValue value;
|
||||
};
|
||||
|
||||
struct ListInsertArgs {
|
||||
int64_t index;
|
||||
CacheValue value;
|
||||
};
|
||||
|
||||
struct ListPushBackArgs {
|
||||
CacheValue value;
|
||||
};
|
||||
|
||||
struct ListPushManyArgs {
|
||||
std::vector<CacheValue> values;
|
||||
bool replace_entire_list = false; // true => overwrite entire list, false => append
|
||||
};
|
||||
|
||||
using MessageArguments = std::variant<std::monostate,
|
||||
PrimitiveSetArgs,
|
||||
ListCreateArgs,
|
||||
ListIndexArgs,
|
||||
ListSetArgs,
|
||||
ListInsertArgs,
|
||||
ListPushBackArgs,
|
||||
ListPushManyArgs>;
|
||||
42
rediska/common/QueueMessage.hpp
Normal file
42
rediska/common/QueueMessage.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "rediska/common/enums.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
#include "rediska/common/MessageArguments.hpp"
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include <grpcpp/support/async_stream.h> // for ServerAsyncStreamingInterface completeness
|
||||
|
||||
// TODO: get rid of forward declaration somehow maybe
|
||||
struct BaseRequestManager;
|
||||
|
||||
struct RequestEvent {
|
||||
BaseRequestManager& manager;
|
||||
};
|
||||
|
||||
// This just leaves the responder for you to delete.
|
||||
struct FinishEvent {
|
||||
std::unique_ptr<grpc::internal::ServerAsyncStreamingInterface> responder;
|
||||
};
|
||||
|
||||
using EventVariant = std::variant<RequestEvent, FinishEvent>;
|
||||
|
||||
struct QueueMessage {
|
||||
CacheValueId type;
|
||||
CacheKey key;
|
||||
OperationId operation;
|
||||
MessageArguments arguments;
|
||||
std::unique_ptr<grpc::internal::ServerAsyncStreamingInterface> responder;
|
||||
|
||||
template<typename ResponseT>
|
||||
void respond(ResponseT response) {
|
||||
if (auto* writer = dynamic_cast<grpc::ServerAsyncResponseWriter<ResponseT>*>(responder.get())) {
|
||||
writer->Finish(response, grpc::Status::OK, std::make_unique<EventVariant>(FinishEvent { std::move(responder) }).release());
|
||||
responder = nullptr;
|
||||
} else {
|
||||
throw std::runtime_error("Invalid responder type in QueueMessage::respond");
|
||||
}
|
||||
}
|
||||
};
|
||||
13
rediska/common/enums.hpp
Normal file
13
rediska/common/enums.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
enum class OperationId {
|
||||
GET,
|
||||
SET,
|
||||
DELETE,
|
||||
LIST_PUSH_BACK,
|
||||
LIST_POP_BACK,
|
||||
LIST_INSERT,
|
||||
LIST_ERASE,
|
||||
OBJECT_GET_FIELD,
|
||||
OBJECT_SET_FIELD,
|
||||
};
|
||||
0
rediska/common/tmp.cpp
Normal file
0
rediska/common/tmp.cpp
Normal file
30
rediska/common/types.hpp
Normal file
30
rediska/common/types.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <chrono>
|
||||
#include "rediska/data-structures/impl/ListDataStructure.hpp"
|
||||
|
||||
using CacheKey = std::string;
|
||||
|
||||
// Definition order of `CacheValue` and `TypeId` MUST match!
|
||||
using CacheValue = std::variant<bool, int64_t, double, std::string, std::shared_ptr<ListDataStructure>>;
|
||||
|
||||
enum class CacheValueId { BOOLEAN = 0, INT, FLOAT, STRING, ARRAY }; // TODO: Object
|
||||
|
||||
enum class RediskaReturnCode {
|
||||
OK,
|
||||
INCOMPATIBLE_OPERATION,
|
||||
NOT_FOUND,
|
||||
KEY_EXPIRED,
|
||||
UNKNOWN_ERROR,
|
||||
DS_EMPTY,
|
||||
DS_OUT_OF_RANGE,
|
||||
DS_UNKNOWN_ERROR
|
||||
};
|
||||
|
||||
using TTL = uint32_t;
|
||||
|
||||
using Timestamp = std::chrono::steady_clock::time_point;
|
||||
24
rediska/common/utils.hpp
Normal file
24
rediska/common/utils.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
constexpr bool is_any_of_v = (std::is_same_v<T, Ts> || ...);
|
||||
|
||||
inline RediskaReturnCode DSReturnCodeToRediskaReturnCode(DSReturnCode code) {
|
||||
switch (code) {
|
||||
case DSReturnCode::OK:
|
||||
return RediskaReturnCode::OK;
|
||||
case DSReturnCode::NOT_FOUND:
|
||||
return RediskaReturnCode::NOT_FOUND;
|
||||
case DSReturnCode::INCOMPATIBLE_OPERATION:
|
||||
return RediskaReturnCode::INCOMPATIBLE_OPERATION;
|
||||
case DSReturnCode::EMPTY:
|
||||
return RediskaReturnCode::DS_EMPTY;
|
||||
case DSReturnCode::OUT_OF_RANGE:
|
||||
return RediskaReturnCode::DS_OUT_OF_RANGE;
|
||||
default:
|
||||
return RediskaReturnCode::UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
14
rediska/data-structures/AbstractDataStructure.hpp
Normal file
14
rediska/data-structures/AbstractDataStructure.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <expected>
|
||||
#include <optional>
|
||||
#include "rediska/common/enums.hpp"
|
||||
#include "rediska/data-structures/enums.hpp"
|
||||
#include "rediska/data-structures/types.hpp"
|
||||
|
||||
class AbstractDataStructure {
|
||||
public:
|
||||
virtual ~AbstractDataStructure() = default;
|
||||
|
||||
virtual std::expected<std::optional<DSValue>, DSReturnCode> handle(OperationId op, DSValue data) = 0;
|
||||
};
|
||||
7
rediska/data-structures/CMakeLists.txt
Normal file
7
rediska/data-structures/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
add_library(data-structures STATIC
|
||||
impl/ListDataStructure.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(data-structures PUBLIC
|
||||
common
|
||||
)
|
||||
0
rediska/data-structures/README.md
Normal file
0
rediska/data-structures/README.md
Normal file
3
rediska/data-structures/enums.hpp
Normal file
3
rediska/data-structures/enums.hpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
enum class DSReturnCode { OK, INCOMPATIBLE_OPERATION, EMPTY, OUT_OF_RANGE, NOT_FOUND };
|
||||
56
rediska/data-structures/impl/ListDataStructure.cpp
Normal file
56
rediska/data-structures/impl/ListDataStructure.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <list>
|
||||
#include "rediska/data-structures/AbstractDataStructure.hpp"
|
||||
#include "rediska/data-structures/enums.hpp"
|
||||
#include "rediska/data-structures/types.hpp"
|
||||
|
||||
class ListDataStructure : public AbstractDataStructure {
|
||||
public:
|
||||
~ListDataStructure() = default;
|
||||
|
||||
using arguments = struct {};
|
||||
|
||||
std::expected<std::optional<DSValue>, DSReturnCode> handle(OperationId op, DSValue data) override {
|
||||
switch (op) {
|
||||
case OperationId::GET: {
|
||||
// TODO: Index
|
||||
// if (index < 0 || index >= list_.size()) {
|
||||
// return std::unexpected(DSReturnCode::OUT_OF_RANGE);
|
||||
// }
|
||||
return std::make_optional(*list_.begin());
|
||||
}
|
||||
case OperationId::SET: {
|
||||
// Replace entire list with a single value if provided
|
||||
list_.clear();
|
||||
list_.push_back(data);
|
||||
return std::nullopt;
|
||||
}
|
||||
case OperationId::LIST_PUSH_BACK: {
|
||||
list_.push_back(data);
|
||||
return std::nullopt;
|
||||
}
|
||||
case OperationId::LIST_POP_BACK: {
|
||||
if (list_.empty()) {
|
||||
return std::unexpected(DSReturnCode::EMPTY);
|
||||
}
|
||||
DSValue value = list_.back();
|
||||
list_.pop_back();
|
||||
return std::make_optional(value);
|
||||
}
|
||||
case OperationId::LIST_INSERT: {
|
||||
// TODO: if index
|
||||
list_.insert(list_.begin(), data);
|
||||
return std::nullopt;
|
||||
}
|
||||
case OperationId::LIST_ERASE: {
|
||||
// TODO: if index
|
||||
list_.erase(list_.begin());
|
||||
return std::nullopt;
|
||||
}
|
||||
default:
|
||||
return std::unexpected(DSReturnCode::INCOMPATIBLE_OPERATION);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<DSValue> list_;
|
||||
};
|
||||
15
rediska/data-structures/impl/ListDataStructure.hpp
Normal file
15
rediska/data-structures/impl/ListDataStructure.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include "rediska/data-structures/AbstractDataStructure.hpp"
|
||||
#include "rediska/data-structures/types.hpp"
|
||||
|
||||
class ListDataStructure : public AbstractDataStructure {
|
||||
public:
|
||||
~ListDataStructure() = default;
|
||||
|
||||
std::expected<std::optional<DSValue>, DSReturnCode> handle(OperationId op, DSValue data) override;
|
||||
|
||||
private:
|
||||
std::list<DSValue> list_;
|
||||
};
|
||||
7
rediska/data-structures/types.hpp
Normal file
7
rediska/data-structures/types.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
using DSValue = std::variant<bool, int64_t, double, std::string>;
|
||||
63
rediska/frontend/CMakeLists.txt
Normal file
63
rediska/frontend/CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
add_library(frontend STATIC
|
||||
# IntService.cpp
|
||||
# BoolService.cpp
|
||||
# FloatService.cpp
|
||||
# StringService.cpp
|
||||
CallData.cpp
|
||||
server.hpp
|
||||
RequestManager.hpp
|
||||
)
|
||||
|
||||
# Find packages
|
||||
find_package(Protobuf REQUIRED CONFIG)
|
||||
find_package(gRPC REQUIRED CONFIG)
|
||||
|
||||
set(PROTO_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../proto")
|
||||
|
||||
# Define the proto files
|
||||
set(PROTO_FILES
|
||||
"${PROTO_ROOT_DIR}/v1/collections/common.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/collections/element_kind.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/collections/list.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/object/object.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/primitives/bool.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/primitives/float.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/primitives/int.proto"
|
||||
"${PROTO_ROOT_DIR}/v1/primitives/string.proto"
|
||||
)
|
||||
|
||||
# This allows protobuf_generate to find them when inspecting the target.
|
||||
target_sources(frontend PRIVATE ${PROTO_FILES})
|
||||
|
||||
# Include directories
|
||||
# We include the binary dir (for generated .pb.h files) and the root (for imports)
|
||||
target_include_directories(frontend PUBLIC
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"${PROTO_ROOT_DIR}"
|
||||
)
|
||||
|
||||
# Generate Standard Protobuf files (.pb.cc / .pb.h)
|
||||
# We use 'protobuf_generate' instead of the legacy 'protobuf_generate_cpp'.
|
||||
# It automatically adds the generated C++ files back into the 'frontend' target.
|
||||
protobuf_generate(
|
||||
TARGET frontend
|
||||
LANGUAGE cpp
|
||||
IMPORT_DIRS "${PROTO_ROOT_DIR}"
|
||||
PROTOC_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
# Generate gRPC specific files (.grpc.pb.cc / .grpc.pb.h)
|
||||
protobuf_generate(
|
||||
TARGET frontend
|
||||
LANGUAGE grpc
|
||||
GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc
|
||||
PLUGIN "protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin>"
|
||||
IMPORT_DIRS "${PROTO_ROOT_DIR}"
|
||||
PROTOC_OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
target_link_libraries(frontend
|
||||
PUBLIC
|
||||
gRPC::grpc++
|
||||
protobuf::libprotobuf
|
||||
)
|
||||
336
rediska/frontend/CallData.cpp
Normal file
336
rediska/frontend/CallData.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include <functional>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "google/protobuf/empty.pb.h"
|
||||
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
#include "rediska/frontend/RequestManager.hpp"
|
||||
#include "rediska/frontend/server.hpp"
|
||||
|
||||
namespace {
|
||||
CacheValue CollectionElementToCacheValue(const v1::collections::common::CollectionElement& element) {
|
||||
if (element.has_integer())
|
||||
return static_cast<int64_t>(element.integer());
|
||||
if (element.has_floating_point())
|
||||
return element.floating_point();
|
||||
if (element.has_boolean())
|
||||
return element.boolean();
|
||||
if (element.has_str_or_obj())
|
||||
return element.str_or_obj();
|
||||
return std::string{};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using BoolService = v1::primitives::boolean::BoolCacheService::AsyncService;
|
||||
using IntService = v1::primitives::integer::IntCacheService::AsyncService;
|
||||
using StringService = v1::primitives::str::StringCacheService::AsyncService;
|
||||
using ListService = v1::collections::list::ListCacheService::AsyncService;
|
||||
|
||||
struct BoolSetRequestManager : RequestManager<
|
||||
BoolSetRequestManager,
|
||||
v1::primitives::boolean::BoolSetRequest,
|
||||
grpc::ServerAsyncResponseWriter<google::protobuf::Empty>,
|
||||
BoolService,
|
||||
&BoolService::RequestSet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::BOOLEAN;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::SET;
|
||||
msg.arguments = PrimitiveSetArgs{.value = CacheValue{this->request.value()}, .ttl_seconds = 0};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct BoolGetRequestManager : RequestManager<
|
||||
BoolGetRequestManager,
|
||||
v1::primitives::boolean::BoolGetRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::boolean::BoolGetResponse>,
|
||||
BoolService,
|
||||
&BoolService::RequestGet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::BOOLEAN;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::GET;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct BoolDeleteRequestManager : RequestManager<
|
||||
BoolDeleteRequestManager,
|
||||
v1::primitives::boolean::BoolDeleteRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::boolean::BoolDeleteResponse>,
|
||||
BoolService,
|
||||
&BoolService::RequestDelete> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::BOOLEAN;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::DELETE;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntSetRequestManager : RequestManager<
|
||||
IntSetRequestManager,
|
||||
v1::primitives::integer::IntSetRequest,
|
||||
grpc::ServerAsyncResponseWriter<google::protobuf::Empty>,
|
||||
IntService,
|
||||
&IntService::RequestSet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::INT;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::SET;
|
||||
msg.arguments = PrimitiveSetArgs{.value = CacheValue{this->request.value()}, .ttl_seconds = 0};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntGetRequestManager : RequestManager<
|
||||
IntGetRequestManager,
|
||||
v1::primitives::integer::IntGetRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::integer::IntGetResponse>,
|
||||
IntService,
|
||||
&IntService::RequestGet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::INT;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::GET;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntDeleteRequestManager : RequestManager<
|
||||
IntDeleteRequestManager,
|
||||
v1::primitives::integer::IntDeleteRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::integer::IntDeleteResponse>,
|
||||
IntService,
|
||||
&IntService::RequestDelete> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::INT;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::DELETE;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringSetRequestManager : RequestManager<
|
||||
StringSetRequestManager,
|
||||
v1::primitives::str::StringSetRequest,
|
||||
grpc::ServerAsyncResponseWriter<google::protobuf::Empty>,
|
||||
StringService,
|
||||
&StringService::RequestSet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::STRING;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::SET;
|
||||
msg.arguments = PrimitiveSetArgs{.value = CacheValue{this->request.value()}, .ttl_seconds = 0};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringGetRequestManager : RequestManager<
|
||||
StringGetRequestManager,
|
||||
v1::primitives::str::StringGetRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::str::StringGetResponse>,
|
||||
StringService,
|
||||
&StringService::RequestGet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::STRING;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::GET;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringDeleteRequestManager : RequestManager<
|
||||
StringDeleteRequestManager,
|
||||
v1::primitives::str::StringDeleteRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::primitives::str::StringDeleteResponse>,
|
||||
StringService,
|
||||
&StringService::RequestDelete> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::STRING;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::DELETE;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct ListSetRequestManager : RequestManager<
|
||||
ListSetRequestManager,
|
||||
v1::collections::list::ListSetRequest,
|
||||
grpc::ServerAsyncResponseWriter<v1::collections::list::ListSetResponse>,
|
||||
ListService,
|
||||
&ListService::RequestSet> {
|
||||
using RequestManager::RequestManager;
|
||||
QueueMessage BuildMessage() {
|
||||
std::vector<CacheValue> values;
|
||||
values.reserve(static_cast<size_t>(this->request.elements_size()));
|
||||
for (const auto& el : this->request.elements())
|
||||
values.emplace_back(CollectionElementToCacheValue(el));
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::ARRAY;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::SET;
|
||||
msg.arguments = ListPushManyArgs{.values = std::move(values), .replace_entire_list = true};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct ListGetRequestManager : RequestManager<
|
||||
ListGetRequestManager,
|
||||
v1::collections::list::ListGetRequest,
|
||||
grpc::ServerAsyncWriter<v1::collections::list::ListGetResponse>,
|
||||
ListService,
|
||||
&ListService::RequestGet> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::ARRAY;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::GET;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
struct ListDeleteRequestManager : RequestManager<
|
||||
ListDeleteRequestManager,
|
||||
v1::collections::list::DeleteRequest,
|
||||
grpc::ServerAsyncResponseWriter<google::protobuf::Empty>,
|
||||
ListService,
|
||||
&ListService::RequestDelete> {
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
QueueMessage BuildMessage() {
|
||||
QueueMessage msg;
|
||||
msg.type = CacheValueId::ARRAY;
|
||||
msg.key = this->request.id();
|
||||
msg.operation = OperationId::DELETE;
|
||||
msg.arguments = std::monostate{};
|
||||
msg.responder = this->TakeResponder();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
|
||||
void RunFrontendServer(const std::string& address, std::function<void(QueueMessage)> callback_enqueue_message) {
|
||||
grpc::ServerBuilder builder;
|
||||
BoolService bool_service;
|
||||
IntService int_service;
|
||||
StringService string_service;
|
||||
ListService list_service;
|
||||
|
||||
builder.AddListeningPort(address, grpc::InsecureServerCredentials());
|
||||
builder.RegisterService(&bool_service);
|
||||
builder.RegisterService(&int_service);
|
||||
builder.RegisterService(&string_service);
|
||||
builder.RegisterService(&list_service);
|
||||
|
||||
std::unique_ptr<grpc::ServerCompletionQueue> cq = builder.AddCompletionQueue();
|
||||
std::unique_ptr<grpc::Server> server = builder.BuildAndStart();
|
||||
|
||||
BoolSetRequestManager _BoolSetRequestManager(bool_service);
|
||||
_BoolSetRequestManager.ListenForOne(*cq);
|
||||
BoolGetRequestManager _BoolGetRequestManager(bool_service);
|
||||
_BoolGetRequestManager.ListenForOne(*cq);
|
||||
BoolDeleteRequestManager _BoolDeleteRequestManager(bool_service);
|
||||
_BoolDeleteRequestManager.ListenForOne(*cq);
|
||||
|
||||
IntSetRequestManager _IntSetRequestManager(int_service);
|
||||
_IntSetRequestManager.ListenForOne(*cq);
|
||||
IntGetRequestManager _IntGetRequestManager(int_service);
|
||||
_IntGetRequestManager.ListenForOne(*cq);
|
||||
IntDeleteRequestManager _IntDeleteRequestManager(int_service);
|
||||
_IntDeleteRequestManager.ListenForOne(*cq);
|
||||
|
||||
StringSetRequestManager _StringSetRequestManager(string_service);
|
||||
_StringSetRequestManager.ListenForOne(*cq);
|
||||
StringGetRequestManager _StringGetRequestManager(string_service);
|
||||
_StringGetRequestManager.ListenForOne(*cq);
|
||||
StringDeleteRequestManager _StringDeleteRequestManager(string_service);
|
||||
_StringDeleteRequestManager.ListenForOne(*cq);
|
||||
|
||||
ListSetRequestManager _ListSetRequestManager(list_service);
|
||||
_ListSetRequestManager.ListenForOne(*cq);
|
||||
ListGetRequestManager _ListGetRequestManager(list_service);
|
||||
_ListGetRequestManager.ListenForOne(*cq);
|
||||
ListDeleteRequestManager _ListDeleteRequestManager(list_service);
|
||||
_ListDeleteRequestManager.ListenForOne(*cq);
|
||||
|
||||
void* tag;
|
||||
bool ok;
|
||||
while (cq->Next(&tag, &ok)) {
|
||||
if (!tag)
|
||||
continue;
|
||||
// taking ownership as per the manager api
|
||||
std::unique_ptr<EventVariant> event(static_cast<EventVariant*>(tag));
|
||||
|
||||
if (!ok) {
|
||||
// idk could happen, but we do take ownership of the event anyway
|
||||
(void)event;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto* req = std::get_if<RequestEvent>(event.get())) {
|
||||
auto& manager = req->manager;
|
||||
if (auto msg = manager.ConsumeMessage(); msg.has_value()) {
|
||||
callback_enqueue_message(std::move(*msg));
|
||||
}
|
||||
manager.ListenForOne(*cq);
|
||||
} else if (auto* fin = std::get_if<FinishEvent>(event.get())) {
|
||||
// we just destroy
|
||||
std::cout << "Finished a request\n" << std::flush;
|
||||
(void)fin;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
rediska/frontend/RequestManager.hpp
Normal file
52
rediska/frontend/RequestManager.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include <grpcpp/support/async_stream.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
|
||||
// One global context kept alive as long as the server lives.
|
||||
inline grpc::ServerContext& GlobalServerContext() {
|
||||
static grpc::ServerContext ctx;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// The thing recieved from the completion queue in case of a request.
|
||||
struct BaseRequestManager {
|
||||
virtual void ListenForOne(grpc::ServerCompletionQueue& cq) = 0;
|
||||
virtual std::optional<QueueMessage> ConsumeMessage() = 0;
|
||||
virtual ~BaseRequestManager() = default;
|
||||
};
|
||||
|
||||
// This is basically storage for an incoming request.
|
||||
template <typename Derived, typename RequestT, typename ResponderT, typename ServiceT, auto RequestMethod>
|
||||
class RequestManager : public BaseRequestManager {
|
||||
protected:
|
||||
RequestT request;
|
||||
std::unique_ptr<ResponderT> responder;
|
||||
ServiceT& service;
|
||||
|
||||
std::unique_ptr<grpc::internal::ServerAsyncStreamingInterface> TakeResponder() {
|
||||
return std::unique_ptr<grpc::internal::ServerAsyncStreamingInterface>(std::move(responder));
|
||||
}
|
||||
|
||||
public:
|
||||
explicit RequestManager(ServiceT& svc) : service(svc) { }
|
||||
|
||||
// Sets up listening for a new request.
|
||||
// You must ensure this object lives until the request is consumed, or grpc will be unhappy.
|
||||
// The tag you recieve from the queue is a EventVariant* that you must take ownership of.
|
||||
void ListenForOne(grpc::ServerCompletionQueue& cq) override {
|
||||
request = RequestT{};
|
||||
// FIXME: just leak this stupid shit for now idk how to fix I'm done with this stupid language
|
||||
auto* ctx = new grpc::ServerContext();
|
||||
responder = std::make_unique<ResponderT>(ctx);
|
||||
(service.*RequestMethod)(ctx, &request, responder.get(), &cq, &cq, std::make_unique<EventVariant>(RequestEvent{*this}).release());
|
||||
}
|
||||
|
||||
std::optional<QueueMessage> ConsumeMessage() override {
|
||||
QueueMessage msg = static_cast<Derived*>(this)->BuildMessage();
|
||||
return msg;
|
||||
}
|
||||
};
|
||||
11
rediska/frontend/server.hpp
Normal file
11
rediska/frontend/server.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
|
||||
// Starts the gRPC frontend server on the given address and blocks.
|
||||
// Each incoming request is transformed into a QueueMessage with its responder
|
||||
// moved inside; the provided callback decides when/how to finish it.
|
||||
void RunFrontendServer(const std::string& address,
|
||||
std::function<void(QueueMessage)> on_request);
|
||||
5
rediska/main.cpp
Normal file
5
rediska/main.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "rediska/worker/FrontendWorker.cpp"
|
||||
|
||||
int main() {
|
||||
return run_frontend_server();
|
||||
}
|
||||
157
rediska/worker/AsyncWorker.cpp
Normal file
157
rediska/worker/AsyncWorker.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "rediska/worker/AsyncWorker.hpp"
|
||||
#include <google/protobuf/empty.pb.h>
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/bool.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/int.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
#include "v1/primitives/string.pb.h"
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
#include "v1/collections/list.pb.h"
|
||||
|
||||
namespace rediska::worker {
|
||||
|
||||
AsyncWorker::AsyncWorker(size_t num_workers, size_t cache_capacity)
|
||||
: num_workers_(num_workers) {
|
||||
cache_config_.maxCapacity = cache_capacity;
|
||||
cache_config_.ttl = 0;
|
||||
cache_config_.resetTTLOnAccess = true;
|
||||
InitializeCache();
|
||||
}
|
||||
|
||||
AsyncWorker::~AsyncWorker() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void AsyncWorker::Start() {
|
||||
if (running_) return;
|
||||
|
||||
running_ = true;
|
||||
workers_.reserve(num_workers_);
|
||||
|
||||
for (size_t i = 0; i < num_workers_; ++i) {
|
||||
workers_.emplace_back(&AsyncWorker::WorkerLoop, this);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWorker::Stop() {
|
||||
if (!running_) return;
|
||||
|
||||
running_ = false;
|
||||
queue_cv_.notify_all();
|
||||
|
||||
for (auto& worker : workers_) {
|
||||
if (worker.joinable()) {
|
||||
worker.join();
|
||||
}
|
||||
}
|
||||
|
||||
workers_.clear();
|
||||
}
|
||||
|
||||
void AsyncWorker::Enqueue(QueueMessage msg) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
message_queue_.push(std::move(msg));
|
||||
}
|
||||
queue_cv_.notify_one();
|
||||
}
|
||||
|
||||
void AsyncWorker::WorkerLoop() {
|
||||
while (running_) {
|
||||
QueueMessage msg;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
queue_cv_.wait(lock, [this] { return !message_queue_.empty() || !running_; });
|
||||
|
||||
if (!running_) break;
|
||||
|
||||
msg = std::move(message_queue_.front());
|
||||
message_queue_.pop();
|
||||
}
|
||||
|
||||
ProcessMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWorker::ProcessMessage(QueueMessage& msg) {
|
||||
if (!msg.responder) return;
|
||||
|
||||
try {
|
||||
switch (msg.operation) {
|
||||
case OperationId::SET: {
|
||||
if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListSetResponse response;
|
||||
msg.respond<v1::collections::list::ListSetResponse>(response);
|
||||
} else {
|
||||
google::protobuf::Empty response;
|
||||
msg.respond<google::protobuf::Empty>(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::GET: {
|
||||
cache_->get(std::string(msg.key));
|
||||
|
||||
// заглушка, пока кэш не интегрирован полностью TODO
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolGetResponse response;
|
||||
response.set_value(false);
|
||||
msg.respond<v1::primitives::boolean::BoolGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntGetResponse response;
|
||||
response.set_value(0);
|
||||
msg.respond<v1::primitives::integer::IntGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringGetResponse response;
|
||||
response.set_value("cached_value");
|
||||
msg.respond<v1::primitives::str::StringGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListGetResponse response;
|
||||
msg.respond<v1::collections::list::ListGetResponse>(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::DELETE: {
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolDeleteResponse response;
|
||||
response.set_removed_value(false);
|
||||
msg.respond<v1::primitives::boolean::BoolDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntDeleteResponse response;
|
||||
response.set_removed_value(0);
|
||||
msg.respond<v1::primitives::integer::IntDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringDeleteResponse response;
|
||||
response.set_removed_value("");
|
||||
msg.respond<v1::primitives::str::StringDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
google::protobuf::Empty response;
|
||||
msg.respond<google::protobuf::Empty>(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_PUSH_BACK:
|
||||
case OperationId::LIST_POP_BACK:
|
||||
case OperationId::LIST_INSERT:
|
||||
case OperationId::LIST_ERASE: {
|
||||
google::protobuf::Empty response;
|
||||
msg.respond<google::protobuf::Empty>(response);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Error processing message: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncWorker::InitializeCache() {
|
||||
auto callback = [this](auto result) {
|
||||
// TODO
|
||||
};
|
||||
|
||||
cache_ = std::make_unique<cache::LRU>(cache_config_, callback);
|
||||
}
|
||||
|
||||
} // namespace rediska::worker
|
||||
42
rediska/worker/AsyncWorker.hpp
Normal file
42
rediska/worker/AsyncWorker.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include "rediska/cache/lru/LRU.hpp"
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
#include "rediska/common/types.hpp"
|
||||
|
||||
namespace rediska::worker {
|
||||
|
||||
class AsyncWorker {
|
||||
public:
|
||||
explicit AsyncWorker(size_t num_workers = 4, size_t cache_capacity = 1000);
|
||||
~AsyncWorker();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void Enqueue(QueueMessage msg);
|
||||
bool IsRunning() const { return running_; }
|
||||
|
||||
private:
|
||||
void WorkerLoop();
|
||||
void ProcessMessage(QueueMessage& msg);
|
||||
void InitializeCache();
|
||||
|
||||
std::vector<std::thread> workers_;
|
||||
std::queue<QueueMessage> message_queue_;
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable queue_cv_;
|
||||
std::atomic<bool> running_{false};
|
||||
size_t num_workers_;
|
||||
std::unique_ptr<cache::LRU> cache_;
|
||||
cache::LRUConfig cache_config_;
|
||||
};
|
||||
|
||||
} // namespace rediska::worker
|
||||
14
rediska/worker/AsyncWorkerMain.cpp
Normal file
14
rediska/worker/AsyncWorkerMain.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "rediska/worker/AsyncWorker.hpp"
|
||||
#include "rediska/frontend/server.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int run_async_server() {
|
||||
rediska::worker::AsyncWorker worker(4, 1000);
|
||||
worker.Start();
|
||||
|
||||
RunFrontendServer("0.0.0.0:50051", [&worker](QueueMessage msg) {
|
||||
worker.Enqueue(std::move(msg));
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
rediska/worker/CMakeLists.txt
Normal file
15
rediska/worker/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
add_library(worker STATIC
|
||||
tmp.cpp
|
||||
AsyncWorker.cpp
|
||||
AsyncWorker.hpp
|
||||
AsyncWorkerMain.cpp
|
||||
FrontendWorker.cpp
|
||||
CacheWorker.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(worker PUBLIC
|
||||
common
|
||||
frontend
|
||||
cache
|
||||
data-structures
|
||||
)
|
||||
183
rediska/worker/CacheWorker.cpp
Normal file
183
rediska/worker/CacheWorker.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <shared_mutex>
|
||||
#include <chrono>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include "google/protobuf/empty.pb.h"
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/bool.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/int.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
#include "v1/primitives/string.pb.h"
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
#include "v1/collections/list.pb.h"
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
#include "rediska/frontend/RequestManager.hpp"
|
||||
#include "rediska/frontend/server.hpp"
|
||||
#include "rediska/cache/lru/LRU.hpp"
|
||||
#include "rediska/data-structures/impl/ListDataStructure.hpp"
|
||||
|
||||
namespace {
|
||||
class CacheWorker {
|
||||
public:
|
||||
CacheWorker(size_t cache_capacity = 1000) {
|
||||
cache_config_.maxCapacity = cache_capacity;
|
||||
cache_config_.ttl = 0;
|
||||
cache_config_.resetTTLOnAccess = true;
|
||||
|
||||
auto callback = [this](auto result) {
|
||||
};
|
||||
|
||||
cache_ = std::make_unique<cache::LRU>(cache_config_, callback);
|
||||
}
|
||||
|
||||
void Enqueue(QueueMessage msg) {
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
queue_.push(std::move(msg));
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
void Run() {
|
||||
for (;;) {
|
||||
QueueMessage msg;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx_);
|
||||
cv_.wait(lock, [&] { return !queue_.empty(); });
|
||||
msg = std::move(queue_.front());
|
||||
queue_.pop();
|
||||
}
|
||||
|
||||
std::cout << "[cache-worker] key=" << msg.key
|
||||
<< " type=" << static_cast<int>(msg.type)
|
||||
<< " op=" << static_cast<int>(msg.operation) << std::endl;
|
||||
|
||||
if (!msg.responder) continue;
|
||||
|
||||
try {
|
||||
switch (msg.operation) {
|
||||
case OperationId::SET: {
|
||||
if (msg.type == CacheValueId::ARRAY) {
|
||||
auto list = std::make_shared<ListDataStructure>();
|
||||
|
||||
if (std::holds_alternative<ListPushManyArgs>(msg.arguments)) {
|
||||
auto& args = std::get<ListPushManyArgs>(msg.arguments);
|
||||
for (auto& value : args.values) {
|
||||
list->handle(OperationId::LIST_PUSH_BACK, DSValue{});
|
||||
}
|
||||
}
|
||||
|
||||
cache_->set(std::string(msg.key), CacheValue{list}, 0);
|
||||
|
||||
v1::collections::list::ListSetResponse response;
|
||||
msg.respond<v1::collections::list::ListSetResponse>(response);
|
||||
} else {
|
||||
|
||||
CacheValue value;
|
||||
if (msg.arguments.index() != 0) {
|
||||
auto& args = std::get<PrimitiveSetArgs>(msg.arguments);
|
||||
value = args.value;
|
||||
}
|
||||
|
||||
cache_->set(std::string(msg.key), std::move(value), 0);
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::GET: {
|
||||
cache_->get(std::string(msg.key));
|
||||
|
||||
// я не нашел пока полный callback, тут заглушки (TODO)
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolGetResponse response;
|
||||
response.set_value(true);
|
||||
msg.respond<v1::primitives::boolean::BoolGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntGetResponse response;
|
||||
response.set_value(42);
|
||||
msg.respond<v1::primitives::integer::IntGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringGetResponse response;
|
||||
response.set_value("cached_string_value");
|
||||
msg.respond<v1::primitives::str::StringGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListGetResponse response;
|
||||
msg.respond<v1::collections::list::ListGetResponse>(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::DELETE: {
|
||||
cache_->applyTo(std::string(msg.key), OperationId::DELETE, std::move(msg.arguments));
|
||||
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolDeleteResponse response;
|
||||
response.set_removed_value(true);
|
||||
msg.respond<v1::primitives::boolean::BoolDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntDeleteResponse response;
|
||||
response.set_removed_value(42);
|
||||
msg.respond<v1::primitives::integer::IntDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringDeleteResponse response;
|
||||
response.set_removed_value("deleted_string");
|
||||
msg.respond<v1::primitives::str::StringDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_PUSH_BACK: {
|
||||
cache_->applyTo(std::string(msg.key), OperationId::LIST_PUSH_BACK, std::move(msg.arguments));
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_POP_BACK: {
|
||||
cache_->applyTo(std::string(msg.key), OperationId::LIST_POP_BACK, std::move(msg.arguments));
|
||||
|
||||
v1::collections::list::PopBackResponse response;
|
||||
msg.respond<v1::collections::list::PopBackResponse>(response);
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_INSERT: {
|
||||
cache_->applyTo(std::string(msg.key), OperationId::LIST_INSERT, std::move(msg.arguments));
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_ERASE: {
|
||||
cache_->applyTo(std::string(msg.key), OperationId::LIST_ERASE, std::move(msg.arguments));
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Error processing message: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<QueueMessage> queue_;
|
||||
std::mutex mtx_;
|
||||
std::condition_variable cv_;
|
||||
std::unique_ptr<cache::LRU> cache_;
|
||||
cache::LRUConfig cache_config_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
int run_cache_server() {
|
||||
CacheWorker worker(1000);
|
||||
std::thread t([&] { worker.Run(); });
|
||||
|
||||
RunFrontendServer("0.0.0.0:50051", [&](QueueMessage msg) {
|
||||
worker.Enqueue(std::move(msg));
|
||||
});
|
||||
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
123
rediska/worker/FrontendWorker.cpp
Normal file
123
rediska/worker/FrontendWorker.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include "google/protobuf/empty.pb.h"
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
#include "rediska/frontend/RequestManager.hpp"
|
||||
#include "rediska/frontend/server.hpp"
|
||||
#include "rediska/cache/lru/LRU.hpp"
|
||||
|
||||
namespace {
|
||||
class FrontendWorker {
|
||||
public:
|
||||
void Enqueue(QueueMessage msg) {
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
queue_.push(std::move(msg));
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
void Run() {
|
||||
for (;;) {
|
||||
QueueMessage msg;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx_);
|
||||
cv_.wait(lock, [&] { return !queue_.empty(); });
|
||||
msg = std::move(queue_.front());
|
||||
queue_.pop();
|
||||
}
|
||||
|
||||
std::cout << "[worker] key=" << msg.key
|
||||
<< " type=" << static_cast<int>(msg.type)
|
||||
<< " op=" << static_cast<int>(msg.operation) << std::endl;
|
||||
|
||||
if (!msg.responder) continue;
|
||||
|
||||
try {
|
||||
switch (msg.operation) {
|
||||
case OperationId::SET: {
|
||||
if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListSetResponse response;
|
||||
msg.respond<v1::collections::list::ListSetResponse>(response);
|
||||
} else {
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::GET: {
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolGetResponse response;
|
||||
response.set_value(false);
|
||||
msg.respond<v1::primitives::boolean::BoolGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntGetResponse response;
|
||||
response.set_value(0);
|
||||
msg.respond<v1::primitives::integer::IntGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringGetResponse response;
|
||||
response.set_value("cached_value");
|
||||
msg.respond<v1::primitives::str::StringGetResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListGetResponse response;
|
||||
msg.respond<v1::collections::list::ListGetResponse>(response);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::DELETE: {
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolDeleteResponse response;
|
||||
response.set_removed_value(false);
|
||||
msg.respond<v1::primitives::boolean::BoolDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntDeleteResponse response;
|
||||
response.set_removed_value(0);
|
||||
msg.respond<v1::primitives::integer::IntDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringDeleteResponse response;
|
||||
response.set_removed_value("");
|
||||
msg.respond<v1::primitives::str::StringDeleteResponse>(response);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationId::LIST_PUSH_BACK:
|
||||
case OperationId::LIST_POP_BACK:
|
||||
case OperationId::LIST_INSERT:
|
||||
case OperationId::LIST_ERASE: {
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Error processing message: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<QueueMessage> queue_;
|
||||
std::mutex mtx_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
int run_frontend_server() {
|
||||
FrontendWorker worker;
|
||||
std::thread t([&] { worker.Run(); });
|
||||
|
||||
RunFrontendServer("0.0.0.0:50051", [&](QueueMessage msg) {
|
||||
worker.Enqueue(std::move(msg));
|
||||
});
|
||||
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
86
rediska/worker/tmp.cpp
Normal file
86
rediska/worker/tmp.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include "google/protobuf/empty.pb.h"
|
||||
#include "v1/primitives/bool.grpc.pb.h"
|
||||
#include "v1/primitives/int.grpc.pb.h"
|
||||
#include "v1/primitives/string.grpc.pb.h"
|
||||
#include "v1/collections/list.grpc.pb.h"
|
||||
#include "rediska/common/QueueMessage.hpp"
|
||||
#include "rediska/frontend/RequestManager.hpp"
|
||||
#include "rediska/frontend/server.hpp"
|
||||
|
||||
namespace {
|
||||
class PrintWorker {
|
||||
public:
|
||||
void Enqueue(QueueMessage msg) {
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
queue_.push(std::move(msg));
|
||||
cv_.notify_one();
|
||||
}
|
||||
|
||||
void Run() {
|
||||
for (;;) {
|
||||
QueueMessage msg;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx_);
|
||||
cv_.wait(lock, [&] { return !queue_.empty(); });
|
||||
msg = std::move(queue_.front());
|
||||
queue_.pop();
|
||||
}
|
||||
|
||||
std::cout << "[worker] key=" << msg.key
|
||||
<< " type=" << static_cast<int>(msg.type)
|
||||
<< " op=" << static_cast<int>(msg.operation) << std::endl;
|
||||
|
||||
if (!msg.responder) continue;
|
||||
|
||||
switch (msg.operation) {
|
||||
case OperationId::SET:
|
||||
case OperationId::DELETE: {
|
||||
msg.respond<google::protobuf::Empty>(google::protobuf::Empty{});
|
||||
break;
|
||||
}
|
||||
case OperationId::GET: {
|
||||
if (msg.type == CacheValueId::BOOLEAN) {
|
||||
v1::primitives::boolean::BoolGetResponse r; r.set_value(false);
|
||||
msg.respond<v1::primitives::boolean::BoolGetResponse>(r);
|
||||
} else if (msg.type == CacheValueId::INT) {
|
||||
v1::primitives::integer::IntGetResponse r; r.set_value(0);
|
||||
msg.respond<v1::primitives::integer::IntGetResponse>(r);
|
||||
} else if (msg.type == CacheValueId::STRING) {
|
||||
v1::primitives::str::StringGetResponse r; r.set_value("test");
|
||||
msg.respond<v1::primitives::str::StringGetResponse>(r);
|
||||
} else if (msg.type == CacheValueId::ARRAY) {
|
||||
v1::collections::list::ListGetResponse r;
|
||||
msg.respond<v1::collections::list::ListGetResponse>(r);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<QueueMessage> queue_;
|
||||
std::mutex mtx_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
int run_print_server() {
|
||||
PrintWorker worker;
|
||||
std::thread t([&] { worker.Run(); });
|
||||
|
||||
RunFrontendServer("0.0.0.0:50051", [&](QueueMessage msg) {
|
||||
worker.Enqueue(std::move(msg));
|
||||
});
|
||||
|
||||
t.join();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user