Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/raft_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void raft_randomize_election_timeout(raft_server_t* me_)
{
raft_server_private_t* me = (raft_server_private_t*)me_;

/* [election_timeout, 2 * election_timeout) */
/* [election_timeout, 2 * election_timeout) (§9.3) */
me->election_timeout_rand = me->election_timeout + rand() % me->election_timeout;
__log(me_, NULL, "randomize election timeout to %d", me->election_timeout_rand);
}
Expand Down Expand Up @@ -743,6 +743,13 @@ int raft_recv_entry(raft_server_t* me_,
if (0 != e)
return e;

/* Reset the heartbeat timer: for a full request_timeout period we'll be
* good and we won't need to contact followers again, since this was not an
* idle period ("Upon election: send initial empty AppendEntries RPCs
* (heartbeat) to each server; repeat during idle periods to prevent
* election timeouts"). */
me->timeout_elapsed = 0;

for (i = 0; i < me->num_nodes; i++)
{
raft_node_t* node = me->nodes[i];
Expand Down
61 changes: 61 additions & 0 deletions tests/test_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -3843,6 +3843,67 @@ void TestRaft_leader_sends_empty_appendentries_every_request_timeout(
CuAssertTrue(tc, NULL != ae);
}

void TestRaft_leader_does_not_send_empty_appendentries_if_last_appendentries_is_recent(
CuTest * tc)
{
raft_cbs_t funcs = {
.send_appendentries = sender_appendentries,
.log = NULL
};

void *sender = sender_new(NULL);
void *r = raft_new();
raft_set_callbacks(r, &funcs, sender);
raft_add_node(r, NULL, 1, 1);
raft_add_node(r, NULL, 2, 0);
raft_add_node(r, NULL, 3, 0);
raft_set_election_timeout(r, 1000);
raft_set_request_timeout(r, 500);
CuAssertTrue(tc, 0 == raft_get_timeout_elapsed(r));

/* candidate to leader */
raft_set_state(r, RAFT_STATE_CANDIDATE);
raft_become_leader(r);

/* receive initial empty appendentries messages for both nodes */
msg_appendentries_t* ae;
ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL != ae);
ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL != ae);

ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL == ae);

/* let some time elapse, but not enough to trigger the request timeout */
raft_periodic(r, 250);

ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL == ae);

/* receive an entry from the user and send appendentries requests to both
* follower nodes */
msg_entry_t mety = {};
mety.id = 1;
mety.data.buf = "entry";
mety.data.len = strlen("entry");

msg_entry_response_t cr;
raft_recv_entry(r, &mety, &cr);

ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL != ae);
ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL != ae);

/* let some more time elapse, this time it's past the initial request
* timeout, but the timer has been reset so we don't send an empty
* appendentries */
raft_periodic(r, 251);
ae = sender_poll_msg_data(sender);
CuAssertTrue(tc, NULL == ae);
}

/* TODO: If a server receives a request with a stale term number, it rejects the request. */
#if 0
void T_estRaft_leader_sends_appendentries_when_receive_entry_msg(CuTest * tc)
Expand Down