Skip to content

Commit 782191d

Browse files
committed
ovsdb: raft: Actually suppress the disruptive server.
Section 4.2.3 "Disruptive Servers" says: ...if a server receives a RequestVote request within the minimum election timeout of hearing from a current leader, it does not update its term or grant its vote. It can either drop the request, reply with a vote denial, or delay the request; the result is essentially the same... However, while logic in the code does make it skip the term update, it still proceeds to reply with the vote, because when the suppression check returns true, we'll not enter the 'if' block and will proceed to the RPC 'switch' instead. It will reply with a vote for the candidate it already voted for on this term for an actual vote and it will vote for the requester on the pre-vote. This effectively bypasses the pre-vote scheme as the disruptive server will win the pre-vote and proceed to the regular election. And while the disruptor will not win the actual vote, it has a term +1 and will reply with a "stale term" to the next append request, forcing the current leader to step down. Fix that by actually not responding to the disruptive vote requests, i.e. by taking the "drop the request" route. The test is updated with a new failure model where vote requests are actually getting sent out. There is a value in testing the full RPC stop as well, so keeping the current checks as they are. Also removing the duplicate term check in the vote reply handler, as it is now clear that the term check will not be skipped. Fixes: 1b1d2e6 ("ovsdb: Introduce experimental support for clustered databases.") Acked-by: Han Zhou <hzhou@ovn.org> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
1 parent 03cd347 commit 782191d

File tree

2 files changed

+25
-7
lines changed

2 files changed

+25
-7
lines changed

ovsdb/raft.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,10 +3935,6 @@ static void
39353935
raft_handle_vote_reply(struct raft *raft,
39363936
const struct raft_vote_reply *rpy)
39373937
{
3938-
if (!raft_receive_term__(raft, &rpy->common, rpy->term)) {
3939-
return;
3940-
}
3941-
39423938
if (raft->role != RAFT_CANDIDATE) {
39433939
return;
39443940
}
@@ -4700,10 +4696,12 @@ raft_handle_rpc(struct raft *raft, const union raft_rpc *rpc)
47004696
s->last_msg_ts = time_msec();
47014697
}
47024698

4699+
if (raft_should_suppress_disruptive_server(raft, rpc)) {
4700+
return;
4701+
}
4702+
47034703
uint64_t term = raft_rpc_get_term(rpc);
4704-
if (term
4705-
&& !raft_should_suppress_disruptive_server(raft, rpc)
4706-
&& !raft_receive_term__(raft, &rpc->common, term)) {
4704+
if (term && !raft_receive_term__(raft, &rpc->common, term)) {
47074705
if (rpc->type == RAFT_RPC_APPEND_REQUEST) {
47084706
/* Section 3.3: "If a server receives a request with a stale term
47094707
* number, it rejects the request." */
@@ -5227,6 +5225,7 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED,
52275225
failure_test = FT_CRASH_BEFORE_SEND_SNAPSHOT_REP;
52285226
} else if (!strcmp(test, "delay-election")) {
52295227
failure_test = FT_DELAY_ELECTION;
5228+
52305229
struct raft *raft;
52315230
HMAP_FOR_EACH (raft, hmap_node, &all_rafts) {
52325231
if (raft->role == RAFT_FOLLOWER) {
@@ -5235,6 +5234,13 @@ raft_unixctl_failure_test(struct unixctl_conn *conn OVS_UNUSED,
52355234
}
52365235
} else if (!strcmp(test, "dont-send-vote-request")) {
52375236
failure_test = FT_DONT_SEND_VOTE_REQUEST;
5237+
} else if (!strcmp(test, "force-election")) {
5238+
struct raft *raft;
5239+
HMAP_FOR_EACH (raft, hmap_node, &all_rafts) {
5240+
if (raft->role != RAFT_LEADER) {
5241+
raft_start_election(raft, true, false);
5242+
}
5243+
}
52385244
} else if (!strcmp(test, "stop-raft-rpc")) {
52395245
failure_test = FT_STOP_RAFT_RPC;
52405246
} else if (!strcmp(test,

tests/ovsdb-cluster.at

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,18 @@ for i in $(seq $n); do
10051005
AT_CHECK([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name | grep "Term: 1"], [0], [ignore])
10061006
done
10071007

1008+
# Now force election without pausing RPC, so the requests are actually sent.
1009+
AT_CHECK([ovs-appctl -t $(pwd)/s2 cluster/failure-test force-election], [0], [ignore])
1010+
1011+
# The server transitions into a candidate role while engaging the test
1012+
# and shortly after it should step back to be a follower.
1013+
OVS_WAIT_UNTIL([ovs-appctl -t $(pwd)/s2 cluster/status $schema_name | grep "Role: follower"])
1014+
1015+
# No term change.
1016+
for i in $(seq $n); do
1017+
AT_CHECK([ovs-appctl -t $(pwd)/s$i cluster/status $schema_name | grep "Term: 1"], [0], [ignore])
1018+
done
1019+
10081020
for i in $(seq $n); do
10091021
OVS_APP_EXIT_AND_WAIT_BY_TARGET([$(pwd)/s$i], [s$i.pid])
10101022
done

0 commit comments

Comments
 (0)