Skip to content
This repository has been archived by the owner on Mar 4, 2024. It is now read-only.

Fix possible split brain and consequent crash due to assertion failure #429

Merged
merged 2 commits into from
Jun 7, 2023
Merged

Fix possible split brain and consequent crash due to assertion failure #429

merged 2 commits into from
Jun 7, 2023

Conversation

freeekanayaka
Copy link
Contributor

Fixes #386. Please see the individual commit messages and code comments for a detailed explanation.

This test currently fails because the code that sends RequestVote messages uses
information about the in-memory log cache, not about the persisted log.

Signed-off-by: Free Ekanayaka <free@ekanayaka.io>
At the moment we correctly apply pending configurations only once they are
persisted. However, we don't do the same when running an election, where we use
not-yet-persisted log indexes, leading to incosistencies and violation of Raft
invariants.

This commits makes RequestVote messages include the index/term of the last
persisted entry, not the one in the in-memory log cache.

Signed-off-by: Free Ekanayaka <free@ekanayaka.io>
@codecov
Copy link

codecov bot commented Jun 7, 2023

Codecov Report

Merging #429 (a54636b) into master (628a743) will increase coverage by 0.08%.
The diff coverage is 50.00%.

@@            Coverage Diff             @@
##           master     #429      +/-   ##
==========================================
+ Coverage   76.62%   76.71%   +0.08%     
==========================================
  Files          51       51              
  Lines        9597     9597              
  Branches     2444     2443       -1     
==========================================
+ Hits         7354     7362       +8     
+ Misses       1171     1161      -10     
- Partials     1072     1074       +2     
Impacted Files Coverage Δ
src/election.c 79.08% <50.00%> (+0.65%) ⬆️

... and 4 files with indirect coverage changes

Copy link
Contributor

@cole-miller cole-miller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I agree that this change is needed, since the formal description of Raft views the log as always being stored persistently, and our in-memory log is just an optimization against that that should never affect the core protocol implementation.

Comment on lines +878 to +879
/* Server 0 is still leader, since it can contact server 3. */
munit_assert_int(CLUSTER_STATE(0), ==, RAFT_LEADER);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a server 4 implicit in all this, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure what you mean, the test has 4 servers, indexed from 0 to 3, with IDs from 1 to 4 (the off-by-one difference between indexes and IDs is a unfortunate, I'd like to fix that eventually, but it's the current convention).

Copy link
Contributor

@cole-miller cole-miller Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought clusters with an even number of servers weren't allowed? Okay, I was confused because I'm not used to seeing cluster with an even number of servers, I forgot that one of them is not a voter and so two votes is enough to win an election.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can have any number of servers, with any role combination you wish. An even number of (voting) servers is not recommended because having 4 servers (all voters) doesn't make the cluster more "available" than having 3, since with 4 servers to have a majority you need 3 votes, so you can afford to lose at most 1 server, exactly like when you have 3 servers.

Note that the test sets up 4 servers, but only 3 of them are meant to be voting at any given time (the test mimics automatic roles management in that regard, by demoting an offline voter node and promoting a standby to replace it).

@freeekanayaka
Copy link
Contributor Author

Thanks! I agree that this change is needed, since the formal description of Raft views the log as always being stored persistently, and our in-memory log is just an optimization against that that should never affect the core protocol implementation.

Right, the in-memory log is basically a cache (in the sense that it saves a disk round-trip when a leader needs to fetch entries in order to send them to followers) and a buffer (in the sense that it's where we store entries that we receive, waiting for them to be asynchronously persisted on disk).

@MathieuBordere MathieuBordere merged commit 1f96946 into canonical:master Jun 7, 2023
18 of 19 checks passed
@freeekanayaka freeekanayaka deleted the fix-assert-follower branch June 7, 2023 13:11
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

recvAppendEntries: Assertion r->state == RAFT_FOLLOWER || r->state == RAFT_CANDIDATE failed
3 participants