diff --git a/src/main.rs b/src/main.rs index 2eb79d91ba7c4f7a848be89036db1cf3ad0bbe93..969ed8ce29bd47df87ff82363f98de6c2258f19e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -782,7 +782,7 @@ fn postjoin(args: &args::Run, storage: Storage) { node.tick_and_yield(1); // apply configuration, if any node.campaign_and_yield().ok(); // trigger election immediately - assert_eq!(node.status().raft_state, "Leader"); + assert!(node.status().raft_state.is_leader()); } box_cfg.listen = Some(args.listen.clone()); diff --git a/src/traft/node.rs b/src/traft/node.rs index 91eb9927c06bc78e8f424a375482047177baec5d..1d2eff44f3173b8a676a0fa4380236a5b81398b7 100644 --- a/src/traft/node.rs +++ b/src/traft/node.rs @@ -17,7 +17,6 @@ use ::tarantool::proc; use ::tarantool::tlua; use ::tarantool::transaction::start_transaction; use std::cell::Cell; -use std::cell::RefCell; use std::collections::HashMap; use std::collections::HashSet; use std::convert::TryFrom; @@ -66,14 +65,44 @@ use super::{CurrentGrade, TargetGrade}; type RawNode = raft::RawNode<RaftSpaceAccess>; -#[derive(Clone, Debug, tlua::Push, tlua::PushInto)] +crate::define_str_enum! { + pub enum RaftState { + Follower = "Follower", + Candidate = "Candidate", + Leader = "Leader", + PreCandidate = "PreCandidate", + } + FromStr::Err = UnknownRaftState; +} +#[derive(thiserror::Error, Debug)] +#[error("unknown raft state {0}")] +pub struct UnknownRaftState(pub String); + +impl RaftState { + pub fn is_leader(&self) -> bool { + matches!(self, Self::Leader) + } +} + +impl From<RaftStateRole> for RaftState { + fn from(role: RaftStateRole) -> Self { + match role { + RaftStateRole::Follower => Self::Follower, + RaftStateRole::Candidate => Self::Candidate, + RaftStateRole::Leader => Self::Leader, + RaftStateRole::PreCandidate => Self::PreCandidate, + } + } +} + +#[derive(Copy, Clone, Debug, tlua::Push, tlua::PushInto)] pub struct Status { /// `raft_id` of the current instance pub id: RaftId, /// `raft_id` of the leader instance pub leader_id: Option<RaftId>, - /// One of "Follower", "Candidate", "Leader", "PreCandidate" - pub raft_state: String, + /// Current raft state + pub raft_state: RaftState, /// Whether instance has finished its `postjoin` /// initialization stage pub is_ready: bool, @@ -93,7 +122,7 @@ pub struct Node { pub(crate) storage: Storage, main_loop: MainLoop, _conf_change_loop: fiber::UnitJoinHandle<'static>, - status: Rc<RefCell<Status>>, + status: Rc<Cell<Status>>, } impl std::fmt::Debug for Node { @@ -111,10 +140,10 @@ impl Node { let node_impl = NodeImpl::new(storage.clone())?; let raft_id = node_impl.raft_id(); - let status = Rc::new(RefCell::new(Status { + let status = Rc::new(Cell::new(Status { id: raft_id, leader_id: None, - raft_state: "Follower".into(), + raft_state: RaftState::Follower, is_ready: false, })); @@ -149,11 +178,13 @@ impl Node { } pub fn status(&self) -> Status { - self.status.borrow().clone() + self.status.get() } pub fn mark_as_ready(&self) { - self.status.borrow_mut().is_ready = true; + let mut status = self.status.get(); + status.is_ready = true; + self.status.set(status); event::broadcast(Event::StatusChanged); } @@ -696,12 +727,7 @@ impl NodeImpl { /// - or better <https://github.com/etcd-io/etcd/blob/v3.5.5/raft/node.go#L49> /// /// This function yields. - fn advance( - &mut self, - status: &RefCell<Status>, - topology_changed: &mut bool, - expelled: &mut bool, - ) { + fn advance(&mut self, status: &Cell<Status>, topology_changed: &mut bool, expelled: &mut bool) { // Get the `Ready` with `RawNode::ready` interface. if !self.raw_node.has_ready() { return; @@ -718,9 +744,10 @@ impl NodeImpl { } if let Some(ss) = ready.ss() { - let mut status = status.borrow_mut(); - status.leader_id = (ss.leader_id != INVALID_ID).then_some(ss.leader_id); - status.raft_state = format!("{:?}", ss.raft_state); + let mut s = status.get(); + s.leader_id = (ss.leader_id != INVALID_ID).then_some(ss.leader_id); + s.raft_state = ss.raft_state.into(); + status.set(s); event::broadcast(Event::StatusChanged); } @@ -799,7 +826,7 @@ struct MainLoop { } struct MainLoopArgs { - status: Rc<RefCell<Status>>, + status: Rc<Cell<Status>>, node_impl: Rc<Mutex<NodeImpl>>, } @@ -812,7 +839,7 @@ struct MainLoopState { impl MainLoop { pub const TICK: Duration = Duration::from_millis(100); - fn start(status: Rc<RefCell<Status>>, node_impl: Rc<Mutex<NodeImpl>>) -> Self { + fn start(status: Rc<Cell<Status>>, node_impl: Rc<Mutex<NodeImpl>>) -> Self { let loop_cond: Rc<Cond> = Default::default(); let stop_flag: Rc<Cell<bool>> = Default::default(); @@ -979,7 +1006,7 @@ fn raft_conf_change(storage: &RaftSpaceAccess, peers: &[Peer]) -> Option<raft::C Some(conf_change) } -fn raft_conf_change_loop(status: Rc<RefCell<Status>>, storage: Storage) { +fn raft_conf_change_loop(status: Rc<Cell<Status>>, storage: Storage) { let mut pool = ConnectionPool::builder(storage.peers.clone()) .call_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_millis(500)) @@ -987,7 +1014,7 @@ fn raft_conf_change_loop(status: Rc<RefCell<Status>>, storage: Storage) { .build(); loop { - if status.borrow().raft_state != "Leader" { + if !status.get().raft_state.is_leader() { event::wait(Event::StatusChanged).expect("Events system must be initialized"); continue; }