Skip to content

Commit 81f445b

Browse files
committed
raft: refresh election timer on recovery-path append_entries
A follower that is behind a live leader can get stuck in a perpetual pre-vote storm. `should_skip_vote` suppresses elections only while `_hbeat` (last leader contact) is within one election timeout, but in `do_append_entries` the `_hbeat` refresh lives on the successful batch-append path (a deferred update). The two early returns that fire when the follower is behind, log gap (prev_log_index > last_log_offset) and prev-log-term mismatch, call maybe_update_leader() and reply to the leader but never refresh `_hbeat`. So a follower in continuous contact with the current-term leader, but unable to append because it is still being recovered, lets its election timer expire, starts a (pre-)vote that deterministically fails the longest-log check (log_ok=false), rearms, and repeats. The storm burns CPU that further starves recovery, a self-reinforcing livelock that leaves partitions under-replicated and stalls acks=all produce with REQUEST_TIMED_OUT. Refresh `_hbeat` as soon as an append_entries from the current-term leader is accepted (right after maybe_update_leader), covering the recovery early-return paths as well as the success path. This matches the existing design intent noted in vote_request handling, where the comment states `_hbeat` is updated by the leader. Liveness is preserved: if the leader is actually gone, no append_entries arrive, `_hbeat` goes stale, and elections proceed as before. Fixes #30815 Signed-off-by: Steven Pall <mail@stevenpall.ca>
1 parent 84702c7 commit 81f445b

1 file changed

Lines changed: 2 additions & 5 deletions

File tree

src/v/raft/consensus.cc

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,6 +2052,8 @@ consensus::do_append_entries(append_entries_request&& r) {
20522052
// follower (§5.2)
20532053
maybe_update_leader(r.source_node());
20542054

2055+
auto refresh_hbeat = ss::defer([this] { _hbeat = clock_type::now(); });
2056+
20552057
// raft.pdf: Reply false if log doesn’t contain an entry at
20562058
// prevLogIndex whose term matches prevLogTerm (§5.3)
20572059
// broken into 3 sections
@@ -2307,11 +2309,6 @@ consensus::do_append_entries(append_entries_request&& r) {
23072309
// success. copy entries for each subsystem
23082310

23092311
try {
2310-
auto deferred = ss::defer([this] {
2311-
// we do not want to include our disk flush latency into
2312-
// the leader vote timeout
2313-
_hbeat = clock_type::now();
2314-
});
23152312
validate_offset_translator_delta(request_metadata, lstats);
23162313

23172314
// simulate disk error

0 commit comments

Comments
 (0)