Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Learner needs to respond vote requests. #58

Merged
merged 10 commits into from
May 12, 2018
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ categories = ["algorithms", "database-implementations"]

[dependencies]
log = "0.4.1"
protobuf = "1.2"
protobuf = "~1.5"
quick-error = "1.2.1"
rand = "0.4"
fxhash = "0.2.1"
Expand Down
19 changes: 1 addition & 18 deletions src/raft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,7 @@ impl<T: Storage> Raft<T> {
return;
}

// Only send vote request to voters.
let prs = self.take_prs();
prs.voters()
.keys()
Expand Down Expand Up @@ -1016,24 +1017,6 @@ impl<T: Storage> Raft<T> {
debug!("{} ignoring MsgHup because already leader", self.tag);
},
MessageType::MsgRequestVote | MessageType::MsgRequestPreVote => {
if self.is_learner {
// TODO: learner may need to vote, in case of node down when confchange.
info!(
"{} [logterm: {}, index: {}, vote: {}] ignored {:?} from {} \
[logterm: {}, index: {}] at term {}: learner can not vote",
self.tag,
self.raft_log.last_term(),
self.raft_log.last_index(),
self.vote,
m.get_msg_type(),
m.get_from(),
m.get_log_term(),
m.get_index(),
self.term,
);
return Ok(());
}

// We can vote if this is a repeat of a vote we've already cast...
let can_vote = (self.vote == m.get_from()) ||
// ...we haven't voted and we don't think there's a leader yet in this term...
Expand Down
43 changes: 28 additions & 15 deletions tests/cases/test_raft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3784,21 +3784,6 @@ fn test_learner_promotion() {
assert_eq!(network.peers[&2].state, StateRole::Leader);
}

// TestLearnerCannotVote checks that a learner can't vote even it receives a valid Vote request.
#[test]
fn test_learner_cannot_vote() {
let mut n2 = new_test_learner_raft(2, vec![1], vec![2], 10, 1, new_storage());
n2.become_follower(1, INVALID_ID);

let mut msg_vote = new_message(1, 2, MessageType::MsgRequestVote, 0);
msg_vote.set_term(2);
msg_vote.set_log_term(11);
msg_vote.set_index(11);
n2.step(msg_vote).unwrap();

assert_eq!(n2.msgs.len(), 0);
}

// TestLearnerLogReplication tests that a learner can receive entries from the leader.
#[test]
fn test_learner_log_replication() {
Expand Down Expand Up @@ -3964,3 +3949,31 @@ fn test_remove_learner() {
assert!(n1.prs().nodes().is_empty());
assert!(n1.prs().learner_nodes().is_empty());
}

#[test]
fn test_learner_respond_vote() {
let mut n1 = new_test_learner_raft(1, vec![1, 2], vec![3], 10, 1, new_storage());
n1.become_follower(1, INVALID_ID);
n1.reset_randomized_election_timeout();

let mut n3 = new_test_learner_raft(3, vec![1, 2], vec![3], 10, 1, new_storage());
n3.become_follower(1, INVALID_ID);
n3.reset_randomized_election_timeout();

let do_campaign = |nw: &mut Network| {
let msg = new_message(1, 1, MessageType::MsgHup, 0);
nw.send(vec![msg]);
};

let mut network = Network::new(vec![Some(n1), None, Some(n3)]);
network.isolate(2);

// Can't elect new leader because 1 won't send MsgRequestVote to 3.
do_campaign(&mut network);
assert_eq!(network.peers[&1].state, StateRole::Candidate);

// After promote 3 to voter, election should success.
network.peers.get_mut(&1).unwrap().add_node(3);
do_campaign(&mut network);
assert_eq!(network.peers[&1].state, StateRole::Leader);
}