From cb9bfeb7f92d781ea5be5ded9119dd3775f02585 Mon Sep 17 00:00:00 2001 From: Rafail Shakhin-ogly Date: Tue, 16 Jun 2026 02:38:03 +0300 Subject: [PATCH 1/3] feat redis: generic command for transactions --- .../include/userver/storages/redis/client.hpp | 3 +- .../userver/storages/redis/transaction.hpp | 15 +++++++++ redis/src/storages/redis/transaction_impl.cpp | 14 ++++++--- redis/src/storages/redis/transaction_impl.hpp | 31 ++++++++++++------- .../storages/redis/mock_transaction.hpp | 24 ++++++++++---- .../redis/mock_transaction_impl_base.hpp | 2 ++ .../src/storages/redis/mock_transaction.cpp | 24 +++++++------- .../redis/mock_transaction_impl_base.cpp | 8 +++++ 8 files changed, 88 insertions(+), 33 deletions(-) diff --git a/redis/include/userver/storages/redis/client.hpp b/redis/include/userver/storages/redis/client.hpp index 952263d0b3bb..00ed8d0355b4 100644 --- a/redis/include/userver/storages/redis/client.hpp +++ b/redis/include/userver/storages/redis/client.hpp @@ -158,7 +158,8 @@ class Client { size_t key_index, const CommandControl& command_control ) { - return RequestGeneric{GenericCommon(std::move(command), std::move(args), key_index, command_control) + return RequestGeneric{ + GenericCommon(std::move(command), std::move(args), key_index, command_control) }; } diff --git a/redis/include/userver/storages/redis/transaction.hpp b/redis/include/userver/storages/redis/transaction.hpp index b881712c4570..126968b858a5 100644 --- a/redis/include/userver/storages/redis/transaction.hpp +++ b/redis/include/userver/storages/redis/transaction.hpp @@ -9,6 +9,7 @@ #include #include #include +#include USERVER_NAMESPACE_BEGIN @@ -413,7 +414,21 @@ class Transaction { virtual RequestJsonMset JsonMset(std::vector key_path_values) = 0; + /// @brief Execute a custom Redis command. + /// @param key_index Index of the key in the args vector used to determine the shard + template + RequestGeneric GenericCommand(std::string command, std::vector args, size_t key_index) { + return RequestGeneric{GenericCommon(std::move(command), std::move(args), key_index)}; + } + // end of redis commands + +protected: + virtual RequestGenericCommon GenericCommon( + std::string command, + std::vector args, + size_t key_index + ) = 0; }; using TransactionPtr = std::unique_ptr; diff --git a/redis/src/storages/redis/transaction_impl.cpp b/redis/src/storages/redis/transaction_impl.cpp index d85a86a5d03a..95eae8207859 100644 --- a/redis/src/storages/redis/transaction_impl.cpp +++ b/redis/src/storages/redis/transaction_impl.cpp @@ -20,10 +20,7 @@ RequestExec CreateExecRequest(impl::Request&& request, std::vector client, CheckShards check_shards) - : client_(std::move(client)), - check_shards_(check_shards), - cmd_args_({"MULTI"}) -{} + : client_(std::move(client)), check_shards_(check_shards), cmd_args_({"MULTI"}) {} RequestExec TransactionImpl::Exec(const CommandControl& command_control) { if (!shard_) { @@ -1041,6 +1038,15 @@ RequestJsonMset TransactionImpl::JsonMset(std::vector key_path return AddCmd("json.mset", true, std::move(args)); } +RequestGenericCommon TransactionImpl::GenericCommon( + std::string command, + std::vector args, + size_t key_index +) { + UpdateShard(args.at(key_index)); + return AddCmd(std::move(command), true, std::move(args)); +} + // end of redis commands void TransactionImpl::UpdateShard(const std::string& key) { diff --git a/redis/src/storages/redis/transaction_impl.hpp b/redis/src/storages/redis/transaction_impl.hpp index 8e1e8f2530dc..ec4935f8eaaa 100644 --- a/redis/src/storages/redis/transaction_impl.hpp +++ b/redis/src/storages/redis/transaction_impl.hpp @@ -28,8 +28,7 @@ class TransactionImpl final : public Transaction { public: template ResultPromise(engine::Promise&& promise, To>) - : impl_(std::make_unique>(std::move(promise))) - {} + : impl_(std::make_unique>(std::move(promise))) {} ResultPromise(ResultPromise&& other) = default; void ProcessReply(ReplyData&& reply_data, const std::string& request_description) { @@ -47,9 +46,7 @@ class TransactionImpl final : public Transaction { template class ResultPromiseImpl : public ResultPromiseImplBase { public: - ResultPromiseImpl(engine::Promise&& promise) - : promise_(std::move(promise)) - {} + ResultPromiseImpl(engine::Promise&& promise) : promise_(std::move(promise)) {} void ProcessReply(ReplyData&& reply_data, const std::string& request_description) override { try { @@ -239,13 +236,19 @@ class TransactionImpl final : public Transaction { RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value) override; - RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value, std::chrono::milliseconds ttl) - override; + RequestSetIfNotExistOrGet SetIfNotExistOrGet( + std::string key, + std::string value, + std::chrono::milliseconds ttl + ) override; RequestSetex Setex(std::string key, std::chrono::seconds seconds, std::string value) override; - RequestSetAndGetPrevious SetAndGetPrevious(std::string key, std::string value, std::chrono::milliseconds ttl) - override; + RequestSetAndGetPrevious SetAndGetPrevious( + std::string key, + std::string value, + std::chrono::milliseconds ttl + ) override; RequestSismember Sismember(std::string key, std::string member) override; @@ -295,8 +298,12 @@ class TransactionImpl final : public Transaction { RequestZrangebyscore Zrangebyscore(std::string key, std::string min, std::string max) override; - RequestZrangebyscore Zrangebyscore(std::string key, double min, double max, const RangeOptions& range_options) - override; + RequestZrangebyscore Zrangebyscore( + std::string key, + double min, + double max, + const RangeOptions& range_options + ) override; RequestZrangebyscore Zrangebyscore( std::string key, @@ -417,6 +424,8 @@ class TransactionImpl final : public Transaction { RequestJsonMset JsonMset(std::vector key_path_values) override; + RequestGenericCommon GenericCommon(std::string command, std::vector args, size_t key_index) override; + // end of redis commands private: diff --git a/redis/testing/include/userver/storages/redis/mock_transaction.hpp b/redis/testing/include/userver/storages/redis/mock_transaction.hpp index 6174ef123f3e..b5d2a7e45718 100644 --- a/redis/testing/include/userver/storages/redis/mock_transaction.hpp +++ b/redis/testing/include/userver/storages/redis/mock_transaction.hpp @@ -189,13 +189,19 @@ class MockTransaction final : public Transaction { RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value) override; - RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value, std::chrono::milliseconds ttl) - override; + RequestSetIfNotExistOrGet SetIfNotExistOrGet( + std::string key, + std::string value, + std::chrono::milliseconds ttl + ) override; RequestSetex Setex(std::string key, std::chrono::seconds seconds, std::string value) override; - RequestSetAndGetPrevious SetAndGetPrevious(std::string key, std::string value, std::chrono::milliseconds ttl) - override; + RequestSetAndGetPrevious SetAndGetPrevious( + std::string key, + std::string value, + std::chrono::milliseconds ttl + ) override; RequestSismember Sismember(std::string key, std::string member) override; @@ -245,8 +251,12 @@ class MockTransaction final : public Transaction { RequestZrangebyscore Zrangebyscore(std::string key, std::string min, std::string max) override; - RequestZrangebyscore Zrangebyscore(std::string key, double min, double max, const RangeOptions& range_options) - override; + RequestZrangebyscore Zrangebyscore( + std::string key, + double min, + double max, + const RangeOptions& range_options + ) override; RequestZrangebyscore Zrangebyscore( std::string key, @@ -363,6 +373,8 @@ class MockTransaction final : public Transaction { RequestJsonMset JsonMset(std::vector key_path_values) override; + RequestGenericCommon GenericCommon(std::string command, std::vector args, size_t key_index) override; + // end of redis commands private: diff --git a/redis/testing/include/userver/storages/redis/mock_transaction_impl_base.hpp b/redis/testing/include/userver/storages/redis/mock_transaction_impl_base.hpp index ccfca6788fa0..88accc5fa7e7 100644 --- a/redis/testing/include/userver/storages/redis/mock_transaction_impl_base.hpp +++ b/redis/testing/include/userver/storages/redis/mock_transaction_impl_base.hpp @@ -363,6 +363,8 @@ class MockTransactionImplBase { virtual RequestJsonMset JsonMset(std::vector key_path_values); + virtual RequestGenericCommon GenericCommon(std::string command, std::vector args, size_t key_index); + // end of redis commands }; diff --git a/redis/testing/src/storages/redis/mock_transaction.cpp b/redis/testing/src/storages/redis/mock_transaction.cpp index aa77a1de7d5d..3cc4b7d850dc 100644 --- a/redis/testing/src/storages/redis/mock_transaction.cpp +++ b/redis/testing/src/storages/redis/mock_transaction.cpp @@ -15,8 +15,7 @@ class MockTransaction::ResultPromise { public: template ResultPromise(engine::Promise&& promise, Request&& subrequest) - : impl_(std::make_unique>(std::move(promise), std::move(subrequest))) - {} + : impl_(std::make_unique>(std::move(promise), std::move(subrequest))) {} ResultPromise(ResultPromise&& other) = default; @@ -34,9 +33,7 @@ class MockTransaction::ResultPromise { class ResultPromiseImpl : public ResultPromiseImplBase { public: ResultPromiseImpl(engine::Promise&& promise, Request&& subrequest) - : promise_(std::move(promise)), - subrequest_(std::move(subrequest)) - {} + : promise_(std::move(promise)), subrequest_(std::move(subrequest)) {} void ProcessReply(const std::string& request_description) override { try { @@ -64,8 +61,7 @@ class MockTransaction::ResultPromise { class MockTransaction::MockRequestExecDataImpl final : public RequestDataBase { public: MockRequestExecDataImpl(std::vector>&& result_promises) - : result_promises_(std::move(result_promises)) - {} + : result_promises_(std::move(result_promises)) {} void Wait() override {} @@ -94,10 +90,7 @@ MockTransaction::MockTransaction( std::unique_ptr impl, CheckShards check_shards ) - : client_(std::move(client)), - check_shards_(check_shards), - impl_(std::move(impl)) -{} + : client_(std::move(client)), check_shards_(check_shards), impl_(std::move(impl)) {} MockTransaction::~MockTransaction() = default; @@ -873,6 +866,15 @@ RequestJsonMset MockTransaction::JsonMset(std::vector key_path return AddSubrequest(impl_->JsonMset(std::move(key_path_values))); } +RequestGenericCommon MockTransaction::GenericCommon( + std::string command, + std::vector args, + size_t key_index +) { + UpdateShard(args.at(key_index)); + return AddSubrequest(impl_->GenericCommon(std::move(command), std::move(args), key_index)); +} + // end of redis commands void MockTransaction::UpdateShard(const std::string& key) { diff --git a/redis/testing/src/storages/redis/mock_transaction_impl_base.cpp b/redis/testing/src/storages/redis/mock_transaction_impl_base.cpp index 7ad4f58e8665..971114a4aa8c 100644 --- a/redis/testing/src/storages/redis/mock_transaction_impl_base.cpp +++ b/redis/testing/src/storages/redis/mock_transaction_impl_base.cpp @@ -665,6 +665,14 @@ RequestJsonMset MockTransactionImplBase::JsonMset(std::vector AbortWithStacktrace("Redis method not mocked"); } +RequestGenericCommon MockTransactionImplBase::GenericCommon( + std::string /*command*/, + std::vector /*args*/, + size_t /*key_index*/ +) { + AbortWithStacktrace("Redis method not mocked"); +} + // end of redis commands } // namespace storages::redis From 46e172c172b1bbe069a58df948fc72328b630487 Mon Sep 17 00:00:00 2001 From: Rafail Shakhin-ogly Date: Tue, 16 Jun 2026 02:48:39 +0300 Subject: [PATCH 2/3] format --- .../include/userver/storages/redis/client.hpp | 3 +- redis/src/storages/redis/transaction_impl.cpp | 5 +++- redis/src/storages/redis/transaction_impl.hpp | 28 +++++++------------ .../storages/redis/mock_transaction.hpp | 22 ++++----------- .../src/storages/redis/mock_transaction.cpp | 15 +++++++--- 5 files changed, 32 insertions(+), 41 deletions(-) diff --git a/redis/include/userver/storages/redis/client.hpp b/redis/include/userver/storages/redis/client.hpp index 00ed8d0355b4..952263d0b3bb 100644 --- a/redis/include/userver/storages/redis/client.hpp +++ b/redis/include/userver/storages/redis/client.hpp @@ -158,8 +158,7 @@ class Client { size_t key_index, const CommandControl& command_control ) { - return RequestGeneric{ - GenericCommon(std::move(command), std::move(args), key_index, command_control) + return RequestGeneric{GenericCommon(std::move(command), std::move(args), key_index, command_control) }; } diff --git a/redis/src/storages/redis/transaction_impl.cpp b/redis/src/storages/redis/transaction_impl.cpp index 95eae8207859..b6b7f2b487d0 100644 --- a/redis/src/storages/redis/transaction_impl.cpp +++ b/redis/src/storages/redis/transaction_impl.cpp @@ -20,7 +20,10 @@ RequestExec CreateExecRequest(impl::Request&& request, std::vector client, CheckShards check_shards) - : client_(std::move(client)), check_shards_(check_shards), cmd_args_({"MULTI"}) {} + : client_(std::move(client)), + check_shards_(check_shards), + cmd_args_({"MULTI"}) +{} RequestExec TransactionImpl::Exec(const CommandControl& command_control) { if (!shard_) { diff --git a/redis/src/storages/redis/transaction_impl.hpp b/redis/src/storages/redis/transaction_impl.hpp index ec4935f8eaaa..ca8b525f9a20 100644 --- a/redis/src/storages/redis/transaction_impl.hpp +++ b/redis/src/storages/redis/transaction_impl.hpp @@ -28,7 +28,8 @@ class TransactionImpl final : public Transaction { public: template ResultPromise(engine::Promise&& promise, To>) - : impl_(std::make_unique>(std::move(promise))) {} + : impl_(std::make_unique>(std::move(promise))) + {} ResultPromise(ResultPromise&& other) = default; void ProcessReply(ReplyData&& reply_data, const std::string& request_description) { @@ -46,7 +47,8 @@ class TransactionImpl final : public Transaction { template class ResultPromiseImpl : public ResultPromiseImplBase { public: - ResultPromiseImpl(engine::Promise&& promise) : promise_(std::move(promise)) {} + ResultPromiseImpl(engine::Promise&& promise) : promise_(std::move(promise)) + {} void ProcessReply(ReplyData&& reply_data, const std::string& request_description) override { try { @@ -236,19 +238,13 @@ class TransactionImpl final : public Transaction { RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value) override; - RequestSetIfNotExistOrGet SetIfNotExistOrGet( - std::string key, - std::string value, - std::chrono::milliseconds ttl - ) override; + RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value, std::chrono::milliseconds ttl) + override; RequestSetex Setex(std::string key, std::chrono::seconds seconds, std::string value) override; - RequestSetAndGetPrevious SetAndGetPrevious( - std::string key, - std::string value, - std::chrono::milliseconds ttl - ) override; + RequestSetAndGetPrevious SetAndGetPrevious(std::string key, std::string value, std::chrono::milliseconds ttl) + override; RequestSismember Sismember(std::string key, std::string member) override; @@ -298,12 +294,8 @@ class TransactionImpl final : public Transaction { RequestZrangebyscore Zrangebyscore(std::string key, std::string min, std::string max) override; - RequestZrangebyscore Zrangebyscore( - std::string key, - double min, - double max, - const RangeOptions& range_options - ) override; + RequestZrangebyscore Zrangebyscore(std::string key, double min, double max, const RangeOptions& range_options) + override; RequestZrangebyscore Zrangebyscore( std::string key, diff --git a/redis/testing/include/userver/storages/redis/mock_transaction.hpp b/redis/testing/include/userver/storages/redis/mock_transaction.hpp index b5d2a7e45718..ca002d4b7abd 100644 --- a/redis/testing/include/userver/storages/redis/mock_transaction.hpp +++ b/redis/testing/include/userver/storages/redis/mock_transaction.hpp @@ -189,19 +189,13 @@ class MockTransaction final : public Transaction { RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value) override; - RequestSetIfNotExistOrGet SetIfNotExistOrGet( - std::string key, - std::string value, - std::chrono::milliseconds ttl - ) override; + RequestSetIfNotExistOrGet SetIfNotExistOrGet(std::string key, std::string value, std::chrono::milliseconds ttl) + override; RequestSetex Setex(std::string key, std::chrono::seconds seconds, std::string value) override; - RequestSetAndGetPrevious SetAndGetPrevious( - std::string key, - std::string value, - std::chrono::milliseconds ttl - ) override; + RequestSetAndGetPrevious SetAndGetPrevious(std::string key, std::string value, std::chrono::milliseconds ttl) + override; RequestSismember Sismember(std::string key, std::string member) override; @@ -251,12 +245,8 @@ class MockTransaction final : public Transaction { RequestZrangebyscore Zrangebyscore(std::string key, std::string min, std::string max) override; - RequestZrangebyscore Zrangebyscore( - std::string key, - double min, - double max, - const RangeOptions& range_options - ) override; + RequestZrangebyscore Zrangebyscore(std::string key, double min, double max, const RangeOptions& range_options) + override; RequestZrangebyscore Zrangebyscore( std::string key, diff --git a/redis/testing/src/storages/redis/mock_transaction.cpp b/redis/testing/src/storages/redis/mock_transaction.cpp index 3cc4b7d850dc..25140c81f4b4 100644 --- a/redis/testing/src/storages/redis/mock_transaction.cpp +++ b/redis/testing/src/storages/redis/mock_transaction.cpp @@ -15,7 +15,8 @@ class MockTransaction::ResultPromise { public: template ResultPromise(engine::Promise&& promise, Request&& subrequest) - : impl_(std::make_unique>(std::move(promise), std::move(subrequest))) {} + : impl_(std::make_unique>(std::move(promise), std::move(subrequest))) + {} ResultPromise(ResultPromise&& other) = default; @@ -33,7 +34,9 @@ class MockTransaction::ResultPromise { class ResultPromiseImpl : public ResultPromiseImplBase { public: ResultPromiseImpl(engine::Promise&& promise, Request&& subrequest) - : promise_(std::move(promise)), subrequest_(std::move(subrequest)) {} + : promise_(std::move(promise)), + subrequest_(std::move(subrequest)) + {} void ProcessReply(const std::string& request_description) override { try { @@ -61,7 +64,8 @@ class MockTransaction::ResultPromise { class MockTransaction::MockRequestExecDataImpl final : public RequestDataBase { public: MockRequestExecDataImpl(std::vector>&& result_promises) - : result_promises_(std::move(result_promises)) {} + : result_promises_(std::move(result_promises)) + {} void Wait() override {} @@ -90,7 +94,10 @@ MockTransaction::MockTransaction( std::unique_ptr impl, CheckShards check_shards ) - : client_(std::move(client)), check_shards_(check_shards), impl_(std::move(impl)) {} + : client_(std::move(client)), + check_shards_(check_shards), + impl_(std::move(impl)) +{} MockTransaction::~MockTransaction() = default; From a773082d9073e00ce78b128e14b32f8e2e7483fb Mon Sep 17 00:00:00 2001 From: Rafail Shakhin-ogly Date: Tue, 16 Jun 2026 02:51:06 +0300 Subject: [PATCH 3/3] format2 --- redis/src/storages/redis/transaction_impl.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/redis/src/storages/redis/transaction_impl.hpp b/redis/src/storages/redis/transaction_impl.hpp index ca8b525f9a20..7bbfe11fa1a8 100644 --- a/redis/src/storages/redis/transaction_impl.hpp +++ b/redis/src/storages/redis/transaction_impl.hpp @@ -47,7 +47,8 @@ class TransactionImpl final : public Transaction { template class ResultPromiseImpl : public ResultPromiseImplBase { public: - ResultPromiseImpl(engine::Promise&& promise) : promise_(std::move(promise)) + ResultPromiseImpl(engine::Promise&& promise) + : promise_(std::move(promise)) {} void ProcessReply(ReplyData&& reply_data, const std::string& request_description) override {