// ----------------------------------------------------------------------
// File: RaftTimeouts.cc
// Author: Georgios Bitzes - CERN
// ----------------------------------------------------------------------
/************************************************************************
* quarkdb - a redis-like highly available key-value store *
* Copyright (C) 2016 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "raft/RaftTimeouts.hh"
#include "Utils.hh"
#include "utils/ParseUtils.hh"
#include
using namespace quarkdb;
std::random_device RaftTimeouts::rd;
std::mt19937 RaftTimeouts::gen(rd());
std::mutex RaftTimeouts::genMutex;
RaftTimeouts quarkdb::relaxedTimeouts {milliseconds(5000), milliseconds(10000),
milliseconds(500)};
RaftTimeouts quarkdb::defaultTimeouts {milliseconds(1000), milliseconds(1500),
milliseconds(250)};
RaftTimeouts quarkdb::tightTimeouts {milliseconds(100), milliseconds(150),
milliseconds(75)};
RaftTimeouts quarkdb::aggressiveTimeouts {milliseconds(50), milliseconds(75),
milliseconds(5)};
RaftTimeouts::RaftTimeouts(const milliseconds &low, const milliseconds &high,
const milliseconds &heartbeat)
: timeoutLow(low), timeoutHigh(high), heartbeatInterval(heartbeat), dist(low.count(), high.count()) {
}
milliseconds RaftTimeouts::getLow() const {
return timeoutLow;
}
milliseconds RaftTimeouts::getHigh() const {
return timeoutHigh;
}
milliseconds RaftTimeouts::getRandom() const {
std::scoped_lock lock(genMutex);
return std::chrono::milliseconds(dist(gen));
}
milliseconds RaftTimeouts::getHeartbeatInterval() const {
return heartbeatInterval;
}
std::string RaftTimeouts::toString() const {
return SSTR(getLow().count() << ":" << getHigh().count() << ":" << getHeartbeatInterval().count());
}
static bool parseError(const std::string &str) {
qdb_critical("Unable to parse raft timeouts: " << str);
return false;
}
bool RaftTimeouts::fromString(RaftTimeouts &ret, const std::string &str) {
std::vector parts = split(str, ":");
if(parts.size() != 3) {
return parseError(str);
}
int64_t low, high, heartbeat;
if(!ParseUtils::parseInt64(parts[0], low)) {
return parseError(str);
}
if(!ParseUtils::parseInt64(parts[1], high)) {
return parseError(str);
}
if(!ParseUtils::parseInt64(parts[2], heartbeat)) {
return parseError(str);
}
ret = RaftTimeouts(milliseconds(low), milliseconds(high), milliseconds(heartbeat));
return true;
}
RaftHeartbeatTracker::RaftHeartbeatTracker(const RaftTimeouts t)
: timeouts(t) {
refreshRandomTimeout();
}
void RaftHeartbeatTracker::heartbeat(std::chrono::steady_clock::time_point now) {
std::scoped_lock lock(lastHeartbeatMutex);
if(lastHeartbeat <= now) {
lastHeartbeat = now;
}
}
void RaftHeartbeatTracker::triggerTimeout() {
std::scoped_lock lock(lastHeartbeatMutex);
artificialTimeout = true;
}
TimeoutStatus RaftHeartbeatTracker::timeout(std::chrono::steady_clock::time_point now) {
std::scoped_lock lock(lastHeartbeatMutex);
if(artificialTimeout) {
qdb_event("Triggering an artificial timeout.");
artificialTimeout = false;
return TimeoutStatus::kArtificial;
}
bool isTimeout = now - lastHeartbeat > randomTimeout;
if(isTimeout) {
return TimeoutStatus::kYes;
}
return TimeoutStatus::kNo;
}
milliseconds RaftHeartbeatTracker::getRandomTimeout() {
std::scoped_lock lock(lastHeartbeatMutex);
return randomTimeout;
}
milliseconds RaftHeartbeatTracker::refreshRandomTimeout() {
std::scoped_lock lock(lastHeartbeatMutex);
randomTimeout = timeouts.getRandom();
return randomTimeout;
}
std::chrono::steady_clock::time_point RaftHeartbeatTracker::getLastHeartbeat() {
std::scoped_lock lock(lastHeartbeatMutex);
return lastHeartbeat;
}