Skip to content

Commit

Permalink
Joint Consensus (#101)
Browse files Browse the repository at this point in the history
* Enable Joint Consensus.

This commit enables Joint Consensus as described by the Raft paper
(https://raft.github.io/raft.pdf).

As of this commit it is possible to undergo arbitrary peer membership
changes in a safe way.

Notably, this gives us a resilient "Replace Node" functionality, which
is able to progress in the situation of a loss of both the old (removed)
and new (added) peers go down. This is not possible with our previous
one-at-a-time strategy.

Unfortunately, this feature is fairly large in scope. Thankfully it's
mostly testing code!

There is some new API surface, notably the
`Raft::begin_membership_change` function.

There is also some moved API surface. The `RaftLog::applied_to` function
has been moved to `Raft::commit_apply`. The old function, while it still
works, carries a depreaction warning. In the future we should make it a
`pub(crate)` function.

Finally, many tests were added to ensure Joint Consensus can work.

TODOs:
- Consider renaming `progress::Configuration` to `progress::Topology`.
- Test interaction between Joint Consensus and one-by-one method.
- Think of more cases we might need.
- Documentation update.

Signed-off-by: Hoverbear <operator@hoverbear.org>
  • Loading branch information
Hoverbear authored and hicqu committed Feb 14, 2019
1 parent 311b3b5 commit 096c000
Show file tree
Hide file tree
Showing 21 changed files with 3,406 additions and 411 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ quick-error = "1.2.2"
rand = "0.5.4"
hashbrown = "0.1"
fail = { version = "0.2", optional = true }
getset = "0.0.6"

[dev-dependencies]
env_logger = "0.5"
Expand Down
2 changes: 1 addition & 1 deletion benches/benches.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![allow(dead_code)] // Due to criterion we need this to avoid warnings.
#![cfg_attr(feature = "cargo-clippy", allow(let_and_return))] // Benches often artificially return values. Allow it.
#![cfg_attr(feature = "cargo-clippy", allow(clippy::let_and_return))] // Benches often artificially return values. Allow it.

extern crate criterion;
extern crate env_logger;
Expand Down
35 changes: 31 additions & 4 deletions benches/suites/progress_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ pub fn bench_progress_set(c: &mut Criterion) {
bench_progress_set_remove(c);
bench_progress_set_iter(c);
bench_progress_set_get(c);
bench_progress_set_nodes(c);
bench_progress_set_voters(c);
bench_progress_set_learners(c);
}

fn quick_progress_set(voters: usize, learners: usize) -> ProgressSet {
Expand Down Expand Up @@ -146,14 +147,40 @@ pub fn bench_progress_set_iter(c: &mut Criterion) {
});
}

pub fn bench_progress_set_nodes(c: &mut Criterion) {
pub fn bench_progress_set_voters(c: &mut Criterion) {
let bench = |voters, learners| {
move |b: &mut Bencher| {
let set = quick_progress_set(voters, learners);
b.iter(|| {
let set = set.clone();
let agg = set.iter().all(|_| true);
agg
let sum = set.voters().fold(0, |mut sum, _| {
sum += 1;
sum
});
sum
});
}
};

DEFAULT_RAFT_SETS.iter().for_each(|(voters, learners)| {
c.bench_function(
&format!("ProgressSet::nodes ({}, {})", voters, learners),
bench(*voters, *learners),
);
});
}

pub fn bench_progress_set_learners(c: &mut Criterion) {
let bench = |voters, learners| {
move |b: &mut Bencher| {
let set = quick_progress_set(voters, learners);
b.iter(|| {
let set = set.clone();
let sum = set.voters().fold(0, |mut sum, _| {
sum += 1;
sum
});
sum
});
}
};
Expand Down
4 changes: 2 additions & 2 deletions benches/suites/raft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ fn quick_raft(voters: usize, learners: usize) -> Raft<MemStorage> {
let config = Config::new(id);
let mut raft = Raft::new(&config, storage).unwrap();
(0..voters).for_each(|id| {
raft.add_node(id as u64);
raft.add_node(id as u64).unwrap();
});
(voters..learners).for_each(|id| {
raft.add_learner(id as u64);
raft.add_learner(id as u64).unwrap();
});
raft
}
Expand Down
3 changes: 1 addition & 2 deletions benches/suites/raw_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ fn quick_raw_node() -> RawNode<MemStorage> {
let peers = vec![];
let storage = MemStorage::default();
let config = Config::new(id);
let node = RawNode::new(&config, storage, peers).unwrap();
node
RawNode::new(&config, storage, peers).unwrap()
}

pub fn bench_raw_node_new(c: &mut Criterion) {
Expand Down
11 changes: 11 additions & 0 deletions proto/eraftpb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ message Entry {

message SnapshotMetadata {
ConfState conf_state = 1;
ConfState pending_membership_change = 4;
uint64 pending_membership_change_index = 5;
uint64 index = 2;
uint64 term = 3;
}
Expand Down Expand Up @@ -91,11 +93,20 @@ enum ConfChangeType {
AddNode = 0;
RemoveNode = 1;
AddLearnerNode = 2;
BeginMembershipChange = 3;
FinalizeMembershipChange = 4;
}

message ConfChange {
uint64 id = 1;
ConfChangeType change_type = 2;
// Used in `AddNode`, `RemoveNode`, and `AddLearnerNode`.
uint64 node_id = 3;
bytes context = 4;
// Used in `BeginMembershipChange` and `FinalizeMembershipChange`.
ConfState configuration = 5;
// Used in `BeginMembershipChange` and `FinalizeMembershipChange`.
// Because `RawNode::apply_conf_change` takes a `ConfChange` instead of an `Entry` we must
// include this index so it can be known.
uint64 start_index = 6;
}
Loading

0 comments on commit 096c000

Please sign in to comment.