Commit f8e9f025 authored by Georgios Bitzes's avatar Georgios Bitzes
Browse files

raft: add method to describe the result of an election

parent 40d1433d
Pipeline #1514331 failed with stages
in 83 minutes and 26 seconds
......@@ -109,6 +109,61 @@ ElectionOutcome RaftVoteRegistry::determineOutcome() const {
return ElectionOutcome::kNotElected;
}
//------------------------------------------------------------------------------
// Count a specific type of vote
//------------------------------------------------------------------------------
size_t RaftVoteRegistry::count(RaftVote vote) const {
size_t num = 0;
for(auto it = mContents.begin(); it != mContents.end(); it++) {
const SingleVote& sv = it->second;
if(sv.netError || sv.parseError) {
continue;
}
if(sv.resp.vote == vote) {
num++;
}
}
return num;
}
//------------------------------------------------------------------------------
// Count network errors
//------------------------------------------------------------------------------
size_t RaftVoteRegistry::countNetworkError() const {
size_t num = 0;
for(auto it = mContents.begin(); it != mContents.end(); it++) {
const SingleVote& sv = it->second;
if(sv.netError) {
num++;
}
}
return num;
}
//------------------------------------------------------------------------------
// Count parse errors
//------------------------------------------------------------------------------
size_t RaftVoteRegistry::countParseError() const {
size_t num = 0;
for(auto it = mContents.begin(); it != mContents.end(); it++) {
const SingleVote& sv = it->second;
if(sv.parseError) {
num++;
}
}
return num;
}
//------------------------------------------------------------------------------
// Register vote
//------------------------------------------------------------------------------
......@@ -134,4 +189,41 @@ void RaftVoteRegistry::registerVote(const RaftServer &srv, std::future<qclient::
return registerVote(srv, resp);
}
//------------------------------------------------------------------------------
// Describe outcome
//------------------------------------------------------------------------------
std::string RaftVoteRegistry::describeOutcome() const {
std::ostringstream ss;
if(mPreVote) {
ss << "Pre-vote round";
}
else {
ss << "Election round";
}
const ElectionOutcome outcome = determineOutcome();
const size_t granted = count(RaftVote::GRANTED);
const size_t refused = count(RaftVote::REFUSED);
const size_t veto = count(RaftVote::VETO);
if(outcome == ElectionOutcome::kElected) {
ss << " successful";
}
else {
ss << " unsuccessful";
}
ss << " for term " << mTerm << ". Contacted " << mContents.size() << " nodes,";
ss << " received " << granted+refused+veto << " replies with a tally of ";
ss << granted << " positive votes, " << refused << " refused votes, and ";
ss << veto << " vetoes.";
if(granted >= calculateQuorumSize(mContents.size()+1) && veto > 0) {
qdb_critical("Received a quorum of positive votes (" << granted << ") plus vetoes: " << veto);
}
return ss.str();
}
}
\ No newline at end of file
......@@ -70,11 +70,30 @@ public:
void registerVote(const RaftServer &srv, std::future<qclient::redisReplyPtr> &fut,
std::chrono::steady_clock::time_point deadline);
//----------------------------------------------------------------------------
// Count a specific type of vote
//----------------------------------------------------------------------------
size_t count(RaftVote vote) const;
//----------------------------------------------------------------------------
// Count network errors
//----------------------------------------------------------------------------
size_t countNetworkError() const;
//----------------------------------------------------------------------------
// Count parse errors
//----------------------------------------------------------------------------
size_t countParseError() const;
//----------------------------------------------------------------------------
// Determine outcome
//----------------------------------------------------------------------------
ElectionOutcome determineOutcome() const;
//----------------------------------------------------------------------------
// Describe outcome
//----------------------------------------------------------------------------
std::string describeOutcome() const;
private:
RaftTerm mTerm;
......
......@@ -1084,6 +1084,12 @@ TEST(RaftVoteRegistry, OneForOneAgainst) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
registry.registerVote(RaftServer("localhost", 7778), RaftVoteResponse(1, RaftVote::REFUSED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 1u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 1u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
}
......@@ -1093,7 +1099,15 @@ TEST(RaftVoteRegistry, OneForOneVeto) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
registry.registerVote(RaftServer("localhost", 7778), RaftVoteResponse(1, RaftVote::VETO));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 1u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 1u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kVetoed);
ASSERT_EQ(registry.describeOutcome(),
"Election round unsuccessful for term 1. Contacted 2 nodes, received 2 replies with a tally of 1 positive votes, 0 refused votes, and 1 vetoes.");
}
TEST(RaftVoteRegistry, OneForOneNetErr) {
......@@ -1102,6 +1116,12 @@ TEST(RaftVoteRegistry, OneForOneNetErr) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
registry.registerNetworkError(RaftServer("localhost", 7778));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 1u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 1u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
}
......@@ -1111,6 +1131,12 @@ TEST(RaftVoteRegistry, OneForOneParseErr) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
registry.registerParseError(RaftServer("localhost", 7778));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 1u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 1u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
}
......@@ -1120,6 +1146,12 @@ TEST(RaftVoteRegistry, ParsingError) {
registry.registerParseError(RaftServer("localhost", 7777));
registry.registerParseError(RaftServer("localhost", 7778));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 0u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 2u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kNotElected);
}
......@@ -1129,7 +1161,15 @@ TEST(RaftVoteRegistry, PreVoteParsingError) {
registry.registerParseError(RaftServer("localhost", 7777));
registry.registerParseError(RaftServer("localhost", 7778));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 0u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 2u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
ASSERT_EQ(registry.describeOutcome(),
"Pre-vote round successful for term 1. Contacted 2 nodes, received 0 replies with a tally of 0 positive votes, 0 refused votes, and 0 vetoes.");
}
TEST(RaftVoteRegistry, TwoAgainst) {
......@@ -1138,6 +1178,12 @@ TEST(RaftVoteRegistry, TwoAgainst) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::REFUSED));
registry.registerVote(RaftServer("localhost", 7778), RaftVoteResponse(1, RaftVote::REFUSED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 0u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 2u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kNotElected);
}
......@@ -1147,6 +1193,12 @@ TEST(RaftVoteRegistry, TwoVetoes) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::VETO));
registry.registerVote(RaftServer("localhost", 7778), RaftVoteResponse(1, RaftVote::VETO));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 0u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 2u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kVetoed);
}
......@@ -1156,7 +1208,15 @@ TEST(RaftVoteRegistry, TwoFor) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
registry.registerVote(RaftServer("localhost", 7778), RaftVoteResponse(1, RaftVote::GRANTED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 2u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
ASSERT_EQ(registry.describeOutcome(),
"Election round successful for term 1. Contacted 2 nodes, received 2 replies with a tally of 2 positive votes, 0 refused votes, and 0 vetoes.");
}
TEST(RaftVoteRegistry, OneFor) {
......@@ -1164,6 +1224,12 @@ TEST(RaftVoteRegistry, OneFor) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::GRANTED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 1u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 0u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
}
......@@ -1172,6 +1238,12 @@ TEST(RaftVoteRegistry, OneAgainst) {
registry.registerVote(RaftServer("localhost", 7777), RaftVoteResponse(1, RaftVote::REFUSED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 0u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 1u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kNotElected);
}
......@@ -1183,6 +1255,12 @@ TEST(RaftVoteRegistry, TwoForOneAgainst) {
registry.registerVote(RaftServer("localhost", 7780), RaftVoteResponse(1, RaftVote::REFUSED));
registry.registerVote(RaftServer("localhost", 7781), RaftVoteResponse(1, RaftVote::REFUSED));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 2u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 2u);
ASSERT_EQ(registry.count(RaftVote::VETO), 0u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kElected);
}
......@@ -1194,6 +1272,12 @@ TEST(RaftVoteRegistry, TwoForOneVeto) {
registry.registerVote(RaftServer("localhost", 7780), RaftVoteResponse(1, RaftVote::REFUSED));
registry.registerVote(RaftServer("localhost", 7781), RaftVoteResponse(1, RaftVote::VETO));
ASSERT_EQ(registry.count(RaftVote::GRANTED), 2u);
ASSERT_EQ(registry.count(RaftVote::REFUSED), 1u);
ASSERT_EQ(registry.count(RaftVote::VETO), 1u);
ASSERT_EQ(registry.countNetworkError(), 0u);
ASSERT_EQ(registry.countParseError(), 0u);
ASSERT_EQ(registry.determineOutcome(), ElectionOutcome::kVetoed);
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment