forked from boostorg/mqtt5
Wait exponentially longer when all hosts have been exhausted during reconnect
Summary: related to T13746 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Differential Revision: https://repo.mireo.local/D27995
This commit is contained in:
@ -6,15 +6,38 @@
|
||||
#include <boost/asio/prepend.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/any_completion_handler.hpp>
|
||||
|
||||
#include <boost/asio/experimental/parallel_group.hpp>
|
||||
|
||||
#include <boost/random/linear_congruential.hpp>
|
||||
#include <boost/random/uniform_smallint.hpp>
|
||||
|
||||
#include <async_mqtt5/types.hpp>
|
||||
#include <async_mqtt5/detail/async_traits.hpp>
|
||||
#include <async_mqtt5/impl/connect_op.hpp>
|
||||
|
||||
namespace async_mqtt5::detail {
|
||||
|
||||
class exponential_backoff {
|
||||
int _curr_exp { 0 };
|
||||
|
||||
static constexpr int _base_mulptilier = 1000;
|
||||
static constexpr int _max_exp = 4;
|
||||
|
||||
// sizeof(_generator) = 8
|
||||
boost::random::rand48 _generator { uint32_t(std::time(0)) };
|
||||
boost::random::uniform_smallint<> _distribution { -500, 500 };
|
||||
public:
|
||||
exponential_backoff() = default;
|
||||
|
||||
duration generate() {
|
||||
int exponent = _curr_exp < _max_exp ? _curr_exp++ : _max_exp;
|
||||
int base = 1 << exponent;
|
||||
return std::chrono::milliseconds(
|
||||
base * _base_mulptilier + _distribution(_generator) /* noise */
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
namespace asio = boost::asio;
|
||||
|
||||
template <typename Owner>
|
||||
@ -31,6 +54,8 @@ class reconnect_op {
|
||||
|
||||
std::unique_ptr<std::string> _buffer_ptr;
|
||||
|
||||
exponential_backoff _generator;
|
||||
|
||||
using endpoint = asio::ip::tcp::endpoint;
|
||||
using epoints = asio::ip::tcp::resolver::results_type;
|
||||
|
||||
@ -87,7 +112,7 @@ public:
|
||||
}
|
||||
|
||||
void backoff_and_reconnect() {
|
||||
_owner._connect_timer.expires_from_now(std::chrono::seconds(5));
|
||||
_owner._connect_timer.expires_from_now(_generator.generate());
|
||||
_owner._connect_timer.async_wait(
|
||||
asio::prepend(std::move(*this), on_backoff {})
|
||||
);
|
||||
|
36
test/unit/reconnect_op.cpp
Normal file
36
test/unit/reconnect_op.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <async_mqtt5/impl/reconnect_op.hpp>
|
||||
|
||||
using namespace async_mqtt5;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(reconnect_op/*, *boost::unit_test::disabled()*/)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(exponential_backoff) {
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
detail::exponential_backoff generator;
|
||||
|
||||
auto first_iter = generator.generate();
|
||||
BOOST_TEST((first_iter >= 500ms && first_iter <= 1500ms));
|
||||
|
||||
auto second_iter = generator.generate();
|
||||
BOOST_TEST((second_iter >= 1500ms && first_iter <= 2500ms));
|
||||
|
||||
auto third_iter = generator.generate();
|
||||
BOOST_TEST((third_iter >= 3500ms && third_iter <= 4500ms));
|
||||
|
||||
auto fourth_iter = generator.generate();
|
||||
BOOST_TEST((fourth_iter >= 7500ms && fourth_iter <= 8500ms));
|
||||
|
||||
auto fifth_iter = generator.generate();
|
||||
BOOST_TEST((fifth_iter >= 15500ms && fourth_iter <= 16500ms));
|
||||
|
||||
auto sixth_iter = generator.generate();
|
||||
BOOST_TEST((sixth_iter >= 15500ms && sixth_iter <= 16500ms));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
Reference in New Issue
Block a user