Skip to content
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

Fix cft election #1641

Merged
merged 40 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e6cf454
Do not truncate committable entries on election
achamayou Sep 21, 2020
c4458cf
Fix test
achamayou Sep 21, 2020
5b0cb0c
Fix test
achamayou Sep 22, 2020
6eaa15f
Fix test
achamayou Sep 22, 2020
a0eeecb
Merge branch 'master' into fix_cft_election
achamayou Sep 22, 2020
1bbd829
Fix test
achamayou Sep 22, 2020
e9bcbbb
Merge branch 'master' into fix_cft_election
achamayou Sep 23, 2020
5eb87d9
Use last committable index only in elections
achamayou Sep 23, 2020
c51ffa0
Update src/consensus/aft/raft.h
achamayou Sep 23, 2020
f72406f
Merge branch 'master' into fix_cft_election
achamayou Sep 24, 2020
87d85a0
Merge branch 'master' into fix_cft_election
achamayou Sep 24, 2020
50d3c81
rotation test
achamayou Sep 24, 2020
f5991d9
Merge branch 'fix_cft_election' of https://github.com/achamayou/CCF i…
achamayou Sep 24, 2020
e52bb00
rotation test
achamayou Sep 24, 2020
67545d1
No such thing as simplebank anymore
achamayou Sep 24, 2020
a946640
Remove now unnecessary barrier in recovery
achamayou Sep 24, 2020
d85c25e
rotation test
achamayou Sep 25, 2020
a567cb8
rotation test
achamayou Sep 25, 2020
835aac5
fix
achamayou Sep 25, 2020
2bf6d0f
Merge branch 'master' into fix_cft_election
achamayou Sep 25, 2020
6a4d3db
fix
achamayou Sep 25, 2020
d2aa627
Merge branch 'master' into fix_cft_election
achamayou Sep 28, 2020
a1a5e68
wip
achamayou Sep 28, 2020
6846905
fix
achamayou Sep 28, 2020
d401a2a
test committable suffix
achamayou Sep 29, 2020
ccc3b91
.
achamayou Sep 29, 2020
06c67f2
missing file
achamayou Sep 29, 2020
02faaf6
Merge branch 'master' into fix_cft_election
achamayou Sep 29, 2020
f3d5b0b
wait for commit
achamayou Sep 29, 2020
ba5c1f1
Merge branch 'fix_cft_election' of https://github.com/achamayou/CCF i…
achamayou Sep 29, 2020
97c5a72
comment
achamayou Sep 29, 2020
0ab0ec5
diff
achamayou Sep 29, 2020
b43cea6
Update tests/committable.py
achamayou Sep 29, 2020
e99cb7b
Merge branch 'master' into fix_cft_election
achamayou Sep 29, 2020
d005173
Merge branch 'master' into fix_cft_election
achamayou Sep 29, 2020
281795c
format
achamayou Sep 29, 2020
8d3d9bc
Merge branch 'master' into fix_cft_election
achamayou Sep 29, 2020
c9d5ba9
ignore stopped nodes in config check
achamayou Sep 30, 2020
5514fca
Merge branch 'fix_cft_election' of https://github.com/achamayou/CCF i…
achamayou Sep 30, 2020
07cbb0b
Merge branch 'master' into fix_cft_election
achamayou Sep 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .azure-pipelines-templates/daily-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
cmake_args: '${{ parameters.build.debug.cmake_args }} ${{ parameters.build.NoSGX.cmake_args }}'
suffix: 'Instrumented'
artifact_name: 'NoSGX_Instrumented'
ctest_filter: '-LE "benchmark|perf|long_test"'
ctest_filter: '-LE "benchmark|perf"'
ctest_timeout: '300'

- template: common.yml
Expand Down
4 changes: 2 additions & 2 deletions .azure-pipelines-templates/matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ parameters:

test:
NoSGX:
ctest_args: '-LE "benchmark|perf|tlstest|long_test"'
ctest_args: '-LE "benchmark|perf|tlstest"'
SGX:
ctest_args: '-LE "benchmark|perf|tlstest|long_test"'
ctest_args: '-LE "benchmark|perf|tlstest"'
perf:
ctest_args: '-L "benchmark|perf"'

Expand Down
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,20 @@ if(BUILD_TESTS)
4000
)

