Unverified Commit 73a3a856 authored by Ozan Tezcan's avatar Ozan Tezcan Committed by GitHub
Browse files

Prevote implementation (#52)

* prevote implementation
parent bdd0797f
...@@ -28,17 +28,17 @@ typedef enum { ...@@ -28,17 +28,17 @@ typedef enum {
RAFT_MEMBERSHIP_REMOVE, RAFT_MEMBERSHIP_REMOVE,
} raft_membership_e; } raft_membership_e;
#define RAFT_UNKNOWN_NODE_ID (-1)
typedef enum { typedef enum {
RAFT_STATE_NONE, RAFT_STATE_NONE,
RAFT_STATE_FOLLOWER, RAFT_STATE_FOLLOWER,
RAFT_STATE_PRECANDIDATE,
RAFT_STATE_CANDIDATE, RAFT_STATE_CANDIDATE,
RAFT_STATE_LEADER RAFT_STATE_LEADER
} raft_state_e; } raft_state_e;
/** Allow entries to apply while taking a snapshot */ /** Allow entries to apply while taking a snapshot */
#define RAFT_SNAPSHOT_NONBLOCKING_APPLY 1 #define RAFT_SNAPSHOT_NONBLOCKING_APPLY 1
#define RAFT_NODE_ID_NONE (-1)
typedef enum { typedef enum {
/** /**
...@@ -131,6 +131,9 @@ typedef struct ...@@ -131,6 +131,9 @@ typedef struct
* This message could force a leader/candidate to become a follower. */ * This message could force a leader/candidate to become a follower. */
typedef struct typedef struct
{ {
/** 1 if this is a prevote message, 0 otherwise */
int prevote;
/** currentTerm, to force other leader/candidate to step down */ /** currentTerm, to force other leader/candidate to step down */
raft_term_t term; raft_term_t term;
...@@ -148,6 +151,12 @@ typedef struct ...@@ -148,6 +151,12 @@ typedef struct
* Indicates if node has accepted the server's vote request. */ * Indicates if node has accepted the server's vote request. */
typedef struct typedef struct
{ {
/** 1 if this is a prevote message, 0 otherwise */
int prevote;
/** term of received requestvote msg */
raft_term_t request_term;
/** currentTerm, for candidate to update itself */ /** currentTerm, for candidate to update itself */
raft_term_t term; raft_term_t term;
...@@ -285,7 +294,7 @@ typedef int ( ...@@ -285,7 +294,7 @@ typedef int (
* This callback is optional * This callback is optional
* @param[in] raft The Raft server making this callback * @param[in] raft The Raft server making this callback
* @param[in] node_id The node id that is the subject of this log. If log is not * @param[in] node_id The node id that is the subject of this log. If log is not
* related to a node, this could be 'RAFT_UNKNOWN_NODE_ID'. * related to a node, this could be 'RAFT_NODE_ID_NONE'.
* @param[in] user_data User data that is passed from Raft server * @param[in] user_data User data that is passed from Raft server
* @param[in] buf The buffer that was logged */ * @param[in] buf The buffer that was logged */
typedef void ( typedef void (
...@@ -818,6 +827,10 @@ int raft_is_follower(raft_server_t* me); ...@@ -818,6 +827,10 @@ int raft_is_follower(raft_server_t* me);
* @return 1 if leader; 0 otherwise */ * @return 1 if leader; 0 otherwise */
int raft_is_leader(raft_server_t* me); int raft_is_leader(raft_server_t* me);
/**
* @return 1 if precandidate; 0 otherwise */
int raft_is_precandidate(raft_server_t* me);
/** /**
* @return 1 if candidate; 0 otherwise */ * @return 1 if candidate; 0 otherwise */
int raft_is_candidate(raft_server_t* me); int raft_is_candidate(raft_server_t* me);
...@@ -883,7 +896,7 @@ int raft_get_voted_for(raft_server_t* me); ...@@ -883,7 +896,7 @@ int raft_get_voted_for(raft_server_t* me);
/** Get what this node thinks the node ID of the leader is. /** Get what this node thinks the node ID of the leader is.
* @return node of what this node thinks is the valid leader; * @return node of what this node thinks is the valid leader;
* RAFT_UNKNOWN_NODE_ID if there is no leader */ * RAFT_NODE_ID_NONE if there is no leader */
raft_node_id_t raft_get_leader_id(raft_server_t* me_); raft_node_id_t raft_get_leader_id(raft_server_t* me_);
/** Get what this node thinks the node of the leader is. /** Get what this node thinks the node of the leader is.
......
...@@ -75,7 +75,7 @@ typedef struct { ...@@ -75,7 +75,7 @@ typedef struct {
raft_msg_id_t last_acked_msg_id; raft_msg_id_t last_acked_msg_id;
/* what this node thinks is the node ID of the current leader or /* what this node thinks is the node ID of the current leader or
* RAFT_UNKNOWN_NODE_ID if there isn't a known current leader. */ * RAFT_NODE_ID_NONE if there isn't a known current leader. */
raft_node_id_t leader_id; raft_node_id_t leader_id;
/* callbacks */ /* callbacks */
...@@ -121,6 +121,8 @@ int raft_election_start(raft_server_t* me); ...@@ -121,6 +121,8 @@ int raft_election_start(raft_server_t* me);
int raft_become_candidate(raft_server_t* me); int raft_become_candidate(raft_server_t* me);
int raft_become_precandidate(raft_server_t* me);
void raft_randomize_election_timeout(raft_server_t* me_); void raft_randomize_election_timeout(raft_server_t* me_);
void raft_update_quorum_meta(raft_server_t* me_, raft_msg_id_t id); void raft_update_quorum_meta(raft_server_t* me_, raft_msg_id_t id);
......
...@@ -72,7 +72,7 @@ static void raft_log_node(raft_server_t *me_, ...@@ -72,7 +72,7 @@ static void raft_log_node(raft_server_t *me_,
me->cb.log(me_, id, me->udata, buf); me->cb.log(me_, id, me->udata, buf);
} }
#define raft_log(me, ...) (raft_log_node(me, RAFT_UNKNOWN_NODE_ID, __VA_ARGS__)) #define raft_log(me, ...) (raft_log_node(me, RAFT_NODE_ID_NONE, __VA_ARGS__))
void raft_randomize_election_timeout(raft_server_t* me_) void raft_randomize_election_timeout(raft_server_t* me_)
{ {
...@@ -116,7 +116,7 @@ raft_server_t* raft_new_with_log(const raft_log_impl_t *log_impl, void *log_arg) ...@@ -116,7 +116,7 @@ raft_server_t* raft_new_with_log(const raft_log_impl_t *log_impl, void *log_arg)
me->voting_cfg_change_log_idx = -1; me->voting_cfg_change_log_idx = -1;
raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER); raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
me->snapshot_in_progress = 0; me->snapshot_in_progress = 0;
raft_set_snapshot_metadata((raft_server_t*)me, 0, 0); raft_set_snapshot_metadata((raft_server_t*)me, 0, 0);
...@@ -155,7 +155,7 @@ void raft_clear(raft_server_t* me_) ...@@ -155,7 +155,7 @@ void raft_clear(raft_server_t* me_)
raft_randomize_election_timeout(me_); raft_randomize_election_timeout(me_);
me->voting_cfg_change_log_idx = -1; me->voting_cfg_change_log_idx = -1;
raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER); raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
me->commit_idx = 0; me->commit_idx = 0;
me->last_applied_idx = 0; me->last_applied_idx = 0;
me->num_nodes = 0; me->num_nodes = 0;
...@@ -167,7 +167,7 @@ raft_node_t* raft_add_node_internal(raft_server_t* me_, raft_entry_t *ety, void* ...@@ -167,7 +167,7 @@ raft_node_t* raft_add_node_internal(raft_server_t* me_, raft_entry_t *ety, void*
{ {
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
if (id == RAFT_UNKNOWN_NODE_ID) if (id == RAFT_NODE_ID_NONE)
return NULL; return NULL;
/* set to voting if node already exists */ /* set to voting if node already exists */
...@@ -253,7 +253,7 @@ void raft_remove_node(raft_server_t* me_, raft_node_t* node) ...@@ -253,7 +253,7 @@ void raft_remove_node(raft_server_t* me_, raft_node_t* node)
me->num_nodes--; me->num_nodes--;
if (me->leader_id == raft_node_get_id(node)) { if (me->leader_id == raft_node_get_id(node)) {
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
} }
raft_node_free(node); raft_node_free(node);
...@@ -365,7 +365,11 @@ int raft_election_start(raft_server_t* me_) ...@@ -365,7 +365,11 @@ int raft_election_start(raft_server_t* me_)
me->election_timeout_rand, me->timeout_elapsed, me->current_term, me->election_timeout_rand, me->timeout_elapsed, me->current_term,
raft_get_current_idx(me_)); raft_get_current_idx(me_));
return raft_become_candidate(me_); me->leader_id = RAFT_NODE_ID_NONE;
me->timeout_elapsed = 0;
raft_randomize_election_timeout(me_);
return raft_become_precandidate(me_);
} }
int raft_become_leader(raft_server_t* me_) int raft_become_leader(raft_server_t* me_)
...@@ -414,6 +418,32 @@ int raft_become_leader(raft_server_t* me_) ...@@ -414,6 +418,32 @@ int raft_become_leader(raft_server_t* me_)
return 0; return 0;
} }
int raft_become_precandidate(raft_server_t* me_)
{
raft_server_private_t* me = (raft_server_private_t*)me_;
raft_log(me_,
"becoming pre-candidate, next term : %ld", me->current_term + 1);
if (me->cb.notify_state_event)
me->cb.notify_state_event(me_, me->udata, RAFT_STATE_PRECANDIDATE);
for (int i = 0; i < me->num_nodes; i++)
raft_node_vote_for_me(me->nodes[i], 0);
raft_set_state(me_, RAFT_STATE_PRECANDIDATE);
for (int i = 0; i < me->num_nodes; i++)
{
raft_node_t* node = me->nodes[i];
if (me->node != node && raft_node_is_voting(node))
raft_send_requestvote(me_, node);
}
return 0;
}
int raft_become_candidate(raft_server_t* me_) int raft_become_candidate(raft_server_t* me_)
{ {
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
...@@ -432,12 +462,9 @@ int raft_become_candidate(raft_server_t* me_) ...@@ -432,12 +462,9 @@ int raft_become_candidate(raft_server_t* me_)
if (raft_node_is_voting(me->node)) if (raft_node_is_voting(me->node))
raft_vote(me_, me->node); raft_vote(me_, me->node);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
raft_set_state(me_, RAFT_STATE_CANDIDATE); raft_set_state(me_, RAFT_STATE_CANDIDATE);
raft_randomize_election_timeout(me_);
me->timeout_elapsed = 0;
for (i = 0; i < me->num_nodes; i++) for (i = 0; i < me->num_nodes; i++)
{ {
raft_node_t* node = me->nodes[i]; raft_node_t* node = me->nodes[i];
...@@ -621,7 +648,7 @@ int raft_recv_appendentries_response(raft_server_t* me_, ...@@ -621,7 +648,7 @@ int raft_recv_appendentries_response(raft_server_t* me_,
if (0 != e) if (0 != e)
return e; return e;
raft_become_follower(me_); raft_become_follower(me_);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
return 0; return 0;
} }
else if (me->current_term != r->term) else if (me->current_term != r->term)
...@@ -882,39 +909,6 @@ out: ...@@ -882,39 +909,6 @@ out:
return e; return e;
} }
int raft_already_voted(raft_server_t* me_)
{
return ((raft_server_private_t*)me_)->voted_for != -1;
}
static int raft_should_grant_vote(raft_server_t* me_, msg_requestvote_t* vr)
{
if (vr->term < raft_get_current_term(me_))
return 0;
/* TODO: if voted for is candidate return 1 (if below checks pass) */
if (raft_already_voted(me_))
return 0;
/* Below we check if log is more up-to-date... */
raft_index_t current_idx = raft_get_current_idx(me_);
/* Our log is definitely not more up-to-date if it's empty! */
if (0 == current_idx)
return 1;
raft_term_t ety_term = raft_get_last_log_term(me_);
if (ety_term < vr->last_log_term)
return 1;
if (vr->last_log_term == ety_term && current_idx <= vr->last_log_idx)
return 1;
return 0;
}
int raft_recv_requestvote(raft_server_t* me_, int raft_recv_requestvote(raft_server_t* me_,
raft_node_t* node, raft_node_t* node,
msg_requestvote_t* vr, msg_requestvote_t* vr,
...@@ -923,46 +917,68 @@ int raft_recv_requestvote(raft_server_t* me_, ...@@ -923,46 +917,68 @@ int raft_recv_requestvote(raft_server_t* me_,
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
int e = 0; int e = 0;
r->prevote = vr->prevote;
r->request_term = vr->term;
r->vote_granted = 0; r->vote_granted = 0;
if (!node)
node = raft_get_node(me_, vr->candidate_id);
/* Reject request if we have a leader */ /* Reject request if we have a leader */
if (me->leader_id != RAFT_UNKNOWN_NODE_ID && if (me->leader_id != RAFT_NODE_ID_NONE &&
me->leader_id != vr->candidate_id && me->leader_id != vr->candidate_id &&
me->timeout_elapsed < me->election_timeout) { me->timeout_elapsed < me->election_timeout) {
goto done; goto done;
} }
if (raft_get_current_term(me_) < vr->term) /* Update the term only if this is not a prevote request */
if (!vr->prevote && raft_get_current_term(me_) < vr->term)
{ {
e = raft_set_current_term(me_, vr->term); e = raft_set_current_term(me_, vr->term);
if (0 != e) { if (0 != e) {
goto done; goto done;
} }
raft_become_follower(me_); raft_become_follower(me_);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
}
if (me->current_term > vr->term) {
goto done;
} }
if (raft_should_grant_vote(me_, vr)) if (me->current_term == vr->term &&
(me->voted_for != -1 && me->voted_for != vr->candidate_id)) {
goto done;
}
/* Below we check if log is more up-to-date... */
raft_index_t current_idx = raft_get_current_idx(me_);
raft_term_t ety_term = raft_get_last_log_term(me_);
if (vr->last_log_term < ety_term ||
(vr->last_log_term == ety_term && vr->last_log_idx < current_idx)) {
goto done;
}
r->vote_granted = 1;
if (!vr->prevote)
{ {
/* It shouldn't be possible for a leader or candidate to grant a vote /* It shouldn't be possible for a leader or candidate to grant a vote
* Both states would have voted for themselves */ * Both states would have voted for themselves */
assert(!(raft_is_leader(me_) || raft_is_candidate(me_))); assert(!(raft_is_leader(me_) || raft_is_candidate(me_)));
e = raft_vote_for_nodeid(me_, vr->candidate_id); e = raft_vote_for_nodeid(me_, vr->candidate_id);
if (0 == e) if (0 != e)
r->vote_granted = 1; r->vote_granted = 0;
/* must be in an election. */ /* must be in an election. */
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
me->timeout_elapsed = 0; me->timeout_elapsed = 0;
} }
done: done:
raft_log_node(me_, vr->candidate_id, "node requested vote: %d replying: %s", raft_log_node(me_, vr->candidate_id, "node requested vote: %d, t:%ld, pv:%d replying: %s",
vr->candidate_id, vr->candidate_id,
vr->term,
vr->prevote,
r->vote_granted == 1 ? "granted" : r->vote_granted == 1 ? "granted" :
r->vote_granted == 0 ? "not granted" : "unknown"); r->vote_granted == 0 ? "not granted" : "unknown");
...@@ -985,24 +1001,31 @@ int raft_recv_requestvote_response(raft_server_t* me_, ...@@ -985,24 +1001,31 @@ int raft_recv_requestvote_response(raft_server_t* me_,
raft_server_private_t* me = (raft_server_private_t*)me_; raft_server_private_t* me = (raft_server_private_t*)me_;
raft_log_node(me_, raft_node_get_id(node), raft_log_node(me_, raft_node_get_id(node),
"node responded to requestvote status:%s ct:%ld rt:%ld", "node responded to requestvote status:%s pv:%d rt:%ld ct:%ld rt:%ld",
r->vote_granted == 1 ? "granted" : r->vote_granted == 1 ? "granted" :
r->vote_granted == 0 ? "not granted" : "unknown", r->vote_granted == 0 ? "not granted" : "unknown",
r->prevote,
r->request_term,
me->current_term, me->current_term,
r->term); r->term);
if (!raft_is_candidate(me_) || raft_get_current_term(me_) > r->term) if (r->term > me->current_term)
{
return 0;
}
if (raft_get_current_term(me_) < r->term)
{ {
int e = raft_set_current_term(me_, r->term); int e = raft_set_current_term(me_, r->term);
if (0 != e) if (0 != e)
return e; return e;
raft_become_follower(me_); raft_become_follower(me_);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
return 0;
}
if (r->prevote) {
/* Validate prevote is not stale */
if (!raft_is_precandidate(me_) || r->request_term != me->current_term + 1)
return 0;
} else {
/* Validate reqvote is not stale */
if (!raft_is_candidate(me_) || r->request_term != me->current_term)
return 0; return 0;
} }
...@@ -1010,9 +1033,13 @@ int raft_recv_requestvote_response(raft_server_t* me_, ...@@ -1010,9 +1033,13 @@ int raft_recv_requestvote_response(raft_server_t* me_,
{ {
if (node) if (node)
raft_node_vote_for_me(node, 1); raft_node_vote_for_me(node, 1);
int votes = raft_get_nvotes_for_me(me_); int votes = raft_get_nvotes_for_me(me_);
if (raft_votes_is_majority(raft_get_num_voting_nodes(me_), votes)) { int nodes = raft_get_num_voting_nodes(me_);
int e = raft_become_leader(me_);
if (raft_votes_is_majority(nodes, votes)) {
int e = raft_is_precandidate(me_) ? raft_become_candidate(me_) :
raft_become_leader(me_);
if (0 != e) if (0 != e)
return e; return e;
} }
...@@ -1094,7 +1121,17 @@ int raft_send_requestvote(raft_server_t* me_, raft_node_t* node) ...@@ -1094,7 +1121,17 @@ int raft_send_requestvote(raft_server_t* me_, raft_node_t* node)
raft_node_id_t id = raft_node_get_id(node); raft_node_id_t id = raft_node_get_id(node);
raft_log_node(me_, id, "sending requestvote to: %d", id); raft_log_node(me_, id, "sending requestvote to: %d", id);
if (raft_is_precandidate(me_))
{
rv.prevote = 1;
rv.term = me->current_term + 1;
}
else
{
rv.prevote = 0;
rv.term = me->current_term; rv.term = me->current_term;
}
rv.last_log_idx = raft_get_current_idx(me_); rv.last_log_idx = raft_get_current_idx(me_);
rv.last_log_term = raft_get_last_log_term(me_); rv.last_log_term = raft_get_last_log_term(me_);
rv.candidate_id = raft_get_nodeid(me_); rv.candidate_id = raft_get_nodeid(me_);
...@@ -1319,7 +1356,7 @@ int raft_get_nvotes_for_me(raft_server_t* me_) ...@@ -1319,7 +1356,7 @@ int raft_get_nvotes_for_me(raft_server_t* me_)
} }
} }
if (me->voted_for == raft_get_nodeid(me_)) if (raft_node_is_voting(me->node))
votes += 1; votes += 1;
return votes; return votes;
...@@ -1559,7 +1596,7 @@ int raft_begin_load_snapshot( ...@@ -1559,7 +1596,7 @@ int raft_begin_load_snapshot(
} }
raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER); raft_set_state((raft_server_t*)me, RAFT_STATE_FOLLOWER);
me->leader_id = RAFT_UNKNOWN_NODE_ID; me->leader_id = RAFT_NODE_ID_NONE;
me->log_impl->reset(me->log, last_included_index + 1, last_included_term); me->log_impl->reset(me->log, last_included_index + 1, last_included_term);
......
...@@ -204,6 +204,11 @@ int raft_is_leader(raft_server_t* me_) ...@@ -204,6 +204,11 @@ int raft_is_leader(raft_server_t* me_)
return raft_get_state(me_) == RAFT_STATE_LEADER; return raft_get_state(me_) == RAFT_STATE_LEADER;
} }
int raft_is_precandidate(raft_server_t* me_)
{
return raft_get_state(me_) == RAFT_STATE_PRECANDIDATE;
}
int raft_is_candidate(raft_server_t* me_) int raft_is_candidate(raft_server_t* me_)
{ {
return raft_get_state(me_) == RAFT_STATE_CANDIDATE; return raft_get_state(me_) == RAFT_STATE_CANDIDATE;
......
...@@ -213,13 +213,23 @@ void TestRaft_server_remove_node(CuTest * tc) ...@@ -213,13 +213,23 @@ void TestRaft_server_remove_node(CuTest * tc)
CuAssertTrue(tc, NULL == raft_get_node(r, 9)); CuAssertTrue(tc, NULL == raft_get_node(r, 9));
} }
void TestRaft_election_start_increments_term(CuTest * tc) void TestRaft_election_start_does_not_increment_term(CuTest * tc)
{ {
void *r = raft_new(); void *r = raft_new();
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_set_callbacks(r, &generic_funcs, NULL); raft_set_callbacks(r, &generic_funcs, NULL);
raft_set_current_term(r, 1); raft_set_current_term(r, 1);
raft_election_start(r); raft_election_start(r);
CuAssertTrue(tc, 1 == raft_get_current_term(r));
}
void TestRaft_election_become_candidate_increments_term(CuTest * tc)
{
void *r = raft_new();
raft_add_node(r, NULL, 1, 1);
raft_set_callbacks(r, &generic_funcs, NULL);
raft_set_current_term(r, 1);
raft_become_candidate(r);
CuAssertTrue(tc, 2 == raft_get_current_term(r)); CuAssertTrue(tc, 2 == raft_get_current_term(r));
} }
...@@ -632,7 +642,7 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_n ...@@ -632,7 +642,7 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_n
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0); raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1); raft_set_current_term(r, 1);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
memset(&rvr, 0, sizeof(msg_requestvote_response_t)); memset(&rvr, 0, sizeof(msg_requestvote_response_t));
...@@ -640,7 +650,7 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_n ...@@ -640,7 +650,7 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_n
rvr.vote_granted = 0; rvr.vote_granted = 0;
int e = raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); int e = raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
CuAssertIntEquals(tc, 0, e); CuAssertIntEquals(tc, 0, e);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
} }
void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_term_is_not_equal( void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_term_is_not_equal(
...@@ -658,14 +668,14 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_t ...@@ -658,14 +668,14 @@ void TestRaft_server_recv_requestvote_response_dont_increase_votes_for_me_when_t
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0); raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 3); raft_set_current_term(r, 3);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
memset(&rvr, 0, sizeof(msg_requestvote_response_t)); memset(&rvr, 0, sizeof(msg_requestvote_response_t));
rvr.term = 2; rvr.term = 2;
rvr.vote_granted = 1; rvr.vote_granted = 1;
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
} }
void TestRaft_server_recv_requestvote_response_increase_votes_for_me( void TestRaft_server_recv_requestvote_response_increase_votes_for_me(
...@@ -685,7 +695,7 @@ void TestRaft_server_recv_requestvote_response_increase_votes_for_me( ...@@ -685,7 +695,7 @@ void TestRaft_server_recv_requestvote_response_increase_votes_for_me(
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0); raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1); raft_set_current_term(r, 1);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
CuAssertIntEquals(tc, 1, raft_get_current_term(r)); CuAssertIntEquals(tc, 1, raft_get_current_term(r));
raft_become_candidate(r); raft_become_candidate(r);
...@@ -694,6 +704,7 @@ void TestRaft_server_recv_requestvote_response_increase_votes_for_me( ...@@ -694,6 +704,7 @@ void TestRaft_server_recv_requestvote_response_increase_votes_for_me(
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
memset(&rvr, 0, sizeof(msg_requestvote_response_t)); memset(&rvr, 0, sizeof(msg_requestvote_response_t));
rvr.request_term = 2;
rvr.term = 2; rvr.term = 2;
rvr.vote_granted = 1; rvr.vote_granted = 1;
int e = raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); int e = raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
...@@ -717,7 +728,7 @@ void TestRaft_server_recv_requestvote_response_must_be_candidate_to_receive( ...@@ -717,7 +728,7 @@ void TestRaft_server_recv_requestvote_response_must_be_candidate_to_receive(
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0); raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1); raft_set_current_term(r, 1);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
raft_become_leader(r); raft_become_leader(r);
...@@ -726,7 +737,7 @@ void TestRaft_server_recv_requestvote_response_must_be_candidate_to_receive( ...@@ -726,7 +737,7 @@ void TestRaft_server_recv_requestvote_response_must_be_candidate_to_receive(
rvr.term = 1; rvr.term = 1;
rvr.vote_granted = 1; rvr.vote_granted = 1;
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
CuAssertTrue(tc, 0 == raft_get_nvotes_for_me(r)); CuAssertTrue(tc, 1 == raft_get_nvotes_for_me(r));
} }
/* Reply false if term < currentTerm (§5.1) */ /* Reply false if term < currentTerm (§5.1) */
...@@ -934,7 +945,7 @@ void TestRaft_server_recv_requestvote_dont_grant_vote_if_we_didnt_vote_for_this_ ...@@ -934,7 +945,7 @@ void TestRaft_server_recv_requestvote_dont_grant_vote_if_we_didnt_vote_for_this_
msg_requestvote_t rv = {}; msg_requestvote_t rv = {};
rv.term = 1; rv.term = 1;
rv.candidate_id = 1; rv.candidate_id = 2;
rv.last_log_idx = 1; rv.last_log_idx = 1;
rv.last_log_term = 1; rv.last_log_term = 1;
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
...@@ -987,6 +998,73 @@ void TestRaft_server_recv_requestvote_ignore_if_master_is_fresh(CuTest * tc) ...@@ -987,6 +998,73 @@ void TestRaft_server_recv_requestvote_ignore_if_master_is_fresh(CuTest * tc)
CuAssertTrue(tc, 1 == rvr.vote_granted); CuAssertTrue(tc, 1 == rvr.vote_granted);
} }
void TestRaft_server_recv_prevote_ignore_if_candidate(CuTest * tc)
{
void *r = raft_new();
raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1);
raft_set_election_timeout(r, 1000);
raft_become_candidate(r);
msg_requestvote_response_t rv = {
.term = 1,
.request_term = 1,
.prevote = 1,
};
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rv);
CuAssertTrue(tc, raft_is_candidate(r));
CuAssertTrue(tc, raft_get_nvotes_for_me(r) == 1);
}
void TestRaft_server_recv_reqvote_ignore_if_not_candidate(CuTest * tc)
{
void *r = raft_new();
raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1);
raft_set_election_timeout(r, 1000);
raft_become_precandidate(r);
msg_requestvote_response_t rv = {
.term = 1,
.request_term = 1
};
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rv);
CuAssertTrue(tc, raft_is_precandidate(r));
CuAssertTrue(tc, raft_get_nvotes_for_me(r) == 1);
}
void TestRaft_server_recv_reqvote_always_update_term(CuTest * tc)
{
void *r = raft_new();
raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0);
raft_set_current_term(r, 1);
raft_set_election_timeout(r, 1000);
raft_become_precandidate(r);
msg_requestvote_response_t rv = {
.term = 3,
.request_term = 3,
};
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rv);
CuAssertTrue(tc, raft_is_follower(r));
CuAssertTrue(tc, raft_get_current_term(r) == 3);
}
void TestRaft_follower_becomes_follower_is_follower(CuTest * tc) void TestRaft_follower_becomes_follower_is_follower(CuTest * tc)
{ {
void *r = raft_new(); void *r = raft_new();
...@@ -1686,7 +1764,7 @@ void TestRaft_follower_recv_appendentries_failure_includes_current_idx( ...@@ -1686,7 +1764,7 @@ void TestRaft_follower_recv_appendentries_failure_includes_current_idx(
CuAssertIntEquals(tc, 2, aer.current_idx); CuAssertIntEquals(tc, 2, aer.current_idx);
} }
void TestRaft_follower_becomes_candidate_when_election_timeout_occurs( void TestRaft_follower_becomes_precandidate_when_election_timeout_occurs(
CuTest * tc) CuTest * tc)
{ {
raft_cbs_t funcs = { raft_cbs_t funcs = {
...@@ -1708,7 +1786,7 @@ void TestRaft_follower_becomes_candidate_when_election_timeout_occurs( ...@@ -1708,7 +1786,7 @@ void TestRaft_follower_becomes_candidate_when_election_timeout_occurs(
raft_periodic(r, max_election_timeout(1000) + 1); raft_periodic(r, max_election_timeout(1000) + 1);
/* is a candidate now */ /* is a candidate now */
CuAssertTrue(tc, 1 == raft_is_candidate(r)); CuAssertTrue(tc, 1 == raft_is_precandidate(r));
} }
/* Candidate 5.2 */ /* Candidate 5.2 */
...@@ -2024,11 +2102,6 @@ void TestRaft_candidate_election_timeout_and_no_leader_results_in_new_election( ...@@ -2024,11 +2102,6 @@ void TestRaft_candidate_election_timeout_and_no_leader_results_in_new_election(
void *r = raft_new(); void *r = raft_new();
raft_set_callbacks(r, &funcs, NULL); raft_set_callbacks(r, &funcs, NULL);
msg_requestvote_response_t vr;
memset(&vr, 0, sizeof(msg_requestvote_response_t));
vr.term = 0;
vr.vote_granted = 1;
raft_add_node(r, NULL, 1, 1); raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0); raft_add_node(r, NULL, 2, 0);
raft_set_election_timeout(r, 1000); raft_set_election_timeout(r, 1000);
...@@ -2037,13 +2110,30 @@ void TestRaft_candidate_election_timeout_and_no_leader_results_in_new_election( ...@@ -2037,13 +2110,30 @@ void TestRaft_candidate_election_timeout_and_no_leader_results_in_new_election(
raft_become_candidate(r); raft_become_candidate(r);
CuAssertTrue(tc, 1 == raft_get_current_term(r)); CuAssertTrue(tc, 1 == raft_get_current_term(r));
/* clock over (ie. max election timeout + 1), causing new election */ /* clock over (ie. max election timeout + 1), does not increment term */
raft_periodic(r, max_election_timeout(1000) + 1); raft_periodic(r, max_election_timeout(1000) + 1);
CuAssertTrue(tc, 2 == raft_get_current_term(r)); CuAssertTrue(tc, 1 == raft_get_current_term(r));
CuAssertIntEquals(tc, RAFT_STATE_PRECANDIDATE, raft_get_state(r));
msg_requestvote_response_t vr0 = {
.prevote = 1,
.request_term = raft_get_current_term(r) + 1,
.term = 0,
.vote_granted = 1
};
/* receiving this vote gives the server majority */
raft_recv_requestvote_response(r, raft_get_node(r, 2), &vr0);
CuAssertIntEquals(tc, RAFT_STATE_CANDIDATE, raft_get_state(r));
msg_requestvote_response_t vr1 = {
.request_term = 2,
.term = 2,
.vote_granted = 1
};
/* receiving this vote gives the server majority */ /* receiving this vote gives the server majority */
// raft_recv_requestvote_response(r,1,&vr); raft_recv_requestvote_response(r, raft_get_node(r, 2), &vr1);
// CuAssertTrue(tc, 1 == raft_is_leader(r)); CuAssertIntEquals(tc, RAFT_STATE_LEADER, raft_get_state(r));
} }
/* Candidate 5.2 */ /* Candidate 5.2 */
...@@ -2075,6 +2165,7 @@ void TestRaft_candidate_receives_majority_of_votes_becomes_leader(CuTest * tc) ...@@ -2075,6 +2165,7 @@ void TestRaft_candidate_receives_majority_of_votes_becomes_leader(CuTest * tc)
/* a vote for us */ /* a vote for us */
memset(&vr, 0, sizeof(msg_requestvote_response_t)); memset(&vr, 0, sizeof(msg_requestvote_response_t));
vr.request_term = 1;
vr.term = 1; vr.term = 1;
vr.vote_granted = 1; vr.vote_granted = 1;
/* get one vote */ /* get one vote */
...@@ -2170,6 +2261,7 @@ void TestRaft_candidate_recv_requestvote_response_becomes_follower_if_current_te ...@@ -2170,6 +2261,7 @@ void TestRaft_candidate_recv_requestvote_response_becomes_follower_if_current_te
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr;
memset(&rvr, 0, sizeof(msg_requestvote_response_t)); memset(&rvr, 0, sizeof(msg_requestvote_response_t));
rvr.request_term = 2;
rvr.term = 2; rvr.term = 2;
rvr.vote_granted = 0; rvr.vote_granted = 0;
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
...@@ -3571,17 +3663,30 @@ void TestRaft_leader_recv_requestvote_responds_without_granting(CuTest * tc) ...@@ -3571,17 +3663,30 @@ void TestRaft_leader_recv_requestvote_responds_without_granting(CuTest * tc)
raft_election_start(r); raft_election_start(r);
msg_requestvote_response_t rvr; msg_requestvote_response_t rvr = {
memset(&rvr, 0, sizeof(msg_requestvote_response_t)); .prevote = 1,
rvr.term = 1; .request_term = raft_get_current_term(r) + 1,
rvr.vote_granted = 1; .term = 0,
.vote_granted = 1
};
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr); raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr);
CuAssertTrue(tc, 1 == raft_is_candidate(r));
msg_requestvote_response_t rvr1 = {
.request_term = 1,
.term = 1,
.vote_granted = 1
};
raft_recv_requestvote_response(r, raft_get_node(r, 2), &rvr1);
CuAssertTrue(tc, 1 == raft_is_leader(r)); CuAssertTrue(tc, 1 == raft_is_leader(r));
/* receive request vote from node 3 */ /* receive request vote from node 3 */
msg_requestvote_t rv; msg_requestvote_t rv = {
memset(&rv, 0, sizeof(msg_requestvote_t)); .term = 1
rv.term = 1; };
raft_recv_requestvote(r, raft_get_node(r, 3), &rv, &rvr); raft_recv_requestvote(r, raft_get_node(r, 3), &rv, &rvr);
CuAssertTrue(tc, 0 == rvr.vote_granted); CuAssertTrue(tc, 0 == rvr.vote_granted);
} }
...@@ -4133,8 +4238,10 @@ void TestRaft_removed_node_starts_election(CuTest * tc) ...@@ -4133,8 +4238,10 @@ void TestRaft_removed_node_starts_election(CuTest * tc)
raft_become_follower(r); raft_become_follower(r);
raft_set_election_timeout(r, 1000); raft_set_election_timeout(r, 1000);
raft_periodic(r, 2000); raft_periodic(r, 2000);
raft_become_candidate(r);
msg_requestvote_response_t reqresp = { msg_requestvote_response_t reqresp = {
.request_term = 2,
.vote_granted = 1, .vote_granted = 1,
.term = 2 .term = 2
}; };
...@@ -4163,7 +4270,8 @@ int main(void) ...@@ -4163,7 +4270,8 @@ int main(void)
SUITE_ADD_TEST(suite, TestRaft_server_add_non_voting_node_with_already_existing_id_is_not_allowed); SUITE_ADD_TEST(suite, TestRaft_server_add_non_voting_node_with_already_existing_id_is_not_allowed);
SUITE_ADD_TEST(suite, TestRaft_server_add_non_voting_node_with_already_existing_voting_id_is_not_allowed); SUITE_ADD_TEST(suite, TestRaft_server_add_non_voting_node_with_already_existing_voting_id_is_not_allowed);
SUITE_ADD_TEST(suite, TestRaft_server_remove_node); SUITE_ADD_TEST(suite, TestRaft_server_remove_node);
SUITE_ADD_TEST(suite, TestRaft_election_start_increments_term); SUITE_ADD_TEST(suite, TestRaft_election_start_does_not_increment_term);
SUITE_ADD_TEST(suite, TestRaft_election_become_candidate_increments_term);
SUITE_ADD_TEST(suite, TestRaft_set_state); SUITE_ADD_TEST(suite, TestRaft_set_state);
SUITE_ADD_TEST(suite, TestRaft_server_starts_as_follower); SUITE_ADD_TEST(suite, TestRaft_server_starts_as_follower);
SUITE_ADD_TEST(suite, TestRaft_server_starts_with_election_timeout_of_1000ms); SUITE_ADD_TEST(suite, TestRaft_server_starts_with_election_timeout_of_1000ms);
...@@ -4200,6 +4308,9 @@ int main(void) ...@@ -4200,6 +4308,9 @@ int main(void)
SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_depends_on_candidate_id); SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_depends_on_candidate_id);
SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_dont_grant_vote_if_we_didnt_vote_for_this_candidate); SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_dont_grant_vote_if_we_didnt_vote_for_this_candidate);
SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_ignore_if_master_is_fresh); SUITE_ADD_TEST(suite, TestRaft_server_recv_requestvote_ignore_if_master_is_fresh);
SUITE_ADD_TEST(suite, TestRaft_server_recv_prevote_ignore_if_candidate);
SUITE_ADD_TEST(suite, TestRaft_server_recv_reqvote_ignore_if_not_candidate);
SUITE_ADD_TEST(suite, TestRaft_server_recv_reqvote_always_update_term);
SUITE_ADD_TEST(suite, TestRaft_follower_becomes_follower_is_follower); SUITE_ADD_TEST(suite, TestRaft_follower_becomes_follower_is_follower);
SUITE_ADD_TEST(suite, TestRaft_follower_becomes_follower_does_not_clear_voted_for); SUITE_ADD_TEST(suite, TestRaft_follower_becomes_follower_does_not_clear_voted_for);
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_reply_false_if_term_less_than_currentterm); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_reply_false_if_term_less_than_currentterm);
...@@ -4217,7 +4328,7 @@ int main(void) ...@@ -4217,7 +4328,7 @@ int main(void)
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_set_commitidx_to_prevLogIdx); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_set_commitidx_to_prevLogIdx);
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_set_commitidx_to_LeaderCommit); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_set_commitidx_to_LeaderCommit);
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_failure_includes_current_idx); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_failure_includes_current_idx);
SUITE_ADD_TEST(suite, TestRaft_follower_becomes_candidate_when_election_timeout_occurs); SUITE_ADD_TEST(suite, TestRaft_follower_becomes_precandidate_when_election_timeout_occurs);
SUITE_ADD_TEST(suite, TestRaft_follower_dont_grant_vote_if_candidate_has_a_less_complete_log); SUITE_ADD_TEST(suite, TestRaft_follower_dont_grant_vote_if_candidate_has_a_less_complete_log);
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_heartbeat_does_not_overwrite_logs); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_heartbeat_does_not_overwrite_logs);
SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_does_not_deleted_commited_entries); SUITE_ADD_TEST(suite, TestRaft_follower_recv_appendentries_does_not_deleted_commited_entries);
......
...@@ -101,6 +101,8 @@ def state2str(state): ...@@ -101,6 +101,8 @@ def state2str(state):
return colorama.Fore.GREEN + 'leader' + colorama.Style.RESET_ALL return colorama.Fore.GREEN + 'leader' + colorama.Style.RESET_ALL
elif state == lib.RAFT_STATE_CANDIDATE: elif state == lib.RAFT_STATE_CANDIDATE:
return 'candidate' return 'candidate'
elif state == lib.RAFT_STATE_PRECANDIDATE:
return 'pre-candidate'
elif state == lib.RAFT_STATE_FOLLOWER: elif state == lib.RAFT_STATE_FOLLOWER:
return 'follower' return 'follower'
else: else:
......
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