use gloo::timers::callback::Timeout;
use serde::{Deserialize, Serialize};
use std::{collections::HashSet, sync::Arc};
use crate::{
rpc::{Message, Recipient},
NodeState,
};
use super::Node;
#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub enum Role {
Follower,
Candidate,
Leader,
}
impl std::fmt::Display for Role {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Role::Follower => write!(f, "Follower"),
Role::Candidate => write!(f, "Candidate"),
Role::Leader => write!(f, "Leader"),
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Peer(u32);
impl Peer {
pub fn id(&self) -> u32 {
self.0
}
}
impl<T> Node<T>
where
T: serde::ser::Serialize + serde::de::DeserializeOwned + 'static,
{
pub fn peer(&self) -> Peer {
Peer(self.id)
}
pub fn is(&self, peer: &Peer) -> bool {
peer.id() == self.id
}
pub(crate) fn add_peer(&self, peer: Peer) {
let mut state = self.state.lock().expect("poisoned mutex!");
state.peers.insert(peer);
if state.role == Role::Leader {
self.send(Message::PeerSet(state.peers.clone()), Recipient::Everyone)
}
}
pub(crate) fn remove_peer(&self, peer: Peer) {
let mut state = self.state.lock().expect("poisoned mutex!");
state.peers.remove(&peer);
}
pub(crate) fn reconcile_peers(&self, peers: HashSet<Peer>) {
let mut state = self.state.lock().expect("poisoned mutex!");
state.peers = peers;
}
pub(crate) fn new_election_task(self: Arc<Self>) -> Timeout {
Timeout::new(self.election_timeout_ms, || self.start_election())
}
fn start_election(self: Arc<Self>) {
let mut state = self.state.lock().expect("poisoned mutex!");
match state.peers.len() {
1 => {
drop(state);
self.clone().win_election();
}
2 => {
let mut iter = state.peers.iter();
let lower = std::cmp::min(iter.next().unwrap(), iter.next().unwrap());
if self.peer().eq(lower) {
drop(state);
self.clone().win_election();
}
}
_ => {
let candidate = self.peer();
state.role = Role::Candidate;
state.term += 1;
state.votes.insert(self.peer());
state.voted_for = Some(candidate);
state.election_task = Some(self.clone().new_election_task());
self.call_on_role_change(Role::Candidate);
self.send(
Message::VoteRequest {
term: state.term,
candidate,
},
Recipient::Everyone,
);
}
}
}
pub(crate) fn receive_vote(self: Arc<Self>, term: u32, follower: Peer) {
let mut state = self.state.lock().expect("poisoned mutex!");
if state.role != Role::Candidate {
return;
}
match term.cmp(&state.term) {
std::cmp::Ordering::Greater => {}
std::cmp::Ordering::Equal => {}
std::cmp::Ordering::Less => return,
}
state.votes.insert(follower);
if state.votes.len() > (state.peers.len() / 2) {
drop(state);
self.win_election()
}
}
fn win_election(self: Arc<Self>) {
{
let mut state = self.state.lock().expect("poisoned mutex!");
state.role = Role::Leader;
state.voted_for = None;
state.votes.clear();
state.election_task = None;
self.call_on_role_change(Role::Leader);
}
self.send_heartbeat();
}
pub(crate) fn receive_vote_request(self: Arc<Self>, term: u32, candidate: Peer) {
let mut state = self.state.lock().expect("poisoned mutex!");
if self.peer() == candidate {
return;
}
match term.cmp(&state.term) {
std::cmp::Ordering::Less => return,
std::cmp::Ordering::Equal => (),
std::cmp::Ordering::Greater => {
state.term = term;
state.voted_for = None;
state.votes.clear();
}
}
if state.role == Role::Follower && state.voted_for.is_none() {
state.voted_for = Some(candidate);
self.send(
Message::VoteResponse {
term,
candidate,
follower: Peer(self.id),
},
Recipient::Peer(candidate),
);
}
state.replace_election_task(Some(self.clone().new_election_task()));
}
pub(crate) fn new_heartbeat_task(self: Arc<Self>) -> Timeout {
Timeout::new(self.heartbeat_timeout_ms, || self.send_heartbeat())
}
fn send_heartbeat(self: Arc<Self>) {
let mut state = self.state.lock().expect("poisoned mutex!");
self.send(Message::Heartbeat { term: state.term }, Recipient::Everyone);
state.replace_heartbeat_task(Some(self.clone().new_heartbeat_task()));
}
pub(crate) fn receive_hearbeat(self: Arc<Self>, term: u32, _leader: &Peer) {
let mut state = self.state.lock().expect("poisoned mutex!");
match state.role {
Role::Leader => return,
Role::Candidate => {
if term >= state.term {
state.role = Role::Follower;
state.voted_for = None;
state.votes.clear();
self.call_on_role_change(Role::Follower);
}
}
Role::Follower => {
if term > state.term {
state.term = term;
state.voted_for = None;
}
}
}
state.replace_election_task(Some(self.clone().new_election_task()));
}
}
impl<T> Drop for Node<T>
where
T: serde::ser::Serialize + serde::de::DeserializeOwned + 'static,
{
fn drop(&mut self) {
self.stop();
self.send(Message::PeerRemoved, Recipient::Everyone) }
}
impl NodeState {
pub(crate) fn replace_election_task(&mut self, new_task: Option<Timeout>) {
if let Some(old_task) = if let Some(new_task) = new_task {
self.election_task.replace(new_task)
} else {
self.election_task.take()
} {
old_task.cancel();
}
}
pub(crate) fn replace_heartbeat_task(&mut self, new_task: Option<Timeout>) {
if let Some(old_task) = if let Some(new_task) = new_task {
self.heartbeat_task.replace(new_task)
} else {
self.heartbeat_task.take()
} {
old_task.cancel();
}
}
}