userver: userver/storages/redis/hedged_request.hpp Source File
Loading...
Searching...
No Matches
hedged_request.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/redis/hedged_request.hpp
4/// @brief
5/// Classes and functions for performing hedged requests.
6///
7/// Use MakeHedgedRedisRequest method to perform hedged redis request.
8///
9/// Example(Sync):
10/// auto result =
11/// ::redis::MakeHedgedRedisRequest<storages::redis::RequestHGet>(
12/// redis_client_shared_ptr,
13/// &storages::redis::Client::Hget,
14/// redis_cc, hedging_settings,
15/// key, field);
16/// Example(Async):
17/// auto future =
18/// ::redis::MakeHedgedRedisRequestAsync<storages::redis::RequestHGet>(
19/// redis_client_shared_ptr,
20/// &storages::redis::Client::Hget,
21/// redis_cc, hedging_settings,
22/// key, field);
23/// auto result = future.Get();
24///
25
26#include <optional>
27
28#include <userver/storages/redis/client.hpp>
29#include <userver/storages/redis/command_control.hpp>
30#include <userver/utils/hedged_request.hpp>
31
32USERVER_NAMESPACE_BEGIN
33
34namespace redis {
35namespace impl {
36
37template <typename RedisRequestType>
38struct RedisRequestStrategy {
39public:
40 using RequestType = RedisRequestType;
41 using ReplyType = typename RequestType::Reply;
42 using GenF = std::function<std::optional<RequestType>(int)>;
43
44 explicit RedisRequestStrategy(GenF gen_callback) : gen_callback_(std::move(gen_callback)) {}
45
46 RedisRequestStrategy(RedisRequestStrategy&& other) noexcept = default;
47
48 /// @{
49 /// Methods needed by HedgingStrategy
50 std::optional<RequestType> Create(size_t try_count) { return gen_callback_(try_count); }
51 std::optional<std::chrono::milliseconds> ProcessReply(RequestType&& request) {
52 reply_ = std::move(request).Get();
53 return std::nullopt;
54 }
55 std::optional<ReplyType> ExtractReply() { return std::move(reply_); }
56 void Finish(RequestType&&) {}
57 /// @}
58
59private:
60 GenF gen_callback_;
61 std::optional<ReplyType> reply_;
62};
63
64} // namespace impl
65
66template <typename RedisRequestType>
67using HedgedRedisRequest = utils::hedging::HedgedRequestFuture<impl::RedisRequestStrategy<RedisRequestType>>;
68
69template <
70 typename RedisRequestType,
71 typename... Args,
72 typename M = RedisRequestType (storages::redis::Client::*)(Args..., const redis::CommandControl&)>
75 M method,
76 const redis::CommandControl& cc,
78 Args... args
79) {
80 auto gen_request = [redis, method, cc{std::move(cc)}, args_tuple{std::tuple(std::move(args)...)}](int try_count
81 ) mutable -> std::optional<RedisRequestType> {
82 cc.retry_counter = try_count;
83 cc.max_retries = 1; ///< We do retries ourselves
84
85 return std::apply(
86 [redis, method, cc](auto&&... args) { return (redis.get()->*method)(args..., cc); }, args_tuple
87 );
88 };
89 return utils::hedging::HedgeRequestAsync(
90 impl::RedisRequestStrategy<RedisRequestType>(std::move(gen_request)), hedging_settings
91 );
92}
93
94template <typename RedisRequestType>
95using HedgedRedisRequest = utils::hedging::HedgedRequestFuture<impl::RedisRequestStrategy<RedisRequestType>>;
96
97template <
98 typename RedisRequestType,
99 typename... Args,
100 typename M = RedisRequestType (storages::redis::Client::*)(Args..., const redis::CommandControl&)>
101std::optional<typename RedisRequestType::Reply> MakeHedgedRedisRequest(
102 std::shared_ptr<storages::redis::Client> redis,
103 M method,
104 const redis::CommandControl& cc,
105 utils::hedging::HedgingSettings hedging_settings,
106 Args... args
107) {
108 auto gen_request = [redis, method, cc{std::move(cc)}, args_tuple{std::tuple(std::move(args)...)}](int try_count
109 ) mutable -> std::optional<RedisRequestType> {
110 cc.retry_counter = try_count;
111 cc.max_retries = 1; ///< We do retries ourselves
112
113 return std::apply(
114 [redis, method, cc](auto&&... args) { return (redis.get()->*method)(args..., cc); }, args_tuple
115 );
116 };
117 return utils::hedging::HedgeRequest(
118 impl::RedisRequestStrategy<RedisRequestType>(std::move(gen_request)), hedging_settings
119 );
120}
121
122/// Same as MakeHedgedRedisRequest but accepting a vector of argument tuples
123/// instead of a single set of arguments. And return vector of replies
124/// corresponding to input args.
125template <
126 typename RedisRequestType,
127 typename... Args,
128 typename M = RedisRequestType (storages::redis::Client::*)(Args..., const redis::CommandControl&)>
131 M method,
132 const redis::CommandControl& cc,
134 std::vector<std::tuple<Args...>> args
135) {
136 std::vector<redis::impl::RedisRequestStrategy<RedisRequestType>> strategies;
137 strategies.reserve(args.size());
138
139 for (auto&& arg : args) {
140 auto gen_request = [redis, method, cc{std::move(cc)}, args_tuple{std::move(arg)}](int try_count
141 ) mutable -> std::optional<RedisRequestType> {
142 cc.retry_counter = try_count;
143 cc.max_retries = 1; ///< We do retries ourselves
144
145 return std::apply(
146 [redis, method, cc](auto&&... args) { return (redis.get()->*method)(args..., cc); }, args_tuple
147 );
148 };
149 strategies.emplace_back(std::move(gen_request));
150 }
151
152 return utils::hedging::HedgeRequestsBulk(std::move(strategies), hedging_settings);
153}
154
155/// Same as MakeHedgedRedisRequestAsync but accepting a vector of argument
156/// tuples instead of a single set of arguments. And return vector of replies
157/// corresponding to input args.
158template <
159 typename RedisRequestType,
160 typename... Args,
161 typename M = RedisRequestType (storages::redis::Client::*)(Args..., const redis::CommandControl&)>
162utils::hedging::HedgedRequestBulkFuture<redis::impl::RedisRequestStrategy<RedisRequestType>>
164 std::shared_ptr<storages::redis::Client> redis,
165 M method,
166 const redis::CommandControl& cc,
167 utils::hedging::HedgingSettings hedging_settings,
168 std::vector<std::tuple<Args...>> args
169) {
170 using RequestStrategy = redis::impl::RedisRequestStrategy<RedisRequestType>;
171 std::vector<RequestStrategy> strategies;
172 strategies.reserve(args.size());
173
174 for (auto&& arg : args) {
175 auto gen_request = [redis, method, cc{std::move(cc)}, args_tuple{std::move(arg)}](int try_count
176 ) mutable -> std::optional<RedisRequestType> {
177 cc.retry_counter = try_count;
178 cc.max_retries = 1; ///< We do retries ourselves
179
180 return std::apply(
181 [redis, method, cc](auto&&... args) { return (redis.get()->*method)(args..., cc); }, args_tuple
182 );
183 };
184 strategies.emplace_back(std::move(gen_request));
185 }
186
187 return utils::hedging::HedgeRequestsBulkAsync<RequestStrategy>(std::move(strategies), hedging_settings);
188}
189
190} // namespace redis
191
192USERVER_NAMESPACE_END