add_e2e_test(
NAME rotation_test
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/rotation.py
CONSENSUS cft
ADDITIONAL_ARGS --raft-election-timeout 4000
)

add_e2e_test(
NAME committable_suffix_test
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/committable.py
CONSENSUS cft
ADDITIONAL_ARGS --raft-election-timeout 4000
)

add_e2e_test(
NAME lua_e2e_batched
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/e2e_batched.py
Expand Down
2 changes: 1 addition & 1 deletion python/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
msgpack==1.0.0
loguru==0.5.2
loguru==0.5.3
requests==2.24.0
requests-http-signature==0.1.0
websocket-client==0.57.0
Expand Down
91 changes: 72 additions & 19 deletions src/consensus/aft/raft.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ namespace aft

ReplicaState replica_state;
std::chrono::milliseconds timeout_elapsed;
// Last (committable) index preceding the node's election, this is
// used to decide when to start issuing signatures. While commit_idx
// hasn't caught up with election_index, a newly elected leader is
// effectively finishing establishing commit over the previous term
// or even previous terms, and can therefore not meaningfully sign
// over the commit level.
kv::Version election_index = 0;
achamayou marked this conversation as resolved.
Show resolved Hide resolved

// BFT
RequestsMap& pbft_requests_map;
Expand Down Expand Up @@ -184,6 +191,12 @@ namespace aft
return replica_state == Follower;
}

Index last_committable_index() const
{
return committable_indices.empty() ? state->commit_idx :
committable_indices.back();
}

void enable_all_domains()
{
// When receiving append entries as a follower, all security domains will
Expand Down Expand Up @@ -281,6 +294,20 @@ namespace aft
return {get_term_internal(state->commit_idx), state->commit_idx};
}

std::optional<std::pair<Term, Index>> get_signable_commit_term_and_idx()
{
std::lock_guard<SpinLock> guard(state->lock);
if (state->commit_idx >= election_index)
{
return std::pair<Term, Index>{get_term_internal(state->commit_idx),
state->commit_idx};
}
else
{
return std::nullopt;
}
}

Term get_term(Index idx)
{
if (consensus_type == ConsensusType::BFT && is_follower())
Expand Down Expand Up @@ -596,11 +623,12 @@ namespace aft
}

LOG_DEBUG_FMT(
"Received pt: {} pi: {} t: {} i: {}",
"Received pt: {} pi: {} t: {} i: {} toi: {}",
r.prev_term,
r.prev_idx,
r.term,
r.idx);
r.idx,
r.term_of_idx);

// Don't check that the sender node ID is valid. Accept anything that
// passes the integrity check. This way, entries containing dynamic
Expand All @@ -621,7 +649,7 @@ namespace aft
else if (state->current_view > r.term)
{
// Reply false, since our term is later than the received term.
LOG_DEBUG_FMT(
LOG_INFO_FMT(
"Recv append entries to {} from {} but our term is later ({} > {})",
state->my_node_id,
r.from_node,
Expand Down Expand Up @@ -775,11 +803,18 @@ namespace aft
case kv::DeserialiseSuccess::PASS_SIGNATURE:
{
LOG_DEBUG_FMT("Deserialising signature at {}", i);
auto prev_lci = last_committable_index();
committable_indices.push_back(i);

if (sig_term)
{
state->view_history.update(state->commit_idx + 1, sig_term);
// A signature for sig_term tells us that all transactions from
// the previous signature onwards (at least, if not further back)
// happened in sig_term. We reflect this in the history.
if (r.term_of_idx == aft::ViewHistory::InvalidView)
state->view_history.update(1, r.term);
else
state->view_history.update(prev_lci + 1, sig_term);
commit_if_possible(r.leader_commit_idx);
}
if (consensus_type == ConsensusType::BFT)
Expand Down Expand Up @@ -808,7 +843,13 @@ namespace aft
// After entries have been deserialised, we try to commit the leader's
// commit index and update our term history accordingly
commit_if_possible(r.leader_commit_idx);
state->view_history.update(state->commit_idx + 1, r.term_of_idx);

// The term may have changed, and we have not have seen a signature yet.
auto lci = last_committable_index();
if (r.term_of_idx == aft::ViewHistory::InvalidView)
state->view_history.update(1, r.term);
else
state->view_history.update(lci + 1, r.term_of_idx);

send_append_entries_response(r.from_node, true);
}
Expand Down Expand Up @@ -1168,10 +1209,13 @@ namespace aft
{
LOG_INFO_FMT("Send request vote from {} to {}", state->my_node_id, to);

auto last_committable_idx = last_committable_index();
CCF_ASSERT(last_committable_idx >= state->commit_idx, "lci < ci");

RequestVote rv = {{raft_request_vote, state->my_node_id},
state->current_view,
state->commit_idx,
get_term_internal(state->commit_idx)};
last_committable_idx,
get_term_internal(last_committable_idx)};

channels->send_authenticated(ccf::NodeMsgType::consensus_msg, to, rv);
}
Expand Down Expand Up @@ -1238,12 +1282,17 @@ namespace aft
return;
}

// If the candidate's log is at least as up-to-date as ours, vote yes
auto last_commit_term = get_term_internal(state->commit_idx);
// If the candidate's committable log is at least as up-to-date as ours,
// vote yes

auto answer = (r.last_commit_term > last_commit_term) ||
((r.last_commit_term == last_commit_term) &&
(r.last_commit_idx >= state->commit_idx));
auto last_committable_idx = last_committable_index();
auto term_of_last_committable_index =
get_term_internal(last_committable_idx);

auto answer =
(r.term_of_last_committable_idx > term_of_last_committable_index) ||
((r.term_of_last_committable_idx == term_of_last_committable_index) &&
(r.last_committable_idx >= last_committable_idx));

if (answer)
{
Expand Down Expand Up @@ -1380,20 +1429,21 @@ namespace aft

void become_leader()
{
// Discard any un-committed updates we may hold,
election_index = last_committable_index();
LOG_DEBUG_FMT("Election index is {}", election_index);
// Discard any un-committable updates we may hold,
// since we have no signature for them. Except at startup,
// where we do not want to roll back the genesis transaction.
if (state->commit_idx)
{
rollback(state->commit_idx);
rollback(election_index);
}
else
{
// but we still want the KV to know which term we're in
store->set_term(state->current_view);
}

committable_indices.clear();
replica_state = Leader;
leader_id = state->my_node_id;

Expand Down Expand Up @@ -1433,9 +1483,7 @@ namespace aft
voted_for = NoNode;
votes_for_me.clear();

// Rollback unreplicated commits.
rollback(state->commit_idx);
committable_indices.clear();
rollback(last_committable_index());

LOG_INFO_FMT(
"Becoming follower {}: {}", state->my_node_id, state->current_view);
Expand Down Expand Up @@ -1512,6 +1560,11 @@ namespace aft

void commit_if_possible(Index idx)
{
LOG_DEBUG_FMT(
"Commit if possible {} (ci: {}) (ti {})",
idx,
state->commit_idx,
get_term_internal(idx));
if (
(idx > state->commit_idx) &&
(get_term_internal(idx) <= state->current_view))
Expand Down Expand Up @@ -1695,4 +1748,4 @@ namespace aft
}
}
};
}
}
5 changes: 5 additions & 0 deletions src/consensus/aft/raft_consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ namespace aft
return aft->get_commit_term_and_idx();
}

std::optional<std::pair<View, SeqNo>> get_signable_txid() override
{
return aft->get_signable_commit_term_and_idx();
}

View get_view(SeqNo seqno) override
{
return aft->get_term(seqno);
Expand Down
8 changes: 2 additions & 6 deletions src/consensus/aft/raft_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,8 @@ namespace aft
struct RequestVote : RaftHeader
{
Term term;
// last_log_idx in vanilla raft but last_commit_idx here to preserve
// verifiability
Index last_commit_idx;
// last_log_term in vanilla raft but last_commit_term here to preserve
// verifiability
Term last_commit_term;
Index last_committable_idx;
Term term_of_last_committable_idx;
};

struct RequestVoteResponse : RaftHeader
Expand Down
4 changes: 2 additions & 2 deletions src/consensus/aft/test/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ class RaftDriver
aft::NodeId node_id, aft::NodeId tgt_node_id, aft::RequestVote rv)
{
std::ostringstream s;
s << "request_vote t: " << rv.term << ", lli: " << rv.last_commit_idx
<< ", llt: " << rv.last_commit_term;
s << "request_vote t: " << rv.term << ", lci: " << rv.last_committable_idx
<< ", tolci: " << rv.term_of_last_committable_idx;
log(node_id, tgt_node_id, s.str());
}

Expand Down
Loading