7#ifndef BOOST_REDIS_HEALTH_CHECKER_HPP
8#define BOOST_REDIS_HEALTH_CHECKER_HPP
10#include <boost/redis/adapter/any_adapter.hpp>
11#include <boost/redis/config.hpp>
12#include <boost/redis/detail/connection_logger.hpp>
13#include <boost/redis/operation.hpp>
14#include <boost/redis/request.hpp>
15#include <boost/redis/response.hpp>
17#include <boost/asio/compose.hpp>
18#include <boost/asio/consign.hpp>
19#include <boost/asio/coroutine.hpp>
20#include <boost/asio/post.hpp>
21#include <boost/asio/steady_timer.hpp>
25namespace boost::redis::detail {
27template <
class HealthChecker,
class Connection>
30 HealthChecker* checker_ =
nullptr;
31 Connection* conn_ =
nullptr;
32 asio::coroutine coro_{};
35 void operator()(Self& self, system::error_code ec = {}, std::size_t = 0)
37 BOOST_ASIO_CORO_REENTER(coro_)
for (;;)
39 if (checker_->ping_interval_ == std::chrono::seconds::zero()) {
40 conn_->logger_.trace(
"ping_op (1): timeout disabled.");
42 asio::post(std::move(self));
47 if (checker_->checker_has_exited_) {
48 conn_->logger_.trace(
"ping_op (2): checker has exited.");
54 conn_->async_exec(checker_->req_, any_adapter(checker_->resp_), std::move(self));
56 conn_->logger_.trace(
"ping_op (3)", ec);
57 checker_->wait_timer_.cancel();
63 checker_->ping_timer_.expires_after(checker_->ping_interval_);
66 checker_->ping_timer_.async_wait(std::move(self));
68 conn_->logger_.trace(
"ping_op (4)", ec);
76template <
class HealthChecker,
class Connection>
77class check_timeout_op {
79 HealthChecker* checker_ =
nullptr;
80 Connection* conn_ =
nullptr;
81 asio::coroutine coro_{};
84 void operator()(Self& self, system::error_code ec = {})
86 BOOST_ASIO_CORO_REENTER(coro_)
for (;;)
88 if (checker_->ping_interval_ == std::chrono::seconds::zero()) {
89 conn_->logger_.trace(
"check_timeout_op (1): timeout disabled.");
91 asio::post(std::move(self));
96 checker_->wait_timer_.expires_after(2 * checker_->ping_interval_);
99 checker_->wait_timer_.async_wait(std::move(self));
101 conn_->logger_.trace(
"check_timeout_op (2)", ec);
106 if (checker_->resp_.has_error()) {
108 conn_->logger_.trace(
"check_timeout_op (3): Response error.");
113 if (checker_->resp_.value().empty()) {
114 conn_->logger_.trace(
"check_timeout_op (4): pong timeout.");
115 checker_->ping_timer_.cancel();
117 checker_->checker_has_exited_ =
true;
122 if (checker_->resp_.has_value()) {
123 checker_->resp_.value().clear();
129template <
class Executor>
130class health_checker {
132 using timer_type = asio::basic_waitable_timer<
133 std::chrono::steady_clock,
134 asio::wait_traits<std::chrono::steady_clock>,
138 health_checker(Executor ex)
142 req_.
push(
"PING",
"Boost.Redis");
145 void set_config(config
const& cfg)
148 req_.
push(
"PING", cfg.health_check_id);
149 ping_interval_ = cfg.health_check_interval;
154 ping_timer_.cancel();
155 wait_timer_.cancel();
158 template <
class Connection,
class CompletionToken>
159 auto async_ping(Connection& conn, CompletionToken token)
161 return asio::async_compose<CompletionToken, void(system::error_code)>(
162 ping_op<health_checker, Connection>{
this, &conn},
168 template <
class Connection,
class CompletionToken>
169 auto async_check_timeout(Connection& conn, CompletionToken token)
171 checker_has_exited_ =
false;
172 return asio::async_compose<CompletionToken, void(system::error_code)>(
173 check_timeout_op<health_checker, Connection>{
this, &conn},
180 template <
class,
class>
friend class ping_op;
181 template <
class,
class>
friend class check_timeout_op;
183 timer_type ping_timer_;
184 timer_type wait_timer_;
187 std::chrono::steady_clock::duration ping_interval_ = std::chrono::seconds{5};
188 bool checker_has_exited_ =
false;
void push(std::string_view cmd, Ts const &... args)
Appends a new command to the end of the request.
void clear()
Clears the request preserving allocated memory.
adapter::result< std::vector< resp3::node > > generic_response
A generic response to a request.
@ pong_timeout
Connect timeout.
@ run
Refers to connection::async_run operations.