11
11
#include < boost/redis/request.hpp>
12
12
#include < boost/redis/response.hpp>
13
13
#include < boost/redis/operation.hpp>
14
- #include < boost/redis/detail/read_ops.hpp>
15
- #include < boost/asio/experimental/promise.hpp>
16
- #include < boost/asio/experimental/use_promise.hpp>
14
+ #include < boost/redis/detail/helper.hpp>
17
15
#include < boost/asio/steady_timer.hpp>
18
16
#include < boost/asio/compose.hpp>
19
17
#include < boost/asio/consign.hpp>
18
+ #include < boost/asio/coroutine.hpp>
19
+ #include < boost/asio/experimental/parallel_group.hpp>
20
20
#include < memory>
21
21
#include < chrono>
22
- #include < optional>
23
22
24
23
namespace boost ::redis {
25
24
namespace detail {
26
25
27
26
template <class HealthChecker , class Connection >
28
- class check_health_op {
27
+ class ping_op {
29
28
public:
30
- HealthChecker* checker = nullptr ;
31
- Connection* conn = nullptr ;
29
+ HealthChecker* checker_ = nullptr ;
30
+ Connection* conn_ = nullptr ;
32
31
asio::coroutine coro_{};
33
32
34
33
template <class Self >
35
34
void operator ()(Self& self, system::error_code ec = {}, std::size_t = 0 )
36
35
{
37
36
BOOST_ASIO_CORO_REENTER (coro_) for (;;)
38
37
{
39
- checker->prom_ .emplace (conn->async_exec (checker->req_ , checker->resp_ , asio::experimental::use_promise));
38
+ if (checker_->checker_has_exited_ ) {
39
+ self.complete ({});
40
+ return ;
41
+ }
40
42
41
- checker->timer_ .expires_after (checker->timeout_ );
42
43
BOOST_ASIO_CORO_YIELD
43
- checker->timer_ .async_wait (std::move (self));
44
- if (ec || is_cancelled (self) || checker->resp_ .value ().empty ()) {
45
- conn->cancel (operation::run);
46
- BOOST_ASIO_CORO_YIELD
47
- std::move (*checker->prom_ )(std::move (self));
44
+ conn_->async_exec (checker_->req_ , checker_->resp_ , std::move (self));
45
+ BOOST_REDIS_CHECK_OP0 (checker_->wait_timer_ .cancel ();)
46
+
47
+ // Wait before pinging again.
48
+ checker_->ping_timer_ .expires_after (checker_->ping_interval_ );
49
+ BOOST_ASIO_CORO_YIELD
50
+ checker_->ping_timer_ .async_wait (std::move (self));
51
+ BOOST_REDIS_CHECK_OP0 (;)
52
+ }
53
+ }
54
+ };
55
+
56
+ template <class HealthChecker , class Connection >
57
+ class check_timeout_op {
58
+ public:
59
+ HealthChecker* checker_ = nullptr ;
60
+ Connection* conn_ = nullptr ;
61
+ asio::coroutine coro_{};
62
+
63
+ template <class Self >
64
+ void operator ()(Self& self, system::error_code ec = {})
65
+ {
66
+ BOOST_ASIO_CORO_REENTER (coro_) for (;;)
67
+ {
68
+ checker_->wait_timer_ .expires_after (2 * checker_->ping_interval_ );
69
+ BOOST_ASIO_CORO_YIELD
70
+ checker_->wait_timer_ .async_wait (std::move (self));
71
+ BOOST_REDIS_CHECK_OP0 (;)
72
+
73
+ if (!checker_->resp_ .has_value ()) {
48
74
self.complete ({});
49
75
return ;
50
76
}
51
77
52
- checker->reset ();
78
+ if (checker_->resp_ .value ().empty ()) {
79
+ checker_->ping_timer_ .cancel ();
80
+ conn_->cancel (operation::run);
81
+ checker_->checker_has_exited_ = true ;
82
+ self.complete (error::pong_timeout);
83
+ return ;
84
+ }
85
+
86
+ checker_->resp_ .value ().clear ();
87
+
88
+ if (checker_->resp_ .has_value ()) {
89
+ checker_->resp_ .value ().clear ();
90
+ }
91
+ }
92
+ }
93
+ };
94
+
95
+ template <class HealthChecker , class Connection >
96
+ class check_health_op {
97
+ public:
98
+ HealthChecker* checker_ = nullptr ;
99
+ Connection* conn_ = nullptr ;
100
+ asio::coroutine coro_{};
101
+
102
+ template <class Self >
103
+ void
104
+ operator ()(
105
+ Self& self,
106
+ std::array<std::size_t , 2 > order = {},
107
+ system::error_code ec1 = {},
108
+ system::error_code ec2 = {})
109
+ {
110
+ BOOST_ASIO_CORO_REENTER (coro_)
111
+ {
112
+ BOOST_ASIO_CORO_YIELD
113
+ asio::experimental::make_parallel_group (
114
+ [this ](auto token) { return checker_->async_ping (*conn_, token); },
115
+ [this ](auto token) { return checker_->async_check_timeout (*conn_, token);}
116
+ ).async_wait (
117
+ asio::experimental::wait_for_one (),
118
+ std::move (self));
119
+
120
+ if (is_cancelled (self)) {
121
+ self.complete (asio::error::operation_aborted);
122
+ return ;
123
+ }
124
+
125
+ switch (order[0 ]) {
126
+ case 0 : self.complete (ec1); return ;
127
+ case 1 : self.complete (ec2); return ;
128
+ default : BOOST_ASSERT (false );
129
+ }
53
130
}
54
131
}
55
132
};
56
133
57
134
template <class Executor >
58
135
class health_checker {
59
136
private:
60
- using promise_type = asio::experimental::promise<void (system::error_code, std::size_t ), Executor>;
61
137
using timer_type =
62
138
asio::basic_waitable_timer<
63
139
std::chrono::steady_clock,
@@ -68,9 +144,10 @@ class health_checker {
68
144
health_checker (
69
145
Executor ex,
70
146
std::string const & msg,
71
- std::chrono::steady_clock::duration interval)
72
- : timer_{ex}
73
- , timeout_{interval}
147
+ std::chrono::steady_clock::duration ping_interval)
148
+ : ping_timer_{ex}
149
+ , wait_timer_{ex}
150
+ , ping_interval_{ping_interval}
74
151
{
75
152
req_.push (" PING" , msg);
76
153
}
@@ -87,26 +164,41 @@ class health_checker {
87
164
>(check_health_op<health_checker, Connection>{this , &conn}, token, conn);
88
165
}
89
166
90
- void reset ()
167
+ void cancel ()
168
+ {
169
+ ping_timer_.cancel ();
170
+ wait_timer_.cancel ();
171
+ }
172
+
173
+ private:
174
+ template <class Connection , class CompletionToken >
175
+ auto async_ping (Connection& conn, CompletionToken token)
91
176
{
92
- resp_.value ().clear ();
93
- prom_.reset ();
177
+ return asio::async_compose
178
+ < CompletionToken
179
+ , void (system::error_code)
180
+ >(ping_op<health_checker, Connection>{this , &conn}, token, conn, ping_timer_);
94
181
}
95
182
96
- void cancel ()
183
+ template <class Connection , class CompletionToken >
184
+ auto async_check_timeout (Connection& conn, CompletionToken token)
97
185
{
98
- timer_.cancel ();
99
- if (prom_)
100
- prom_.cancel ();
186
+ return asio::async_compose
187
+ < CompletionToken
188
+ , void (system::error_code)
189
+ >(check_timeout_op<health_checker, Connection>{this , &conn}, token, conn, wait_timer_);
101
190
}
102
191
103
- private:
192
+ template <class , class > friend class ping_op ;
193
+ template <class , class > friend class check_timeout_op ;
104
194
template <class , class > friend class check_health_op ;
105
- timer_type timer_;
106
- std::optional<promise_type> prom_;
195
+
196
+ timer_type ping_timer_;
197
+ timer_type wait_timer_;
107
198
redis::request req_;
108
199
redis::generic_response resp_;
109
- std::chrono::steady_clock::duration timeout_;
200
+ std::chrono::steady_clock::duration ping_interval_;
201
+ bool checker_has_exited_ = false ;
110
202
};
111
203
112
204
} // detail
@@ -120,14 +212,17 @@ class health_checker {
120
212
*
121
213
* @param conn A connection to the Redis server.
122
214
* @param msg The message to be sent with the [PING](https://redis.io/commands/ping/) command. Seting a proper and unique id will help users identify which connections are active.
123
- * @param interval Ping interval .
215
+ * @param ping_interval Ping ping_interval .
124
216
* @param token The completion token
125
217
*
126
218
* The completion token must have the following signature
127
219
*
128
220
* @code
129
221
* void f(system::error_code);
130
222
* @endcode
223
+ *
224
+ * Completion occurs when a pong response is not receive within two
225
+ * times the ping interval.
131
226
*/
132
227
template <
133
228
class Connection ,
@@ -137,12 +232,12 @@ auto
137
232
async_check_health (
138
233
Connection& conn,
139
234
std::string const & msg = " Boost.Redis" ,
140
- std::chrono::steady_clock::duration interval = std::chrono::seconds{2 },
235
+ std::chrono::steady_clock::duration ping_interval = std::chrono::seconds{2 },
141
236
CompletionToken token = CompletionToken{})
142
237
{
143
238
using executor_type = typename Connection::executor_type;
144
239
using health_checker_type = detail::health_checker<executor_type>;
145
- auto checker = std::make_shared<health_checker_type>(conn.get_executor (), msg, interval );
240
+ auto checker = std::make_shared<health_checker_type>(conn.get_executor (), msg, ping_interval );
146
241
return checker->async_check_health (conn, asio::consign (std::move (token), checker));
147
242
}
148
243
0 commit comments