#include "internal.h" void libfloat_vote_for(libfloat_ctx_t *ctx, libfloat_node_id_t id) { ctx->persistent.voted_for = id; ctx->write_vote(ctx, id); } static int libfloat_get_number_of_votes_for_me(libfloat_ctx_t *ctx) { int ret = 0; libfloat_node_t *node; for_every_node(ctx, node, { if (node->has_voted_for_me) ret++; }); return ret; } void libfloat_election_start(libfloat_ctx_t *ctx) { libfloat_node_t *node; libfloat_term_t last_term = 0; libfloat_entry_id_t last_id = 0; DEBUG(ctx, "Election starting!"); /* First, reset the vote of everyone */ for_every_node(ctx, node, { node->has_voted_for_me = 0; }); /* Increment term */ libfloat_set_current_term(ctx, ctx->persistent.term + 1); /* Then, vote for myself (Democracy!) */ libfloat_vote_for(ctx, ctx->me->id); ctx->me->has_voted_for_me = 1; /* Discard the current leader and become candidate */ ctx->leader = NULL; libfloat_become_candidate(ctx); /* Randomize election timeout, and reset counter */ ctx->election_timeout_rand = ctx->conf.election_timeout + ctx->rand() % ctx->conf.election_timeout; ctx->timeout_elapsed = 0; libfloat_get_last_term(ctx, &last_id, &last_term); /* Send a vote request to each node of the cluster */ for_every_node(ctx, node, { libfloat_rpc_request_vote_t vote = { 0 }; if (node == ctx->me) { /* Not talking to myself */ continue; } /* Set informations and send the request to the node */ vote.term = ctx->persistent.term; vote.candidate_id = ctx->me->id; vote.last_log_index = last_id; vote.last_log_term = last_term; ctx->request_vote(ctx, node, &vote); }); } static bool libfloat_can_i_grant_vote(libfloat_ctx_t *ctx, libfloat_rpc_request_vote_t *req) { libfloat_term_t last_term; /* Let's check if I have already voted for someone */ if (ctx->persistent.voted_for != 0) { if (ctx->persistent.voted_for == req->candidate_id) return true; return false; } if (libfloat_get_last_term(ctx, NULL, &last_term) == false) { /* We have failed to retrieve last_ferm from log */ return false; } /* Check term match */ if (last_term > req->last_log_term && req->last_log_index <= ctx->persistent.commit_index) { /* We have a superior term and a superior log, we can't grant our vote */ return false; } /* Check commit index match */ if (ctx->persistent.commit_index > req->last_log_index) { /* We have a superior log, we can't grant our vote */ /* There's an election in progress, and we have a superior log, let's try to become leader */ libfloat_election_start(ctx); return false; } /* All good! */ return true; } void libfloat_request_vote_receive(libfloat_ctx_t *ctx, libfloat_rpc_request_vote_t *req, libfloat_rpc_response_vote_t *resp) { libfloat_node_t *node = libfloat_get_node(ctx, req->candidate_id); resp->node = ctx->me->id; if (node == NULL) { /* I don't know you, go away */ return; } if (ctx->leader != NULL && ctx->leader != node && (ctx->timeout_elapsed < ctx->election_timeout_rand) && ctx->persistent.term >= req->term) { /* We already have a leader, and it's not the node that we're currently talking to */ resp->vote_granted = false; goto end; } if (ctx->persistent.term < req->term) { libfloat_set_current_term(ctx, req->term); libfloat_become_follower(ctx); ctx->timeout_elapsed = 0; } if (libfloat_can_i_grant_vote(ctx, req)) { DEBUG(ctx, "Election: Granted vote to node %d", node->id); libfloat_vote_for(ctx, node->id); resp->vote_granted = true; ctx->leader = NULL; ctx->timeout_elapsed = 0; } else { resp->vote_granted = false; } end: resp->term = ctx->persistent.term; } void libfloat_request_vote_response(libfloat_ctx_t *ctx, libfloat_rpc_response_vote_t *resp) { libfloat_node_t *node = libfloat_get_node(ctx, resp->node); int votes; if (node == NULL) { /* I don't know you, go away */ return; } if (ctx->state != RAFT_STATE_CANDIDATE) { /* I am not a candidate, I don't care about this */ return; } if (ctx->persistent.term < resp->term) { libfloat_set_current_term(ctx, resp->term); libfloat_become_follower(ctx); ctx->leader = NULL; return; } if (ctx->persistent.term != resp->term) { /* The node who voted for us would have obtained our term */ return; } if (resp->vote_granted == false) return; node->has_voted_for_me = 1; votes = libfloat_get_number_of_votes_for_me(ctx); if (votes >= ctx->n_nodes / 2 + 1) { /* We have a majority! */ if (ctx->leader == NULL) { DEBUG(ctx, "I have a majority of votes! Assuming leadership"); libfloat_become_leader(ctx); ctx->stat.leader_election++; } } } void libfloat_election_resign(libfloat_ctx_t *ctx) { libfloat_node_t *node; if (ctx->state != RAFT_STATE_LEADER) { ERROR(ctx ,"libfloat_election_resign: not a leader, I can't resign"); return; } libfloat_step_down(ctx); if (ctx->resign == NULL) return; for_every_node(ctx, node, { /* Skip myself */ if (ctx->me == node) continue; ctx->resign(ctx, node); }); } void libfloat_election_resign_receive(libfloat_ctx_t *ctx) { ERROR(ctx, "Restart election"); libfloat_election_start(ctx); }