A workaround for redis sync exec (#1216)

This commit is contained in:
Nitromelon 2022-04-25 19:42:25 +08:00 committed by GitHub
parent 86dbf9875b
commit bccf509ca0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 155 additions and 2 deletions

View File

@ -21,6 +21,7 @@
#include <trantor/utils/Logger.h>
#include <memory>
#include <functional>
#include <future>
#ifdef __cpp_impl_coroutine
#include <drogon/utils/coroutine.h>
#endif
@ -122,6 +123,62 @@ class DROGON_EXPORT RedisClient
string_view command,
...) noexcept = 0;
/**
* @brief Execute a redis command synchronously
*
* @param processFunc Function to extract data from redis result.
* received successfully.
* @param command The command to be executed. the command string can contain
* some placeholders for parameters, such as '%s', '%d', etc.
* @param ... The command parameters.
* @return Returns the same value as process callback.
* For example:
* @code
try
{
std::string res = redisClientPtr->execCommandSync<std::string>(
[](const RedisResult &r){
return r.asString();
},
"get %s",
key.data()
);
}
catch (const RedisException & err)
{
}
catch (const std::exception & err)
{
}
@endcode
*/
template <typename T, typename... Args>
T execCommandSync(std::function<T(const RedisResult &)> &&processFunc,
string_view command,
Args &&...args)
{
std::shared_ptr<std::promise<T>> pro(new std::promise<T>);
std::future<T> f = pro->get_future();
execCommandAsync(
[process = std::move(processFunc), pro](const RedisResult &result) {
try
{
pro->set_value(process(result));
}
catch (...)
{
pro->set_exception(std::current_exception());
}
},
[pro](const RedisException &err) {
pro->set_exception(std::make_exception_ptr(err));
},
command,
std::forward<Args>(args)...);
return f.get();
}
/**
* @brief Create a redis transaction object.
*

View File

@ -41,7 +41,7 @@ DROGON_TEST(RedisTest)
MANDATE(r.asArray().size() == 1UL);
},
[TEST_CTX](const std::exception &err) { MANDATE(err.what()); },
"keys *");
"keys id_*");
// 5
redisClient->execCommandAsync(
[TEST_CTX](const drogon::nosql::RedisResult &r) {
@ -80,6 +80,102 @@ DROGON_TEST(RedisTest)
};
drogon::sync_wait(coro_test());
#endif
// 9. Test sync
try
{
auto res = redisClient->execCommandSync<std::string>(
[](const RedisResult &result) { return result.asString(); },
"set %s %s",
"sync_key",
"sync_value");
MANDATE(res == "OK");
}
catch (const RedisException &err)
{
MANDATE(err.what());
}
try
{
auto [isNull, str] =
redisClient->execCommandSync<std::pair<bool, std::string>>(
[](const RedisResult &result) -> std::pair<bool, std::string> {
if (result.isNil())
{
return {true, ""};
}
return {false, result.asString()};
},
"get %s",
"sync_key");
MANDATE(isNull == false);
MANDATE(str == "sync_value");
}
catch (const RedisException &err)
{
MANDATE(err.what());
}
// 10. Test sync redis exception
try
{
auto [isNull, str] =
redisClient->execCommandSync<std::pair<bool, std::string>>(
[](const RedisResult &result) -> std::pair<bool, std::string> {
if (result.isNil())
{
return {true, ""};
}
return {false, result.asString()};
},
"get %s %s",
"sync_key",
"sync_key");
MANDATE(false);
}
catch (const RedisException &err)
{
LOG_INFO << "Successfully catch sync error: " << err.what();
MANDATE(err.code() == RedisErrorCode::kRedisError);
SUCCESS();
}
// 11. Test sync process function exception
try
{
auto value = redisClient->execCommandSync<std::string>(
[](const RedisResult &result) {
if (result.isNil())
{
throw std::runtime_error("Key not exists");
}
return result.asString();
},
"get %s",
"not_exists");
MANDATE(false);
}
catch (const RedisException &err)
{
MANDATE(false);
}
catch (const std::runtime_error &err)
{
MANDATE(std::string("Key not exists") == err.what());
SUCCESS();
}
try
{
redisClient->execCommandSync<int>([](const RedisResult &) { return 1; },
"del %s",
"sync_key");
}
catch (const RedisException &err)
{
MANDATE(err.what());
}
}
int main(int argc, char **argv)