Use an exponential backoff when deciding how long we need to wait for reconnects (#2892)
Co-authored-by: Leon Richardt <leon.richardt@gmail.com>
This commit is contained in:
@@ -5,9 +5,6 @@
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
// The minimum interval between attempting to establish a new connection
|
||||
const int RECONNECT_MIN_INTERVAL = 15000;
|
||||
|
||||
namespace {
|
||||
|
||||
const auto payload = QString("chatterino/" CHATTERINO_VERSION);
|
||||
@@ -48,18 +45,11 @@ IrcConnection::IrcConnection(QObject *parent)
|
||||
return;
|
||||
}
|
||||
|
||||
auto delta =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - this->lastConnected_)
|
||||
.count();
|
||||
delta = delta < RECONNECT_MIN_INTERVAL
|
||||
? (RECONNECT_MIN_INTERVAL - delta)
|
||||
: 10;
|
||||
qCDebug(chatterinoIrc) << "Reconnecting in" << delta << "ms";
|
||||
this->reconnectTimer_.start(delta);
|
||||
auto delay = this->reconnectBackoff_.next();
|
||||
qCDebug(chatterinoIrc) << "Reconnecting in" << delay.count() << "ms";
|
||||
this->reconnectTimer_.start(delay);
|
||||
});
|
||||
|
||||
this->reconnectTimer_.setInterval(RECONNECT_MIN_INTERVAL);
|
||||
this->reconnectTimer_.setSingleShot(true);
|
||||
QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] {
|
||||
if (this->isConnected())
|
||||
@@ -120,13 +110,12 @@ IrcConnection::IrcConnection(QObject *parent)
|
||||
[this](Communi::IrcMessage *message) {
|
||||
// This connection is probably still alive
|
||||
this->recentlyReceivedMessage_ = true;
|
||||
this->reconnectBackoff_.reset();
|
||||
});
|
||||
}
|
||||
|
||||
void IrcConnection::open()
|
||||
{
|
||||
// Accurately track the time a connection was opened
|
||||
this->lastConnected_ = std::chrono::steady_clock::now();
|
||||
this->expectConnectionLoss_ = false;
|
||||
this->waitingForPong_ = false;
|
||||
this->recentlyReceivedMessage_ = false;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/ExponentialBackoff.hpp"
|
||||
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
#include <IrcConnection>
|
||||
#include <QTimer>
|
||||
#include <chrono>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
@@ -28,7 +29,9 @@ private:
|
||||
QTimer pingTimer_;
|
||||
QTimer reconnectTimer_;
|
||||
std::atomic<bool> recentlyReceivedMessage_{true};
|
||||
std::chrono::steady_clock::time_point lastConnected_;
|
||||
|
||||
// Reconnect with a base delay of 1 second and max out at 1 second * (2^4) (i.e. 16 seconds)
|
||||
ExponentialBackoff<4> reconnectBackoff_{std::chrono::milliseconds{1000}};
|
||||
|
||||
std::atomic<bool> expectConnectionLoss_{false};
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
namespace chatterino {
|
||||
|
||||
// Yes, you can't specify the base 😎 deal with it
|
||||
template <unsigned maxSteps>
|
||||
class ExponentialBackoff
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an object helping you make exponentially (with base 2) backed off times.
|
||||
*
|
||||
* @param start The start time in milliseconds
|
||||
* @param maxSteps The max number of progressions we will take before stopping
|
||||
*
|
||||
* For example, ExponentialBackoff(10ms, 3) would have the next() function return 10ms, 20ms, 40ms, 40ms, ..., 40ms
|
||||
**/
|
||||
ExponentialBackoff(const std::chrono::milliseconds &start)
|
||||
: start_(start)
|
||||
, step_{1}
|
||||
{
|
||||
static_assert(maxSteps > 1, "maxSteps must be higher than 1");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current number in the progression and increment the step until the next one (assuming we're not at the cap)
|
||||
*
|
||||
* @returns current step in milliseconds
|
||||
**/
|
||||
[[nodiscard]] std::chrono::milliseconds next()
|
||||
{
|
||||
auto next = this->start_ * (1 << (this->step_ - 1));
|
||||
|
||||
this->step_ += 1;
|
||||
|
||||
if (this->step_ >= maxSteps)
|
||||
{
|
||||
this->step_ = maxSteps;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the progression back to its initial state
|
||||
**/
|
||||
void reset()
|
||||
{
|
||||
this->step_ = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::chrono::milliseconds start_;
|
||||
unsigned step_;
|
||||
};
|
||||
|
||||
} // namespace chatterino
|
||||
Reference in New Issue
Block a user