Replace master branch with local files
This commit is contained in:
commit
875a53f499
60 changed files with 21637 additions and 0 deletions
311
backend/src/services/RedisHelper.cpp
Normal file
311
backend/src/services/RedisHelper.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#include "RedisHelper.h"
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace services {
|
||||
|
||||
RedisHelper &RedisHelper::instance() {
|
||||
static RedisHelper inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
RedisHelper::RedisHelper() : _initialized(false) {
|
||||
LOG_INFO << "RedisHelper created (connection will be established on first use)";
|
||||
}
|
||||
|
||||
RedisHelper::~RedisHelper() = default;
|
||||
|
||||
void RedisHelper::ensureConnected() {
|
||||
if (_initialized) return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(_initMutex);
|
||||
if (_initialized) return; // Double-check
|
||||
|
||||
try {
|
||||
sw::redis::ConnectionOptions opts;
|
||||
opts.host = getRedisHost();
|
||||
opts.port = getRedisPort();
|
||||
|
||||
const char* envPass = std::getenv("REDIS_PASS");
|
||||
if (envPass && strlen(envPass) > 0) {
|
||||
opts.password = envPass;
|
||||
}
|
||||
|
||||
opts.socket_timeout = std::chrono::milliseconds(1000);
|
||||
opts.connect_timeout = std::chrono::milliseconds(1000);
|
||||
|
||||
LOG_INFO << "Connecting to Redis at " << opts.host << ":" << opts.port;
|
||||
|
||||
_redis = std::make_unique<sw::redis::Redis>(opts);
|
||||
_redis->ping();
|
||||
|
||||
_initialized = true;
|
||||
LOG_INFO << "Redis connection established successfully";
|
||||
} catch (const sw::redis::Error& e) {
|
||||
LOG_ERROR << "Failed to connect to Redis: " << e.what();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
std::string RedisHelper::getRedisHost() const {
|
||||
const char* envHost = std::getenv("REDIS_HOST");
|
||||
if (envHost) return std::string(envHost);
|
||||
|
||||
try {
|
||||
const auto& config = drogon::app().getCustomConfig();
|
||||
if (config.isMember("redis") && config["redis"].isMember("host")) {
|
||||
return config["redis"]["host"].asString();
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
return "redis";
|
||||
}
|
||||
|
||||
int RedisHelper::getRedisPort() const {
|
||||
const char* envPort = std::getenv("REDIS_PORT");
|
||||
if (envPort) {
|
||||
try {
|
||||
return std::stoi(envPort);
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
try {
|
||||
const auto& config = drogon::app().getCustomConfig();
|
||||
if (config.isMember("redis") && config["redis"].isMember("port")) {
|
||||
return config["redis"]["port"].asInt();
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
return 6379;
|
||||
}
|
||||
|
||||
void RedisHelper::executeInThreadPool(std::function<void()> task) {
|
||||
auto loop = drogon::app().getLoop();
|
||||
if (!loop) {
|
||||
LOG_ERROR << "Event loop not available, executing task synchronously";
|
||||
try {
|
||||
task();
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR << "Error executing task: " << e.what();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
loop->queueInLoop([task = std::move(task)]() {
|
||||
std::thread([task]() {
|
||||
try {
|
||||
task();
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR << "Error in thread pool task: " << e.what();
|
||||
}
|
||||
}).detach();
|
||||
});
|
||||
}
|
||||
|
||||
// Define a macro to generate async methods
|
||||
#define REDIS_ASYNC_IMPL(method, return_type, operation) \
|
||||
void RedisHelper::method##Async(const std::string &key, \
|
||||
std::function<void(return_type)> callback) { \
|
||||
executeAsync<return_type>( \
|
||||
[this, key]() { \
|
||||
return _redis->operation; \
|
||||
}, \
|
||||
std::move(callback) \
|
||||
); \
|
||||
}
|
||||
|
||||
// Specialized async methods using the template
|
||||
|
||||
void RedisHelper::setexAsync(const std::string &key,
|
||||
const std::string &value,
|
||||
long ttlSeconds,
|
||||
std::function<void(bool)> callback) {
|
||||
executeAsync<bool>(
|
||||
[this, key, value, ttlSeconds]() {
|
||||
_redis->setex(key, ttlSeconds, value);
|
||||
return true;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::getAsync(const std::string &key,
|
||||
std::function<void(sw::redis::OptionalString)> callback) {
|
||||
executeAsync<sw::redis::OptionalString>(
|
||||
[this, key]() {
|
||||
return _redis->get(key);
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::delAsync(const std::string &key,
|
||||
std::function<void(bool)> callback) {
|
||||
executeAsync<bool>(
|
||||
[this, key]() {
|
||||
return _redis->del(key) > 0;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::saddAsync(const std::string &setName,
|
||||
const std::string &value,
|
||||
std::function<void(bool)> callback) {
|
||||
executeAsync<bool>(
|
||||
[this, setName, value]() {
|
||||
return _redis->sadd(setName, value) > 0;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::sremAsync(const std::string &setName,
|
||||
const std::string &value,
|
||||
std::function<void(bool)> callback) {
|
||||
executeAsync<bool>(
|
||||
[this, setName, value]() {
|
||||
return _redis->srem(setName, value) > 0;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::smembersAsync(const std::string &setName,
|
||||
std::function<void(std::vector<std::string>)> callback) {
|
||||
executeAsync<std::vector<std::string>>(
|
||||
[this, setName]() {
|
||||
std::vector<std::string> members;
|
||||
_redis->smembers(setName, std::back_inserter(members));
|
||||
return members;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::keysAsync(const std::string &pattern,
|
||||
std::function<void(std::vector<std::string>)> callback) {
|
||||
executeAsync<std::vector<std::string>>(
|
||||
[this, pattern]() {
|
||||
std::vector<std::string> keys;
|
||||
_redis->keys(pattern, std::back_inserter(keys));
|
||||
return keys;
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
void RedisHelper::expireAsync(const std::string &key,
|
||||
long ttlSeconds,
|
||||
std::function<void(bool)> callback) {
|
||||
executeAsync<bool>(
|
||||
[this, key, ttlSeconds]() {
|
||||
return _redis->expire(key, ttlSeconds);
|
||||
},
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
// Sync versions for compatibility
|
||||
std::unique_ptr<sw::redis::Redis> RedisHelper::getConnection() {
|
||||
ensureConnected();
|
||||
|
||||
sw::redis::ConnectionOptions opts;
|
||||
opts.host = getRedisHost();
|
||||
opts.port = getRedisPort();
|
||||
|
||||
const char* envPass = std::getenv("REDIS_PASS");
|
||||
if (envPass && strlen(envPass) > 0) {
|
||||
opts.password = envPass;
|
||||
}
|
||||
|
||||
opts.socket_timeout = std::chrono::milliseconds(200);
|
||||
opts.connect_timeout = std::chrono::milliseconds(200);
|
||||
|
||||
return std::make_unique<sw::redis::Redis>(opts);
|
||||
}
|
||||
|
||||
bool RedisHelper::storeKey(const std::string &key, const std::string &value, int ttl) {
|
||||
try {
|
||||
ensureConnected();
|
||||
if (ttl > 0) {
|
||||
_redis->setex(key, ttl, value);
|
||||
} else {
|
||||
_redis->set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (const sw::redis::Error &e) {
|
||||
LOG_ERROR << "Redis SET error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string RedisHelper::getKey(const std::string &key) {
|
||||
try {
|
||||
ensureConnected();
|
||||
auto val = _redis->get(key);
|
||||
return val.has_value() ? val.value() : "";
|
||||
} catch (const sw::redis::Error &e) {
|
||||
LOG_ERROR << "Redis GET error: " << e.what();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
bool RedisHelper::deleteKey(const std::string &key) {
|
||||
try {
|
||||
ensureConnected();
|
||||
return _redis->del(key) > 0;
|
||||
} catch (const sw::redis::Error &e) {
|
||||
LOG_ERROR << "Redis DEL error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RedisHelper::addToSet(const std::string &setName, const std::string &value) {
|
||||
try {
|
||||
ensureConnected();
|
||||
return _redis->sadd(setName, value) > 0;
|
||||
} catch (const sw::redis::Error &e) {
|
||||
LOG_ERROR << "Redis SADD error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RedisHelper::removeFromSet(const std::string &setName, const std::string &value) {
|
||||
try {
|
||||
ensureConnected();
|
||||
return _redis->srem(setName, value) > 0;
|
||||
} catch (const sw::redis::Error &e) {
|
||||
LOG_ERROR << "Redis SREM error: " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated command executor - simplified
|
||||
void RedisHelper::executeAsync(const std::string &command,
|
||||
std::function<void(bool, const std::string&)> callback) {
|
||||
// For the single use case in the code (EXPIRE), handle it directly
|
||||
std::istringstream iss(command);
|
||||
std::string op, key;
|
||||
long ttl;
|
||||
iss >> op >> key >> ttl;
|
||||
|
||||
if (op == "EXPIRE" || op == "expire") {
|
||||
expireAsync(key, ttl, [callback](bool success) {
|
||||
callback(success, success ? "1" : "0");
|
||||
});
|
||||
} else {
|
||||
if (auto loop = drogon::app().getLoop()) {
|
||||
loop->queueInLoop([callback]() {
|
||||
callback(false, "Unsupported command in executeAsync. Use specific async methods.");
|
||||
});
|
||||
} else {
|
||||
callback(false, "Unsupported command in executeAsync. Use specific async methods.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace services
|
||||
Loading…
Add table
Add a link
Reference in a new issue