feat(ha-term-safety): restore controller build + wire vote/AE term wire fields (tasks 1.1, 1.2)

1.1: WalCatchupEntry gains term@4/prevLogIndex@5/prevLogTerm@6 (append-only).
1.2: RequestVote/PreVote gain lastLogTerm@4/lastLogIndex@5; cluster.c vote callers
fixed to the extended library arity (RESTORES xce_controller + controller_unit_test
compile+link, broken by the raft-commit-safety signature extension); ha.c emits the
candidate's last-log tuple on send.

5 minimal stub log/commit callbacks wired into s_real_cbs so raft_init's new
non-NULL requirement is satisfied — tasks 2.x replace them with real bodies; the
logOk freshness gate is inert until the term seam (1.3) + on_get_entry_term (2.2).

Pre-existing test_repl_unit.c wal_catchup -EAGAIN failures + ASAN leaks are an
overlayfs /tmp fsync env artifact (bug-850), not from this task (untouched paths;
append-only schema cannot affect the @3 entries-list decode). Build/baseline
isolation is blocked by a c-capnproto flake-hash drift — see Implementation Notes.
This commit is contained in:
Rongsong Shen 2026-06-22 03:41:49 +00:00
parent e8e41752c0
commit 004a40bb3b
9 changed files with 264 additions and 9 deletions

View file

@ -389,6 +389,11 @@ static int myreq_handler(vtask_t* task)
uint64_t cand_tag = vrq ? vrq->config_tag : 0;
uint64_t cand_lsn = vrq ? vrq->last_log_lsn : 0;
uint64_t cand_term = vrq ? vrq->term : 0;
/* ha-term-safety task 1.2: candidate's Raft (lastLogTerm, lastLogIndex)
* tuple drives the library logOk freshness gate (Req 2.3/2.4/2.6),
* distinct from the config_tag/last_log_lsn application gate above. */
uint64_t cand_llt = vrq ? vrq->last_log_term : 0;
uint64_t cand_lli = vrq ? vrq->last_log_index : 0;
uint64_t local_tag = c->myconf->config_version_tag;
/* Task 4.2 (ha-commit-durability): the voter floor's local-state LSN
* input is the DURABLE WAL tip (persist_durable_floor), not the
@ -428,7 +433,7 @@ static int myreq_handler(vtask_t* task)
int granted = 0;
if (freshness_ok && from_idx >= 0) {
int r = raft_on_vote_request(&c->ha.raft, from_idx, cand_term,
&reply_term);
cand_llt, cand_lli, &reply_term);
granted = (r == RAFT_GRANT) ? 1 : 0;
} else {
/* Freshness deny or unknown candidate: read current term for reply */
@ -479,6 +484,10 @@ static int myreq_handler(vtask_t* task)
uint64_t cand_tag = vrq ? vrq->config_tag : 0;
uint64_t cand_lsn = vrq ? vrq->last_log_lsn : 0;
uint64_t cand_term = vrq ? vrq->term : 0;
/* ha-term-safety task 1.2: candidate's Raft (lastLogTerm, lastLogIndex)
* tuple drives the SHARED library logOk gate (identical to real-vote). */
uint64_t cand_llt = vrq ? vrq->last_log_term : 0;
uint64_t cand_lli = vrq ? vrq->last_log_index : 0;
uint64_t local_tag = c->myconf->config_version_tag;
/* Task 4.2 (ha-commit-durability): voter-floor local-state LSN is the
* DURABLE WAL tip (persist_durable_floor), not the volatile applied_lsn
@ -521,7 +530,8 @@ static int myreq_handler(vtask_t* task)
} else {
/* raft_on_pre_vote_request is pure: stale-term + eligibility gate,
* no term/voted_for/role mutation, no persist. */
int r = raft_on_pre_vote_request(&c->ha.raft, from_idx, cand_term);
int r = raft_on_pre_vote_request(&c->ha.raft, from_idx, cand_term,
cand_llt, cand_lli);
if (r == RAFT_GRANT) {
granted = 1;
} else {