use super::*;
pub struct Effect {
pub voter: Voter,
}
impl Effect {
fn state_machine(&self) -> &Read<StateMachine> {
&self.voter.state_machine
}
pub async fn exec(
self,
candidate_term: Term,
candidate_id: NodeAddress,
candidate_last_log_clock: Clock,
force_vote: bool,
pre_vote: bool,
) -> Result<bool> {
let _g = self.voter.vote_sequencer.try_acquire()?;
let allow_update_ballot = !pre_vote;
let leader_failed = self.voter.get_election_timeout().is_some();
let should_vote = force_vote || leader_failed;
if !should_vote {
return Ok(false);
}
let mut ballot = self.voter.read_ballot().await?;
if candidate_term < ballot.cur_term {
warn!("candidate term is older. reject vote");
return Ok(false);
}
if candidate_term > ballot.cur_term {
warn!("received newer term. reset vote");
ballot.cur_term = candidate_term;
ballot.voted_for = None;
if allow_update_ballot {
self.voter.write_election_state(ElectionState::Follower);
}
}
let last_log_clock = {
let cur_last_index = self.state_machine().get_log_last_index().await?;
if cur_last_index == 0 {
Clock { term: 0, index: 0 }
} else {
self.state_machine()
.get_entry(cur_last_index)
.await?
.this_clock
}
};
let candidate_win = match candidate_last_log_clock.term.cmp(&last_log_clock.term) {
std::cmp::Ordering::Greater => true,
std::cmp::Ordering::Equal => candidate_last_log_clock.index >= last_log_clock.index,
std::cmp::Ordering::Less => false,
};
if !candidate_win {
warn!("candidate clock is older. reject vote");
if allow_update_ballot {
self.voter.write_ballot(ballot).await?;
}
return Ok(false);
}
let grant = match &ballot.voted_for {
None => {
info!("learn node({candidate_id}) as the new leader");
ballot.voted_for = Some(candidate_id.clone());
true
}
Some(id) => {
if id == &candidate_id {
true
} else {
warn!("reject vote for having voted at term({candidate_term})");
false
}
}
};
if allow_update_ballot {
self.voter.write_ballot(ballot).await?;
}
info!("voted response grant({grant}) to node({candidate_id})");
Ok(grant)
}
}