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 {
39 public:
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)
45 : gen_callback_(std::move(gen_callback)) {}
46
47 RedisRequestStrategy(RedisRequestStrategy&& other) noexcept = default;
48
49 /// @{
50 /// Methods needed by HedgingStrategy
51 std::optional<RequestType> Create(size_t try_count) {
52 return gen_callback_(try_count);
53 }
54 std::optional<std::chrono::milliseconds> ProcessReply(RequestType&& request) {
55 reply_ = std::move(request).Get();
56 return std::nullopt;
57 }
58 std::optional<ReplyType> ExtractReply() { return std::move(reply_); }
59 void Finish(RequestType&&) {}
60 /// @}
61
62 private:
63 GenF gen_callback_;
64 std::optional<ReplyType> reply_;
65};
66
67} // namespace impl
68
69template <typename RedisRequestType>
70using HedgedRedisRequest = utils::hedging::HedgedRequestFuture<
71 impl::RedisRequestStrategy<RedisRequestType>>;
72
73template <typename RedisRequestType, typename... Args,
74 typename M = RedisRequestType (storages::redis::Client::*)(
75 Args..., const redis::CommandControl&)>
78 const redis::CommandControl& cc,
80 auto gen_request =
81 [redis, method, cc{std::move(cc)},
82 args_tuple{std::tuple(std::move(args)...)}](
83 int try_count) mutable -> std::optional<RedisRequestType> {
84 cc.retry_counter = try_count;
85 cc.max_retries = 1; ///< We do retries ourselves
86
87 return std::apply(
88 [redis, method, cc](auto&&... args) {
89 return (redis.get()->*method)(args..., cc);
90 },
91 args_tuple);
92 };
93 return utils::hedging::HedgeRequestAsync(
94 impl::RedisRequestStrategy<RedisRequestType>(std::move(gen_request)),
95 hedging_settings);
96}
97
98template <typename RedisRequestType>
99using HedgedRedisRequest = utils::hedging::HedgedRequestFuture<
100 impl::RedisRequestStrategy<RedisRequestType>>;
101
102template <typename RedisRequestType, typename... Args,
103 typename M = RedisRequestType (storages::redis::Client::*)(
104 Args..., const redis::CommandControl&)>
105std::optional<typename RedisRequestType::Reply> MakeHedgedRedisRequest(
106 std::shared_ptr<storages::redis::Client> redis, M method,
107 const redis::CommandControl& cc,
108 utils::hedging::HedgingSettings hedging_settings, Args... args) {
109 auto gen_request =
110 [redis, method, cc{std::move(cc)},
111 args_tuple{std::tuple(std::move(args)...)}](
112 int try_count) mutable -> std::optional<RedisRequestType> {
113 cc.retry_counter = try_count;
114 cc.max_retries = 1; ///< We do retries ourselves
115
116 return std::apply(
117 [redis, method, cc](auto&&... args) {
118 return (redis.get()->*method)(args..., cc);
119 },
120 args_tuple);
121 };
122 return utils::hedging::HedgeRequest(
123 impl::RedisRequestStrategy<RedisRequestType>(std::move(gen_request)),
124 hedging_settings);
125}
126
127/// Same as MakeHedgedRedisRequest but accepting a vector of argument tuples
128/// instead of a single set of arguments. And return vector of replies
129/// corresponding to input args.
130template <typename RedisRequestType, typename... Args,
131 typename M = RedisRequestType (storages::redis::Client::*)(
132 Args..., const redis::CommandControl&)>
135 M method, const redis::CommandControl& cc,
137 std::vector<std::tuple<Args...>> args) {
138 std::vector<redis::impl::RedisRequestStrategy<RedisRequestType>> strategies;
139 strategies.reserve(args.size());
140
141 for (auto&& arg : args) {
142 auto gen_request =
143 [redis, method, cc{std::move(cc)}, args_tuple{std::move(arg)}](
144 int try_count) mutable -> std::optional<RedisRequestType> {
145 cc.retry_counter = try_count;
146 cc.max_retries = 1; ///< We do retries ourselves
147
148 return std::apply(
149 [redis, method, cc](auto&&... args) {
150 return (redis.get()->*method)(args..., cc);
151 },
152 args_tuple);
153 };
154 strategies.emplace_back(std::move(gen_request));
155 }
156
157 return utils::hedging::HedgeRequestsBulk(std::move(strategies),
158 hedging_settings);
159}
160
161/// Same as MakeHedgedRedisRequestAsync but accepting a vector of argument
162/// tuples instead of a single set of arguments. And return vector of replies
163/// corresponding to input args.
164template <typename RedisRequestType, typename... Args,
165 typename M = RedisRequestType (storages::redis::Client::*)(
166 Args..., const redis::CommandControl&)>
167utils::hedging::HedgedRequestBulkFuture<
168 redis::impl::RedisRequestStrategy<RedisRequestType>>
170 std::shared_ptr<storages::redis::Client> redis, M method,
171 const redis::CommandControl& cc,
172 utils::hedging::HedgingSettings hedging_settings,
173 std::vector<std::tuple<Args...>> args) {
174 using RequestStrategy = redis::impl::RedisRequestStrategy<RedisRequestType>;
175 std::vector<RequestStrategy> strategies;
176 strategies.reserve(args.size());
177
178 for (auto&& arg : args) {
179 auto gen_request =
180 [redis, method, cc{std::move(cc)}, args_tuple{std::move(arg)}](
181 int try_count) mutable -> std::optional<RedisRequestType> {
182 cc.retry_counter = try_count;
183 cc.max_retries = 1; ///< We do retries ourselves
184
185 return std::apply(
186 [redis, method, cc](auto&&... args) {
187 return (redis.get()->*method)(args..., cc);
188 },
189 args_tuple);
190 };
191 strategies.emplace_back(std::move(gen_request));
192 }
193
194 return utils::hedging::HedgeRequestsBulkAsync<RequestStrategy>(
195 std::move(strategies), hedging_settings);
196}
197
198} // namespace redis
199
200USERVER_NAMESPACE_END