From 7cbd7725fc3b3e6d953a768d187434e313d27b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korina=20=C5=A0imi=C4=8Devi=C4=87?= Date: Mon, 19 Feb 2024 13:25:20 +0100 Subject: [PATCH] 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 --- include/async_mqtt5/impl/reconnect_op.hpp | 29 ++++++++++++++++-- test/unit/reconnect_op.cpp | 36 +++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 test/unit/reconnect_op.cpp diff --git a/include/async_mqtt5/impl/reconnect_op.hpp b/include/async_mqtt5/impl/reconnect_op.hpp index 80a4672..ff4780a 100644 --- a/include/async_mqtt5/impl/reconnect_op.hpp +++ b/include/async_mqtt5/impl/reconnect_op.hpp @@ -6,15 +6,38 @@ #include #include #include - #include +#include +#include + #include #include #include 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 @@ -31,6 +54,8 @@ class reconnect_op { std::unique_ptr _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 {}) ); diff --git a/test/unit/reconnect_op.cpp b/test/unit/reconnect_op.cpp new file mode 100644 index 0000000..f37a88c --- /dev/null +++ b/test/unit/reconnect_op.cpp @@ -0,0 +1,36 @@ +#include + +#include +#include + +#include + +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();