Skip to main content

nodedb_raft/node/rpc/
request_vote.rs

1// SPDX-License-Identifier: BUSL-1.1
2
3//! `RequestVote` request and response handlers.
4
5use tracing::debug;
6
7use crate::message::{RequestVoteRequest, RequestVoteResponse};
8use crate::node::core::RaftNode;
9use crate::state::NodeRole;
10use crate::storage::LogStorage;
11
12impl<S: LogStorage> RaftNode<S> {
13    /// Handle incoming RequestVote RPC.
14    ///
15    /// Learners and observers never grant votes: by definition they are not
16    /// members of the voting set for this term, and granting a vote could
17    /// let an incorrect quorum form.
18    pub fn handle_request_vote(&mut self, req: &RequestVoteRequest) -> RequestVoteResponse {
19        match self.role {
20            NodeRole::Learner | NodeRole::Observer => {
21                // Learners and observers never grant votes.
22                return RequestVoteResponse {
23                    term: self.hard_state.current_term,
24                    vote_granted: false,
25                };
26            }
27            NodeRole::Follower | NodeRole::Candidate | NodeRole::Leader => {}
28        }
29
30        if req.term > self.hard_state.current_term {
31            self.become_follower(req.term);
32        }
33
34        if req.term < self.hard_state.current_term {
35            return RequestVoteResponse {
36                term: self.hard_state.current_term,
37                vote_granted: false,
38            };
39        }
40
41        let voted_for = self.hard_state.voted_for;
42        let can_vote = voted_for == 0 || voted_for == req.candidate_id;
43
44        let log_ok = req.last_log_term > self.log.last_term()
45            || (req.last_log_term == self.log.last_term()
46                && req.last_log_index >= self.log.last_index());
47
48        if can_vote && log_ok {
49            self.hard_state.voted_for = req.candidate_id;
50            self.persist_hard_state();
51            self.reset_election_timeout();
52
53            debug!(
54                node = self.config.node_id,
55                group = self.config.group_id,
56                candidate = req.candidate_id,
57                term = req.term,
58                "granted vote"
59            );
60
61            RequestVoteResponse {
62                term: self.hard_state.current_term,
63                vote_granted: true,
64            }
65        } else {
66            RequestVoteResponse {
67                term: self.hard_state.current_term,
68                vote_granted: false,
69            }
70        }
71    }
72
73    /// Handle RequestVote response (candidate only).
74    pub fn handle_request_vote_response(&mut self, peer: u64, resp: &RequestVoteResponse) {
75        if resp.term > self.hard_state.current_term {
76            self.become_follower(resp.term);
77            return;
78        }
79
80        if self.role != NodeRole::Candidate {
81            return;
82        }
83
84        if resp.vote_granted {
85            self.votes_received.insert(peer);
86            let vote_count = self.votes_received.len() + 1; // +1 for self-vote
87
88            if vote_count >= self.config.quorum() {
89                self.become_leader();
90            }
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use std::time::{Duration, Instant};
98
99    use crate::message::RequestVoteRequest;
100    use crate::node::core::RaftNode;
101    use crate::node::rpc::test_helpers::{observer_self_config, test_config};
102    use crate::state::NodeRole;
103    use crate::storage::MemStorage;
104
105    #[test]
106    fn vote_grant_and_reject() {
107        let config = test_config(1, vec![2, 3]);
108        let mut node = RaftNode::new(config, MemStorage::new());
109
110        let req = RequestVoteRequest {
111            term: 1,
112            candidate_id: 2,
113            last_log_index: 0,
114            last_log_term: 0,
115            group_id: 1,
116        };
117        let resp = node.handle_request_vote(&req);
118        assert!(resp.vote_granted);
119
120        let req2 = RequestVoteRequest {
121            term: 1,
122            candidate_id: 3,
123            last_log_index: 0,
124            last_log_term: 0,
125            group_id: 1,
126        };
127        let resp2 = node.handle_request_vote(&req2);
128        assert!(!resp2.vote_granted);
129    }
130
131    #[test]
132    fn learner_rejects_vote_request() {
133        let mut config = test_config(2, vec![1]);
134        config.starts_as_learner = true;
135        let mut node = RaftNode::new(config, MemStorage::new());
136        assert_eq!(node.role(), NodeRole::Learner);
137
138        let req = RequestVoteRequest {
139            term: 5,
140            candidate_id: 1,
141            last_log_index: 10,
142            last_log_term: 4,
143            group_id: 1,
144        };
145        let resp = node.handle_request_vote(&req);
146        assert!(
147            !resp.vote_granted,
148            "learner must never grant a vote, got {resp:?}"
149        );
150    }
151
152    #[test]
153    fn three_node_election() {
154        let config1 = test_config(1, vec![2, 3]);
155        let config2 = test_config(2, vec![1, 3]);
156        let config3 = test_config(3, vec![1, 2]);
157
158        let mut node1 = RaftNode::new(config1, MemStorage::new());
159        let mut node2 = RaftNode::new(config2, MemStorage::new());
160        let mut node3 = RaftNode::new(config3, MemStorage::new());
161
162        node1.election_deadline = Instant::now() - Duration::from_millis(1);
163        node1.tick();
164        assert_eq!(node1.role(), NodeRole::Candidate);
165
166        let ready = node1.take_ready();
167        assert_eq!(ready.vote_requests.len(), 2);
168
169        let resp2 = node2.handle_request_vote(&ready.vote_requests[0].1);
170        let resp3 = node3.handle_request_vote(&ready.vote_requests[1].1);
171        assert!(resp2.vote_granted);
172        assert!(resp3.vote_granted);
173
174        node1.handle_request_vote_response(2, &resp2);
175        assert_eq!(node1.role(), NodeRole::Leader);
176    }
177
178    /// Observer never votes even when it receives a RequestVote RPC.
179    #[test]
180    fn observer_self_never_grants_vote() {
181        let mut obs = RaftNode::new(observer_self_config(5), MemStorage::new());
182        assert_eq!(obs.role(), NodeRole::Observer);
183
184        let req = RequestVoteRequest {
185            term: 10,
186            candidate_id: 1,
187            last_log_index: 100,
188            last_log_term: 9,
189            group_id: 1,
190        };
191        let resp = obs.handle_request_vote(&req);
192        assert!(!resp.vote_granted, "observer must never grant a vote");
193        assert_eq!(obs.role(), NodeRole::Observer, "role must stay Observer");
194    }
195
196    /// Observer ticking past its election deadline must never start an election.
197    #[test]
198    fn observer_tick_does_not_start_election() {
199        let mut obs = RaftNode::new(observer_self_config(5), MemStorage::new());
200        obs.election_deadline = Instant::now() - Duration::from_millis(1);
201        obs.tick();
202        assert_eq!(obs.role(), NodeRole::Observer);
203        assert_eq!(obs.current_term(), 0);
204    }
205}