Commit 75b01044 authored by Willem Thiart's avatar Willem Thiart
Browse files

Fixed partitions created by rogue leaders

Thanks to @ar104 for helping me with the fix!
parent f563e205
...@@ -126,7 +126,6 @@ void raft_become_follower(raft_server_t* me_) ...@@ -126,7 +126,6 @@ void raft_become_follower(raft_server_t* me_)
{ {
__log(me_, "becoming follower"); __log(me_, "becoming follower");
raft_set_state(me_, RAFT_STATE_FOLLOWER); raft_set_state(me_, RAFT_STATE_FOLLOWER);
raft_vote(me_, -1);
} }
int raft_periodic(raft_server_t* me_, int msec_since_last_period) int raft_periodic(raft_server_t* me_, int msec_since_last_period)
...@@ -171,13 +170,20 @@ int raft_recv_appendentries_response(raft_server_t* me_, ...@@ -171,13 +170,20 @@ int raft_recv_appendentries_response(raft_server_t* me_,
node, r->success == 1 ? "success" : "fail", r->current_idx, node, r->success == 1 ? "success" : "fail", r->current_idx,
r->first_idx); r->first_idx);
// TODO: should force invalid leaders to stepdown
if (!raft_is_leader(me_)) if (!raft_is_leader(me_))
return -1; return -1;
raft_node_t* p = raft_get_node(me_, node); raft_node_t* p = raft_get_node(me_, node);
/* If response contains term T > currentTerm: set currentTerm = T
and convert to follower (§5.3) */
if (me->current_term < r->term)
{
raft_set_current_term(me_, r->term);
raft_become_follower(me_);
return 0;
}
if (0 == r->success) if (0 == r->success)
{ {
/* If AppendEntries fails because of log inconsistency: /* If AppendEntries fails because of log inconsistency:
...@@ -350,7 +356,10 @@ int raft_recv_requestvote(raft_server_t* me_, int node, msg_requestvote_t* vr, ...@@ -350,7 +356,10 @@ int raft_recv_requestvote(raft_server_t* me_, int node, msg_requestvote_t* vr,
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
if (raft_get_current_term(me_) < vr->term) if (raft_get_current_term(me_) < vr->term)
{
raft_set_current_term(me_, vr->term);
raft_become_follower(me_); raft_become_follower(me_);
}
if (__should_grant_vote(me, vr)) if (__should_grant_vote(me, vr))
{ {
...@@ -430,7 +439,8 @@ int raft_recv_entry(raft_server_t* me_, int node, msg_entry_t* e, ...@@ -430,7 +439,8 @@ int raft_recv_entry(raft_server_t* me_, int node, msg_entry_t* e,
/* Only send new entries. /* Only send new entries.
* Don't send the entry to peers who are behind, to prevent them from * Don't send the entry to peers who are behind, to prevent them from
* becomming congested. */ * becomming congested. */
if (me->nodeid != i) { if (me->nodeid != i)
{
int next_idx = raft_node_get_next_idx(raft_get_node(me_, i)); int next_idx = raft_node_get_next_idx(raft_get_node(me_, i));
int last_log_idx = raft_get_current_idx(me_); int last_log_idx = raft_get_current_idx(me_);
if (next_idx == last_log_idx) if (next_idx == last_log_idx)
......
...@@ -71,9 +71,13 @@ int raft_get_voted_for(raft_server_t* me_) ...@@ -71,9 +71,13 @@ int raft_get_voted_for(raft_server_t* me_)
void raft_set_current_term(raft_server_t* me_, const int term) void raft_set_current_term(raft_server_t* me_, const int term)
{ {
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
if (me->current_term < term)
{
me->current_term = term; me->current_term = term;
me->voted_for = -1;
if (me->cb.persist_term) if (me->cb.persist_term)
me->cb.persist_term(me_, me->udata, term); me->cb.persist_term(me_, me->udata, term);
}
} }
int raft_get_current_term(raft_server_t* me_) int raft_get_current_term(raft_server_t* me_)
......
...@@ -511,6 +511,7 @@ void TestRaft_server_recv_requestvote_candidate_step_down_if_term_is_higher_than ...@@ -511,6 +511,7 @@ void TestRaft_server_recv_requestvote_candidate_step_down_if_term_is_higher_than
raft_add_node(r, (void*)2, 0); raft_add_node(r, (void*)2, 0);
raft_become_candidate(r); raft_become_candidate(r);
raft_set_current_term(r, 1); raft_set_current_term(r, 1);
CuAssertIntEquals(tc, 0, raft_get_voted_for(r));
/* current term is less than term */ /* current term is less than term */
msg_requestvote_t rv; msg_requestvote_t rv;
...@@ -519,7 +520,9 @@ void TestRaft_server_recv_requestvote_candidate_step_down_if_term_is_higher_than ...@@ -519,7 +520,9 @@ void TestRaft_server_recv_requestvote_candidate_step_down_if_term_is_higher_than
rv.last_log_idx = 1; rv.last_log_idx = 1;
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
raft_recv_requestvote(r, 1, &rv, &rvr); raft_recv_requestvote(r, 1, &rv, &rvr);
CuAssertTrue(tc, 1 == raft_is_follower(r)); CuAssertIntEquals(tc, 1, raft_is_follower(r));
CuAssertIntEquals(tc, 2, raft_get_current_term(r));
CuAssertIntEquals(tc, 1, raft_get_voted_for(r));
} }
/* If votedFor is null or candidateId, and candidate's log is at /* If votedFor is null or candidateId, and candidate's log is at
...@@ -552,13 +555,13 @@ void TestRaft_follower_becomes_follower_is_follower(CuTest * tc) ...@@ -552,13 +555,13 @@ void TestRaft_follower_becomes_follower_is_follower(CuTest * tc)
CuAssertTrue(tc, raft_is_follower(r)); CuAssertTrue(tc, raft_is_follower(r));
} }
void TestRaft_follower_becomes_follower_clears_voted_for(CuTest * tc) void TestRaft_follower_becomes_follower_does_not_clear_voted_for(CuTest * tc)
{ {
void *r = raft_new(); void *r = raft_new();
raft_vote(r, 1); raft_vote(r, 1);
CuAssertTrue(tc, 1 == raft_get_voted_for(r)); CuAssertTrue(tc, 1 == raft_get_voted_for(r));
raft_become_follower(r); raft_become_follower(r);
CuAssertTrue(tc, -1 == raft_get_voted_for(r)); CuAssertTrue(tc, 1 == raft_get_voted_for(r));
} }
/* 5.1 */ /* 5.1 */
...@@ -1219,8 +1222,10 @@ void TestRaft_candidate_recv_appendentries_frm_leader_results_in_follower( ...@@ -1219,8 +1222,10 @@ void TestRaft_candidate_recv_appendentries_frm_leader_results_in_follower(
raft_add_node(r, (void*)2, 0); raft_add_node(r, (void*)2, 0);
raft_set_state(r, RAFT_STATE_CANDIDATE); raft_set_state(r, RAFT_STATE_CANDIDATE);
raft_vote(r, 0);
CuAssertTrue(tc, 0 == raft_is_follower(r)); CuAssertTrue(tc, 0 == raft_is_follower(r));
CuAssertTrue(tc, -1 == raft_get_current_leader(r)); CuAssertTrue(tc, -1 == raft_get_current_leader(r));
CuAssertTrue(tc, 0 == raft_get_current_term(r));
/* receive recent appendentries */ /* receive recent appendentries */
msg_appendentries_t ae; msg_appendentries_t ae;
...@@ -1232,6 +1237,8 @@ void TestRaft_candidate_recv_appendentries_frm_leader_results_in_follower( ...@@ -1232,6 +1237,8 @@ void TestRaft_candidate_recv_appendentries_frm_leader_results_in_follower(
CuAssertTrue(tc, 1 == raft_is_follower(r)); CuAssertTrue(tc, 1 == raft_is_follower(r));
/* after accepting a leader, it's available as the last known leader */ /* after accepting a leader, it's available as the last known leader */
CuAssertTrue(tc, 1 == raft_get_current_leader(r)); CuAssertTrue(tc, 1 == raft_get_current_leader(r));
CuAssertTrue(tc, 1 == raft_get_current_term(r));
CuAssertTrue(tc, -1 == raft_get_voted_for(r));
} }
/* Candidate 5.2 */ /* Candidate 5.2 */
...@@ -1662,16 +1669,16 @@ void TestRaft_leader_recv_appendentries_response_increase_commit_idx_when_majori ...@@ -1662,16 +1669,16 @@ void TestRaft_leader_recv_appendentries_response_increase_commit_idx_when_majori
raft_send_appendentries(r, 0); raft_send_appendentries(r, 0);
raft_send_appendentries(r, 1); raft_send_appendentries(r, 1);
/* receive mock success responses */ /* receive mock success responses */
aer.term = 5; aer.term = 1;
aer.success = 1; aer.success = 1;
aer.current_idx = 2; aer.current_idx = 2;
aer.first_idx = 2; aer.first_idx = 2;
raft_recv_appendentries_response(r, 0, &aer); raft_recv_appendentries_response(r, 0, &aer);
raft_recv_appendentries_response(r, 1, &aer); raft_recv_appendentries_response(r, 1, &aer);
/* leader will now have majority followers who have appended this log */ /* leader will now have majority followers who have appended this log */
CuAssertTrue(tc, 2 == raft_get_commit_idx(r)); CuAssertIntEquals(tc, 2, raft_get_commit_idx(r));
raft_periodic(r, 1); raft_periodic(r, 1);
CuAssertTrue(tc, 2 == raft_get_last_applied_idx(r)); CuAssertIntEquals(tc, 2, raft_get_last_applied_idx(r));
} }
void TestRaft_leader_recv_appendentries_response_retry_only_if_leader(CuTest * tc) void TestRaft_leader_recv_appendentries_response_retry_only_if_leader(CuTest * tc)
......
Markdown is supported
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