chore: initial commit
Some checks failed
Code Analysis / analysis (push) Failing after 2m59s

This commit is contained in:
2025-12-30 22:34:58 +07:00
commit 35a6349071
63 changed files with 2675 additions and 0 deletions

25
rediska/CMakeLists.txt Normal file
View 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
View File

@@ -0,0 +1,7 @@
#pragma once
namespace cache {
struct BaseCacheConfig {
bool resetTTLOnAccess = true;
};
}

20
rediska/cache/BaseItemMetadata.cpp vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>)>;
}

View File

@@ -0,0 +1,10 @@
add_library(common STATIC
tmp.cpp
)
find_package(concurrentqueue REQUIRED)
target_link_libraries(common
PUBLIC
spdlog::spdlog
)

View 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>;

View 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
View 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
View File

30
rediska/common/types.hpp Normal file
View 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
View 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;
}
}

View 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;
};

View File

@@ -0,0 +1,7 @@
add_library(data-structures STATIC
impl/ListDataStructure.cpp
)
target_link_libraries(data-structures PUBLIC
common
)

View File

View File

@@ -0,0 +1,3 @@
#pragma once
enum class DSReturnCode { OK, INCOMPATIBLE_OPERATION, EMPTY, OUT_OF_RANGE, NOT_FOUND };

View 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_;
};

View 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_;
};

View File

@@ -0,0 +1,7 @@
#pragma once
#include <cstdint>
#include <string>
#include <variant>
using DSValue = std::variant<bool, int64_t, double, std::string>;

View 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
)

View 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;
}
}
}

View 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;
}
};

View 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
View File

@@ -0,0 +1,5 @@
#include "rediska/worker/FrontendWorker.cpp"
int main() {
return run_frontend_server();
}

View 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

View 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

View 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;
}

View 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
)

View 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;
}

View 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
View 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;
}