A workaround for redis sync exec (#1216)
This commit is contained in:
parent
86dbf9875b
commit
bccf509ca0
|
@ -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
|
||||
|
@ -61,7 +62,7 @@ struct [[nodiscard]] RedisAwaiter : public CallbackAwaiter<RedisResult>
|
|||
};
|
||||
|
||||
struct [[nodiscard]] RedisTransactionAwaiter
|
||||
: public CallbackAwaiter<std::shared_ptr<RedisTransaction> >
|
||||
: public CallbackAwaiter<std::shared_ptr<RedisTransaction>>
|
||||
{
|
||||
RedisTransactionAwaiter(RedisClient *client) : client_(client)
|
||||
{
|
||||
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue