-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
raft: add learner and tests. #2491
Conversation
PTAL @overvenus |
src/raft/raft.rs
Outdated
@@ -443,7 +481,11 @@ impl<T: Storage> Raft<T> { | |||
} | |||
|
|||
fn prepare_send_snapshot(&mut self, m: &mut Message, to: u64) -> bool { | |||
let pr = self.prs.get_mut(&to).unwrap(); | |||
let mut pr = match self.prs.get_mut(&to) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not using get_mut_progress_by_id here?
src/raft/raft.rs
Outdated
@@ -498,12 +540,14 @@ impl<T: Storage> Raft<T> { | |||
} | |||
|
|||
fn prepare_send_entries(&mut self, m: &mut Message, to: u64, term: u64, ents: Vec<Entry>) { | |||
let pr = self.prs.get_mut(&to).unwrap(); | |||
let tag = self.tag.clone(); // TODO: recude clone. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why clone here?
src/raft/raft.rs
Outdated
.maybe_update(self.raft_log.last_index()); | ||
|
||
{ | ||
let pr = match self.prs.get_mut(&self.id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not use get_mut_progress_by_id?
src/raft/raft.rs
Outdated
@@ -308,8 +322,19 @@ impl<T: Storage> Raft<T> { | |||
skip_bcast_commit: c.skip_bcast_commit, | |||
tag: c.tag.to_owned(), | |||
}; | |||
for p in peers { | |||
r.prs.insert(*p, new_progress(1, r.max_inflight)); | |||
for &p in peers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer dereferencing to taking references.
src/raft/raft.rs
Outdated
self.prs.get(&id).or_else(|| self.learner_prs.get(&id)) | ||
} | ||
|
||
fn get_mut_progress_by_id(&mut self, id: u64) -> Option<&mut Progress> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by_id
is redundant.
src/raft/raft.rs
Outdated
@@ -182,9 +188,12 @@ pub struct Raft<T: Storage> { | |||
pub max_inflight: usize, | |||
pub max_msg_size: u64, | |||
pub prs: FlatMap<u64, Progress>, | |||
pub learner_prs: FlatMap<u64, Progress>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems learner_prs
and prs
are almost always handled together, why not put them into a struct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xiang90 suggests separating them. Let's keep the same with etcd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not suggesting to unify them, but use a struct Prs { prs: map, leaner_prs: map }
to manage them all. It's easier to deal with ownership in that way.
@BusyJay @siddontang PTAL. |
src/raft/raft.rs
Outdated
@@ -1836,21 +2003,22 @@ impl<T: Storage> Raft<T> { | |||
// false. | |||
// check_quorum_active also resets all recent_active to false. | |||
fn check_quorum_active(&mut self) -> bool { | |||
let mut act = 0; | |||
// NOTE: Here is a little different from etcd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In etcd, self is always active even if it's a learner. But I think it's always active only if it's a voter but not learner. Is it correct?
See: https://github.com/coreos/etcd/blob/master/raft/raft.go#L1375
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But this function can only be called if the peer is a leader.
src/raft/raft.rs
Outdated
// TODO: avoid copy | ||
let ids: Vec<_> = self.prs.keys().cloned().collect(); | ||
for id in ids { | ||
for id in self.nodes() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why use nodes but not mem replace here?
src/raft/raft.rs
Outdated
for id in ids { | ||
if id == self.id { | ||
continue; | ||
let prs = mem::replace(&mut self.prs, ProgressSet::default()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a bad idea. What if self.prs
is queried before it's reset? It's hard to write correct code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about write code like this:
struct Raft {
prs: ProgressSet,
raft: _Raft, // _Raft is a struct contains all other fields
}
So all method of _Raft
need pass a &ProgressSet
or &mut ProgressSet
in.
src/raft/raft.rs
Outdated
if id == self.id { | ||
continue; | ||
let prs = self.take_prs(); | ||
for (id, pr) in prs.voters.iter().chain(&prs.learners) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not using chain + filter + for_each as above does?
src/raft/raft.rs
Outdated
if id == self.id { | ||
continue; | ||
let prs = self.take_prs(); | ||
for (id, pr) in prs.voters.iter().chain(&prs.learners) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not using chain + filter + for_each as above does?
PTAL @overvenus |
let raft_log = RaftLog::new(store, c.tag.clone()); | ||
let mut peers: &[u64] = &c.peers; | ||
if !rs.conf_state.get_nodes().is_empty() { | ||
if !peers.is_empty() { | ||
let mut learners: &[u64] = &c.learners; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need type declaration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we need.
src/raft/raft.rs
Outdated
// TODO: the peers argument is always nil except in | ||
// tests; the argument should be removed and these tests should be | ||
// updated to specify their nodes through a snap | ||
panic!( | ||
"{} cannot specify both new(peers) and ConfState.Nodes", | ||
"{} cannot specify both new(peers/learners) and ConfState.Nodes", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about "... and ConfState.(Nodes/Learners)",
?
Just keep consistent with etcds[0].
src/raft/raft.rs
Outdated
@@ -181,10 +187,12 @@ pub struct Raft<T: Storage> { | |||
|
|||
pub max_inflight: usize, | |||
pub max_msg_size: u64, | |||
pub prs: FlatMap<u64, Progress>, | |||
prs: Option<ProgressSet>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When does prs
become None
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for bcast to avoid cloning ID
src/raft/raft.rs
Outdated
if self.is_learner { | ||
// TODO: learner may need to vote, in case of node down when confchange. | ||
info!( | ||
r#"{} [logterm: {}, index: {}, vote: {}] ignored {:?} from {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use \
for breaking a long string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't pass rustfmt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? See L1774.
src/raft/raft.rs
Outdated
@@ -1292,10 +1407,19 @@ impl<T: Storage> Raft<T> { | |||
); | |||
} | |||
MessageType::MsgTransferLeader => { | |||
self.handle_transfer_leader(m); | |||
let pr = prs.get_mut_progress(m.get_from()).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could from
be 0?
CC @BusyJay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can not unless we meet a serious bug.
src/raft/raft.rs
Outdated
} | ||
|
||
pub fn add_node(&mut self, id: u64) { | ||
fn add_node_or_learner(&mut self, id: u64, is_learner: bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the "node" is a voter, so I suggest rename it to add_voter_or_learner
or add_node_or_learner_node
.
src/raft/raft.rs
Outdated
.iter_mut() | ||
.chain(prs.learners.iter_mut()) | ||
.filter(|&(id, _)| *id != self_id) | ||
.for_each(|(id, ref mut pr)| { self.send_append(*id, pr); }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, for loop is straightforward and easier to understand.
src/raft/progress.rs
Outdated
pub learners: FlatMap<u64, Progress>, | ||
} | ||
|
||
impl ProgressSet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you provide a method like fn for_each<F: FnMut(u64, &mut Progress, bool)>(&mut self, func: F)
?
So that we do not need to write something like
voter.iter().for_each(do_something);
leader.iter().for_each(do_something);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thinks we don't need that. If we want to apply same closure on voters
and learners
, we can call voters.iter().chain(&learners).for_each(...)
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you split this pr into two: change prs to Option
and change prs to ProgressSet
.
src/raft/progress.rs
Outdated
progress | ||
} | ||
|
||
pub fn check_intersect(&self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is redundant if it's checked before inserting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now we didn't check before inserting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we check it when we insert every node, we need to write the logic twice, once in Raft::new
, and once in set_progress
.
If we check it before inserting with voters
and learners
as two Vec
, the check will be O(n^2).
So I think call check_intersect
after the ProgressSet
is stable is better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to write the logic twice, once in Raft::new, and once in set_progress.
Why? What I mean is to provide an insert
method and check in it.
If we check it before inserting with voters and learners as two Vec, the check will be O(n^2).
How? I think it should be O(n), where n is total count of nodes and learners. Besides, usually n <= 5, so the time complexity doesn't matter here.
When define a seperate check_intersect
method, it requires developer to remember to invoke it every time ProgressSet
is updated, which is very easy to make error.
src/raft/raft.rs
Outdated
} | ||
|
||
pub fn del_progress(&mut self, id: u64) { | ||
self.mut_prs().voters.remove(&id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should check if return value is None
.
src/raft/progress.rs
Outdated
learners: FlatMap<u64, Progress>, | ||
} | ||
|
||
impl<'a> ProgressSet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declare it in method signature instead.
src/raft/raft.rs
Outdated
// self is always active | ||
act += 1; | ||
continue; | ||
let act = self.mut_prs().iter_mut_voters().fold(0, |act, (&id, pr)| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use iter_mut
, and check if is_learner
to decide whether to update act
.
src/raft/progress.rs
Outdated
} | ||
} | ||
|
||
pub fn delete(&mut self, id: u64) -> Option<Progress> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/delete/remove/
src/raft/progress.rs
Outdated
if self.learners.contains_key(&id) { | ||
panic!("insert voter {} but already in learners", id); | ||
} | ||
match self.voters.entry(id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not check self.voters.insert(id, pr).is_some()
instead? Besides it's very strange that insertion of a collection returns the element being inserted.
/// learners contains the IDs of all learner nodes (maybe include self if | ||
/// the local node is a learner) in the raft cluster. | ||
/// learners only receives entries from the leader node. It does not vote | ||
/// or promote itself. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean by not vote or promote itself
? Does it mean promote_learner
should never be called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ported from etcd. I think it means Learner
can't make a proposal to promote itself to Voter
. Only Voter
s can propose to promote a Learner
to Voter
. I will make the comment more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this means the learner can't vote for other peers or vote for itself.
src/raft/raft.rs
Outdated
if self.is_learner { | ||
// TODO: learner may need to vote, in case of node down when confchange. | ||
info!( | ||
r#"{} [logterm: {}, index: {}, vote: {}] ignored {:?} from {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? See L1774.
src/raft/raft.rs
Outdated
@@ -1177,6 +1217,11 @@ impl<T: Storage> Raft<T> { | |||
} | |||
|
|||
fn handle_transfer_leader(&mut self, m: &Message, pr: &mut Progress) { | |||
if self.is_learner { | |||
debug!("{} is learner. Ignored transferring leadership", self.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use self.tag
instead.
src/raft/raft.rs
Outdated
// Ignore any redundant addNode calls (which can happen because the | ||
// initial bootstrapping entries are applied twice). | ||
if self.get_prs().voters().contains_key(&id) { | ||
// If is_learner is true, we don't support change Voter to Learner, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a log for the first case.
src/raft/raft.rs
Outdated
// New progress. | ||
let mut pr = new_progress(self.raft_log.last_index() + 1, self.max_inflight); | ||
pr.is_learner = is_learner; | ||
pr.recent_active = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why set it to true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's ported from etcd.
@@ -1802,29 +1885,19 @@ impl<T: Storage> Raft<T> { | |||
self.pending_conf = false; | |||
} | |||
|
|||
pub fn set_progress(&mut self, id: u64, matched: u64, next_idx: u64) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you keep the method and add a new arg is_learner
? Looks like there are a lot of places using the same pattern.
src/raft/progress.rs
Outdated
} | ||
|
||
#[allow(needless_lifetimes)] | ||
pub fn iter_voters<'a>(&'a self) -> impl Iterator<Item = (&'a u64, &'a Progress)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method is not used anymore, can be removed.
src/raft/progress.rs
Outdated
} | ||
|
||
#[allow(needless_lifetimes)] | ||
pub fn iter_mut_voters<'a>(&'a mut self) -> impl Iterator<Item = (&'a u64, &'a mut Progress)> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
src/raft/progress.rs
Outdated
} | ||
|
||
#[allow(needless_lifetimes)] | ||
pub fn iter_mut_learners<'a>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
src/raft/raft.rs
Outdated
info!( | ||
"{} [logterm: {}, index: {}, vote: {}] ignored {:?} from {} \ | ||
[logterm: {}, index: {}] at term {}: learner can not vote", | ||
self.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use self.tag
instead.
src/raft/raft.rs
Outdated
if id == self.id { | ||
error!( | ||
"{} can't become learner when restores snapshot [index: {}, term: {}]", | ||
self.id, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
src/raft/raft.rs
Outdated
@@ -1711,6 +1752,20 @@ impl<T: Storage> Raft<T> { | |||
return Some(false); | |||
} | |||
|
|||
if !self.is_learner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is going to work. When a raft node is created, it's not learner by default. So there is no way for a new learner to accept snapshot. So should either set different flag for learner messages or also check if the state machine is initialized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can pass learners info in Config
? So the enw learner can set self.is_learner = true
in Raft::new
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a raft state machine is created via conf change, there is no way to tell if it's a learner or a voter in current implementations.
src/raft/raft.rs
Outdated
return; | ||
} | ||
|
||
if !is_learner && self.mut_prs().promote_learner(id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If promotion succeeds, does its active flag need to be set?
src/raft/raft.rs
Outdated
let last_index = self.raft_log.last_index(); | ||
self.set_progress(id, 0, last_index + 1); | ||
self.set_progress(id, 0, last_index + 1, is_learner); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be ignored if is_learner is true, and self.get_prs().learners().contains_key(&id)
?
src/raft/raft.rs
Outdated
} | ||
|
||
pub fn add_node(&mut self, id: u64) { | ||
fn add_voter_or_learner(&mut self, id: u64, is_learner: bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you keep the logic in this function be same as etcd? The rewrite have introduced several problems already.
src/raft/raft.rs
Outdated
self.prs = Some(prs); | ||
} | ||
|
||
pub fn get_prs(&self) -> &FlatMap<u64, Progress> { | ||
pub fn get_prs(&self) -> &ProgressSet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/get_prs/prs/
@BusyJay PTAL. |
tests/raft/test_raft.rs
Outdated
@@ -1427,7 +1433,11 @@ fn test_commit() { | |||
|
|||
let mut sm = new_test_raft(1, vec![1], 5, 1, store); | |||
for (j, &v) in matches.iter().enumerate() { | |||
sm.set_progress(j as u64 + 1, v, v + 1); | |||
let pr = new_progress(ProgressState::default(), v, v + 1, 0, sm.max_inflight); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use set_progress
?
src/raft/progress.rs
Outdated
} | ||
|
||
pub fn promote_learner(&mut self, id: u64) { | ||
match self.learners.entry(id) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use remove
?
tests/raft/test_raft.rs
Outdated
prs.insert( | ||
*id, | ||
Progress { | ||
if prs.learners().get(id).is_some() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contains_key
?
tests/raft/test_raft.rs
Outdated
@@ -584,16 +589,17 @@ fn test_progress_resume_by_heartbeat_resp() { | |||
let mut raft = new_test_raft(1, vec![1, 2], 5, 1, new_storage()); | |||
raft.become_candidate(); | |||
raft.become_leader(); | |||
raft.mut_prs().get_mut(&2).unwrap().paused = true; | |||
raft.mut_prs().get_mut(2).unwrap().paused = true; | |||
raft.mut_prs().get_mut(2).unwrap().paused = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why set twice?
tests/raft/test_raft.rs
Outdated
} | ||
|
||
#[test] | ||
fn test_learner_election_timeout() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please keep the comment for the test case.
// Promote n2 from learner to follower. | ||
network.peers.get_mut(&1).unwrap().add_node(2); | ||
network.peers.get_mut(&2).unwrap().add_node(2); | ||
assert_eq!(network.peers[&2].state, StateRole::Follower); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should check if it's learner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In etcd it is
if n2.isLearner {
t.Error("peer 2 is learner, want not")
}
I think they are same because n2 must not be leader?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if there is no bug. Unfortunately you can't assume there is no bug in tests.
tests/raft/test_raft.rs
Outdated
n2.set_randomized_election_timeout(timeout); | ||
|
||
for _ in 0..timeout { | ||
n1.tick(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's meaningless to tick when it's not in a network.
tests/raft/test_raft.rs
Outdated
|
||
let timeout = n1.get_election_timeout(); | ||
n1.set_randomized_election_timeout(timeout); | ||
n2.set_randomized_election_timeout(timeout); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
tests/raft/test_raft.rs
Outdated
network.send(vec![msg]); | ||
|
||
assert_eq!( | ||
network.peers.get_mut(&2).unwrap().raft_log.committed, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should check node 1 too.
tests/raft/test_raft.rs
Outdated
|
||
#[test] | ||
fn test_restore_with_learner() { | ||
let mut s = Snapshot::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use new_snapshot
?
@BusyJay PTAL thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are still several places can be replaced by new_snapshot
and new_message
. Also please keep the comments of all ported test cases.
src/raft/raft.rs
Outdated
/// | ||
/// The flag will be true if all of `Config.peers` and `Config.learners` | ||
/// and `peers/learners` from `PeerStorage` are empty. | ||
initialized: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just check self.peers
and self.learners
at L1769?
src/raft/raft.rs
Outdated
@@ -1711,6 +1766,20 @@ impl<T: Storage> Raft<T> { | |||
return Some(false); | |||
} | |||
|
|||
if self.initialized && !self.is_learner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't behave the same as Etcd now. I think it's reasonable but not sure if it's a good way. Another way is to add an additional flag to the messages sent to learners. Any ideas? For the reason of the changes see discussion at #2491 (comment).
/cc @siddontang @xiang90
@BusyJay PTAL thanks. |
LGTM |
@siddontang PTAL again, thanks. |
/run-all-tests |
PTAL @overvenus |
progress | ||
} | ||
|
||
// We need explicit lifetime here because of a compiler bug. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add the reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This PR ports raft learner from etcd.