diff --git a/src/ra_server.erl b/src/ra_server.erl index 38c38c7d..6c95e59c 100644 --- a/src/ra_server.erl +++ b/src/ra_server.erl @@ -919,9 +919,11 @@ handle_candidate(#pre_vote_rpc{term = Term} = Msg, handle_candidate(#request_vote_rpc{}, State = #{current_term := Term}) -> Reply = #request_vote_result{term = Term, vote_granted = false}, {candidate, State, [{reply, Reply}]}; -handle_candidate(#pre_vote_rpc{}, State) -> - %% just ignore pre_votes that aren't of a higher term - {candidate, State, []}; +handle_candidate(#pre_vote_rpc{} = PreVote, State) -> + %% unlike request_vote_rpc, a candidate cannot simply reject + %% a pre_vote_rpc that does not have a higher term + %% (see https://github.com/rabbitmq/ra/issues/439 for the detail) + process_pre_vote(candidate, PreVote, State); handle_candidate(#request_vote_result{}, State) -> %% handle to avoid logging as unhandled {candidate, State, []}; diff --git a/test/ra_server_SUITE.erl b/test/ra_server_SUITE.erl index de6bdf6b..795d38f8 100644 --- a/test/ra_server_SUITE.erl +++ b/test/ra_server_SUITE.erl @@ -33,6 +33,7 @@ all() -> higher_term_detected, pre_vote_election, pre_vote_election_reverts, + candidate_receives_pre_vote, leader_receives_pre_vote, candidate_election, is_new, @@ -1927,6 +1928,30 @@ pre_vote_election_reverts(_Config) -> = ra_server:handle_pre_vote(ISR, State), ok. +candidate_receives_pre_vote(_Config) -> + % candidate ignores an pre_vote_rpc with lower term and lower index + Token = make_ref(), + State = (base_state(5, ?FUNCTION_NAME))#{votes => 1}, + PreVoteRpc = #pre_vote_rpc{term = 5, candidate_id = ?N1, + token = Token, + machine_version = 0, + last_log_index = 3, last_log_term = 5}, + % candidate replies `#pre_vote_result{vote_granted=true}` for not lower index + {candidate, #{}, + [{reply, #pre_vote_result{token = Token, vote_granted = true}}]} + = ra_server:handle_candidate(PreVoteRpc, State), + + % candidate replies `#pre_vote_result{vote_granted=false}` for lower index + {candidate, #{}, + [{reply, #pre_vote_result{token = Token, vote_granted = false}}]} + = ra_server:handle_candidate(PreVoteRpc#pre_vote_rpc{last_log_index = 2}, State), + + % candidate abdicates for higher term + {follower, #{current_term := 6}, _} + = ra_server:handle_candidate(PreVoteRpc#pre_vote_rpc{term = 6}, State), + + ok. + leader_receives_pre_vote(_Config) -> % leader should emit rpcs to all nodes immediately upon receiving % an pre_vote_rpc to put upstart followers back in their place