feat(raft-log-replication): raft_send_append_entries AppendEntries emit (tasks 4.1, 4.2)

Leader-only emit (raft.tla AppendEntries:204, Req 3.5 — no-op off leader);
prev_log_index=next_index[j]-1, prev_log_term via on_get_entry_term (0/unqueried
at index 0, Req 3.2-3.3); n_entries=1 when next_index[j]<=log_len else 0 heartbeat
(Req 3.4); leader_commit opaque passthrough. Trace fires only on successful send
(consistency with raft_log_append) and non-leader RAFT_OK asserted — both from
review. 4 spy-based tests. ASAN green.
This commit is contained in:
Rongsong Shen 2026-06-21 16:01:17 +00:00
parent e5b9b83955
commit a1aacc0389
3 changed files with 247 additions and 2 deletions

View file

@ -503,3 +503,41 @@ int raft_log_append(raft_state_t *s, const void *entry, size_t len,
return RAFT_OK;
}
int raft_send_append_entries(raft_state_t *s, int peer_idx,
uint64_t leader_commit)
{
/* Leader-only (Req 3.5): AppendEntries is enabled only at the leader in
* raft.tla. Off the leader this is a no-op — no callback, no state change. */
if (s->role != RAFT_LEADER)
return RAFT_OK;
/* prevLogIndex = nextIndex[j] - 1; prevLogTerm = on_get_entry_term(prevIdx)
* when prevIdx > 0, else 0 (the "absent" sentinel, matching raft.tla
* prevLogTerm == 0 when prevLogIndex == 0). Do NOT query the callback at
* index 0 (Req 3.2). */
uint64_t prev_log_index = s->next_index[peer_idx] - 1;
uint64_t prev_log_term = (prev_log_index > 0)
? s->cbs->on_get_entry_term(s->ctx, prev_log_index)
: 0;
/* lastEntry = Min(log_len, next_index[j]); send SubSeq(log, next_index[j],
* lastEntry) — at most one entry. When next_index[j] > log_len there is
* nothing to send → a 0-entry heartbeat (Req 3.3). */
int n_entries = (s->next_index[peer_idx] <= s->log_len) ? 1 : 0;
/* leader_commit is passed through opaquely; this spec never interprets or
* advances commit (Req 3.4 — commit is owned by raft-commit-safety). */
int rc = s->cbs->on_send_append_entries(s->ctx, peer_idx, prev_log_index,
prev_log_term, leader_commit,
n_entries);
if (rc != 0)
return rc;
/* Trace only on a successful send (mirrors raft_log_append, which traces
* after a confirmed persist) — a failed emit must not look successful. */
if (s->cbs->on_trace)
s->cbs->on_trace(s->ctx, "ae_send", NULL);
return RAFT_OK;
}