fix(controller): advertise true last-log term in vote/prevote — unblock round-4 election (bug-851/task 5.5)
The election cross-deny that stalled round 4 (after bug-1282+bug-1286 fixed): the freshest survivor (e.g. ctrl-2 tip 15 @ term 5) could not win because it advertised lastLogTerm = c->persist.term (the persist layer's RUNNING term, stale/0 after a restart) instead of the term STAMPED ON its last WAL entry. So a voter (ctrl-3, last entry term 4) saw the candidate's lastLogTerm <= its own and raft_log_ok rejected the genuinely-fresher candidate -> every round timed out -> permanent stall. This was a documented stopgap (ha.c "task 1.3" NOTE) and is the bug-851 term-identity family. Fix both advertise sites (real-vote raft_cb_send_vote_request + PreVote raft_cb_send_pre_vote_request): lastLogTerm = persist_entry_term(&c->persist, ha->raft.log_len) when log_len>0 — the SAME accessor voters use to compute their own last-log term (on_get_entry_term -> persist_entry_term), so logOk is now symmetric and the freshest survivor wins. Strengthens Election Safety/Leader Completeness (only genuinely-fresher candidates are granted); the index source (ha->raft.log_len, seeded from the recovered WAL tip) is unchanged. Root-caused from a frozen round-4 soak: ctrl-2 (15@t5) vs ctrl-3 (11@t4); ctrl-3 denied ctrl-2 on logOk (mislabeled "eligibility"). Re-running 7.2 to confirm. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019dbTHR9CsEYK9RsqmQpQpC
This commit is contained in:
parent
5a36698a90
commit
b8453b2d2e
1 changed files with 20 additions and 8 deletions
|
|
@ -179,12 +179,20 @@ static int raft_cb_send_vote_request(void *ctx, int peer_idx, uint64_t term)
|
||||||
vctx->my_lsn = (ectx->n_peers > 0) ? ectx->votes[0].my_lsn : 0;
|
vctx->my_lsn = (ectx->n_peers > 0) ? ectx->votes[0].my_lsn : 0;
|
||||||
/* ha-term-safety task 1.2: advertise our Raft (lastLogTerm, lastLogIndex).
|
/* ha-term-safety task 1.2: advertise our Raft (lastLogTerm, lastLogIndex).
|
||||||
* lastLogIndex == our WAL log length (the library's log_len == WAL tip LSN);
|
* lastLogIndex == our WAL log length (the library's log_len == WAL tip LSN);
|
||||||
* lastLogTerm == the recovered/live WAL term that stamps that last entry.
|
* lastLogTerm == the term STAMPED ON THAT LAST ENTRY.
|
||||||
* NOTE: sourced directly from persist (c->persist.term / raft.log_len), NOT
|
*
|
||||||
* via on_get_entry_term — that callback seam is task 2.x and is unwired here
|
* bug-851 / task 5.5: lastLogTerm MUST be the term of the entry at log_len —
|
||||||
* (see CONCERNS). The formal persist_entry_term accessor is task 1.3. */
|
* sourced via persist_entry_term, the SAME accessor voters use to compute
|
||||||
|
* their own last-log term (on_get_entry_term → persist_entry_term, ha.c:1556).
|
||||||
|
* The old stopgap advertised c->persist.term (the persist layer's RUNNING
|
||||||
|
* term), which is stale/0 after a restart and does NOT equal the last entry's
|
||||||
|
* stored term. That made a freshly-restarted but freshest survivor advertise
|
||||||
|
* a too-low lastLogTerm, so every voter's logOk rejected it (asymmetric with
|
||||||
|
* how the voter computes its own term) and the election stalled. */
|
||||||
vctx->my_last_log_index = ha->raft.log_len;
|
vctx->my_last_log_index = ha->raft.log_len;
|
||||||
vctx->my_last_log_term = c ? c->persist.term : 0;
|
vctx->my_last_log_term = (c && ha->raft.log_len > 0)
|
||||||
|
? persist_entry_term(&c->persist, ha->raft.log_len)
|
||||||
|
: 0;
|
||||||
vctx->my_name = c->myconf ? c->myconf->name : "";
|
vctx->my_name = c->myconf ? c->myconf->name : "";
|
||||||
|
|
||||||
/* Get a fresh endpoint */
|
/* Get a fresh endpoint */
|
||||||
|
|
@ -294,10 +302,14 @@ static int raft_cb_send_pre_vote_request(void *ctx, int peer_idx,
|
||||||
vctx->my_term = prospective_term;
|
vctx->my_term = prospective_term;
|
||||||
vctx->my_tag = (ectx->n_peers > 0) ? ectx->votes[0].my_tag : 0;
|
vctx->my_tag = (ectx->n_peers > 0) ? ectx->votes[0].my_tag : 0;
|
||||||
vctx->my_lsn = (ectx->n_peers > 0) ? ectx->votes[0].my_lsn : 0;
|
vctx->my_lsn = (ectx->n_peers > 0) ? ectx->votes[0].my_lsn : 0;
|
||||||
/* ha-term-safety task 1.2: advertise our Raft (lastLogTerm, lastLogIndex)
|
/* ha-term-safety task 1.2 + bug-851/task 5.5: advertise lastLogTerm as the
|
||||||
* — identical sourcing to the real-vote send path above. */
|
* term of the entry at log_len (persist_entry_term — the same source voters
|
||||||
|
* use), NOT the stale c->persist.term. Identical sourcing to the real-vote
|
||||||
|
* send path above; see the full rationale there. */
|
||||||
vctx->my_last_log_index = ha->raft.log_len;
|
vctx->my_last_log_index = ha->raft.log_len;
|
||||||
vctx->my_last_log_term = c ? c->persist.term : 0;
|
vctx->my_last_log_term = (c && ha->raft.log_len > 0)
|
||||||
|
? persist_entry_term(&c->persist, ha->raft.log_len)
|
||||||
|
: 0;
|
||||||
vctx->my_name = c->myconf ? c->myconf->name : "";
|
vctx->my_name = c->myconf ? c->myconf->name : "";
|
||||||
|
|
||||||
endpoint_t *ep = endpoint_get();
|
endpoint_t *ep = endpoint_get();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue