Skip to content

Commit

Permalink
All time-based transitions implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
tailhook committed Nov 2, 2015
1 parent 89d0265 commit edcdab7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/elect/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub enum Action {
PingAll,
Vote(Id),
ConfirmVote(Id),
Pong,
PingNew,
}

Expand Down
19 changes: 15 additions & 4 deletions src/elect/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,20 @@ impl Machine {
info!("[{}] Time passed. Starting new election", info.id);
start_election(epoch+1, now, &info.id)
},
Voted { .. } => unimplemented!(),
Leader { .. } => unimplemented!(),
Follower { .. } => unimplemented!(),
Voted { epoch, .. } => {
info!("[{}] Time passed. Elect me please", info.id);
start_election(epoch+1, now, &info.id)
}
me @ Leader { .. } => {
let next_ping = now +
Duration::milliseconds(HEARTBEAT_INTERVAL);
(me,
Action::PingAll.and_wait(next_ping))
}
Follower { epoch, .. } => {
info!("[{}] Leader is unresponsive. Elect me please", info.id);
start_election(epoch+1, now, &info.id)
}
};
return (machine, action)
}
Expand Down Expand Up @@ -161,7 +172,7 @@ impl Machine {
fn follow(epoch: Epoch, now: SteadyTime) -> (Machine, ActionList) {
let dline = now + election_ivl();
(Machine::Follower { epoch: epoch, leader_deadline: dline },
Action::wait(dline))
Action::Pong.and_wait(dline))
}

fn pass(me: Machine) -> (Machine, ActionList) {
Expand Down
64 changes: 63 additions & 1 deletion src/elect/test_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ fn test_vote_approved() {

let (node, act) = node.message(&info,
(two.clone(), 1, Message::Vote(id.clone())), env.now());
println!("Node {:?}", node);
assert!(matches!(node, Machine::Leader { .. }));
assert!(act.action == Some(Action::PingAll));
}
Expand All @@ -87,3 +86,66 @@ fn test_election_expired() {
assert!(matches!(node, Machine::Electing { epoch: 2, .. }));
assert!(act.action == Some(Action::Vote(id.clone())));
}

#[test]
fn test_voted_timeout() {
let mut env = Environ::new();
let mut info = Info::new("one");
let node = Machine::new(env.now());
let id = info.id.clone();
assert!(matches!(node, Machine::Starting { .. }));

env.tick();
let two = env.add_another_for(&mut info);
let (node, act) = node.message(&info,
(two.clone(), 1, Message::Vote(two.clone())), env.now());
assert!(act.action == Some(Action::ConfirmVote(two.clone())));
assert!(matches!(node, Machine::Voted { .. }));

env.sleep(3000); // Large timeout, should be enough for new election
let (node, act) = node.time_passed(&info, env.now());
assert!(matches!(node, Machine::Electing { epoch: 2, .. }));
}

#[test]
fn test_leader_timeout() {
// this block is same as in test_alone (optimize
let mut env = Environ::new();
let info = Info::new("one");
let node = Machine::new(env.now());
assert!(matches!(node, Machine::Starting { .. }));
env.sleep(100); // Small time, just continue starting
let (node, act) = node.time_passed(&info, env.now());
assert!(matches!(node, Machine::Starting { .. }));
assert!(act.action == None);
env.sleep(10000); // Large timeout, should already become a leader
let (node, act) = node.time_passed(&info, env.now());
assert!(matches!(node, Machine::Leader { .. }));
assert!(act.action == Some(Action::PingAll));
// end of copy'n'paste

env.sleep(3000); // Large timeout, should make a ping
let (node, act) = node.time_passed(&info, env.now());
assert!(matches!(node, Machine::Leader { .. }));
assert!(act.action == Some(Action::PingAll));
}

#[test]
fn test_follower_timeout() {
let mut env = Environ::new();
let mut info = Info::new("one");
let id = info.id.clone();
let node = Machine::new(env.now());
assert!(matches!(node, Machine::Starting { .. }));
env.tick();
let two = env.add_another_for(&mut info);
let (node, act) = node.message(&info,
(two.clone(), 1, Message::Ping), env.now());
assert!(matches!(node, Machine::Follower { .. }));
assert!(act.action == Some(Action::Pong));

env.sleep(3000); // Large timeout, should start new election
let (node, act) = node.time_passed(&info, env.now());
assert!(matches!(node, Machine::Electing { .. }));
assert!(act.action == Some(Action::Vote(id.clone())));
}
5 changes: 5 additions & 0 deletions src/elect/test_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ impl Environ {
self.now = self.now + Duration::milliseconds(ms);
self.tspec = self.tspec + Duration::milliseconds(ms);
}
/// A single tick in mio is 100ms AFAIK. This is convenience method
/// to have some time passed
pub fn tick(&mut self) {
self.sleep(100)
}
pub fn now(&self) -> SteadyTime {
self.now
}
Expand Down

0 comments on commit edcdab7

Please sign in to comment.