(&mut self)
| 16 | |
| 17 | impl<S: LogStorage> RaftNode<S> { |
| 18 | pub(super) fn start_election(&mut self) { |
| 19 | // Learners and observers never stand for election — defensive check |
| 20 | // in case `tick()` is bypassed (e.g., tests forcing a deadline). |
| 21 | match self.role { |
| 22 | NodeRole::Learner | NodeRole::Observer => return, |
| 23 | NodeRole::Follower | NodeRole::Candidate | NodeRole::Leader => {} |
| 24 | } |
| 25 | |
| 26 | self.hard_state.current_term += 1; |
| 27 | self.role = NodeRole::Candidate; |
| 28 | self.hard_state.voted_for = self.config.node_id; |
| 29 | self.votes_received.clear(); |
| 30 | self.leader_id = 0; |
| 31 | |
| 32 | self.persist_hard_state(); |
| 33 | self.reset_election_timeout(); |
| 34 | |
| 35 | info!( |
| 36 | node = self.config.node_id, |
| 37 | group = self.config.group_id, |
| 38 | term = self.hard_state.current_term, |
| 39 | "starting election" |
| 40 | ); |
| 41 | |
| 42 | // Single-voter cluster: win immediately. (Learners in the group do |
| 43 | // not count — single voter + N learners still elects the single |
| 44 | // voter as leader.) |
| 45 | if self.config.peers.is_empty() { |
| 46 | self.become_leader(); |
| 47 | return; |
| 48 | } |
| 49 | |
| 50 | for &peer in &self.config.peers { |
| 51 | self.ready.vote_requests.push(( |
| 52 | peer, |
| 53 | RequestVoteRequest { |
| 54 | term: self.hard_state.current_term, |
| 55 | candidate_id: self.config.node_id, |
| 56 | last_log_index: self.log.last_index(), |
| 57 | last_log_term: self.log.last_term(), |
| 58 | group_id: self.config.group_id, |
| 59 | }, |
| 60 | )); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | /// Step down to follower (or keep learner/observer role if the node is one). |
| 65 | /// |
no test coverage detected