feat(ha-term-safety): on_send_append_entries callback body (task 2.3)
Replaces the stub: builds a WalCatchupEntry AppendEntries to standbys[peer_idx].ep carrying prev_log_index/prev_log_term; for n_entries==1 sources the entry at prev_log_index+1 from the local WAL (term via persist_entry_term, type+payload via ha_read_wal_entry_at); n_entries==0 emits a zero-entry heartbeat. Async send (xce_wal_batch_req_raw), payload freed on all paths. 2 tests via a capture seam. Review-applied: corrected the misleading repl.c comment — leader_commit is accepted but NOT yet wire-encoded (deferred to task 3.2: WalCatchupEntry leaderCommit field + standby intake). Reviewer APPROVED; per-group 0 failures.
This commit is contained in:
parent
ed2443533f
commit
2c5d298cb5
6 changed files with 452 additions and 4 deletions
|
|
@ -310,6 +310,102 @@ int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* AppendEntries send (ha-term-safety task 2.3)
|
||||
*
|
||||
* The library drives on_send_append_entries(peer_idx, prev tuple,
|
||||
* leader_commit, n_entries); the ha.c adapter sources the single entry (for
|
||||
* n_entries==1) from its own WAL and calls this to build + enqueue the
|
||||
* AppendEntries envelope to the standby at slot peer_idx. Non-blocking: reuses
|
||||
* the async xce_wal_batch_req_raw transport.
|
||||
* ============================================================ */
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
repl_ae_capture_t *repl_send_ae_capture_hook = NULL;
|
||||
#endif
|
||||
|
||||
int repl_send_append_entries(replicator_t *r, int peer_idx,
|
||||
uint64_t prev_log_index, uint64_t prev_log_term,
|
||||
uint64_t leader_commit, int n_entries,
|
||||
uint64_t entry_lsn, uint8_t entry_type,
|
||||
uint64_t entry_term,
|
||||
const void *payload, uint32_t payload_len)
|
||||
{
|
||||
if (r == NULL || peer_idx < 0 || peer_idx >= MAX_STANDBYS)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
/* Capture seam: record the built envelope without network I/O. */
|
||||
if (repl_send_ae_capture_hook) {
|
||||
repl_ae_capture_t *cap = repl_send_ae_capture_hook;
|
||||
cap->called = 1;
|
||||
cap->peer_idx = peer_idx;
|
||||
cap->prev_log_index = prev_log_index;
|
||||
cap->prev_log_term = prev_log_term;
|
||||
cap->leader_commit = leader_commit;
|
||||
cap->n_entries = n_entries;
|
||||
cap->entry_lsn = (n_entries == 1) ? entry_lsn : 0;
|
||||
cap->entry_type = (n_entries == 1) ? entry_type : 0;
|
||||
cap->entry_term = (n_entries == 1) ? entry_term : 0;
|
||||
if (n_entries == 1 && payload && payload_len > 0) {
|
||||
uint32_t cpy = payload_len;
|
||||
if (cpy > sizeof(cap->payload))
|
||||
cpy = sizeof(cap->payload);
|
||||
memcpy(cap->payload, payload, cpy);
|
||||
cap->payload_len = cpy;
|
||||
} else {
|
||||
cap->payload_len = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
mylock_lock(&r->lock);
|
||||
standby_conn_t *s = &r->standbys[peer_idx];
|
||||
repl_ep_t ep = s->ep;
|
||||
char host[XCE_MAXNAME_LEN];
|
||||
uint32_t port = s->port;
|
||||
strncpy(host, s->host, sizeof(host) - 1);
|
||||
host[sizeof(host) - 1] = '\0';
|
||||
mylock_unlock(&r->lock);
|
||||
|
||||
if (ep == NULL)
|
||||
return -ENOENT; /* no live endpoint at this peer slot */
|
||||
|
||||
/* Build a single AppendEntries envelope carrying the prev tuple +
|
||||
* leader_commit and, for n_entries==1, the entry sourced from the leader's
|
||||
* WAL. The carrier struct already holds the Raft term fields (task 1.1). */
|
||||
wal_catchup_entry_t entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.prev_log_index = prev_log_index;
|
||||
entry.prev_log_term = prev_log_term;
|
||||
/* NOTE: leader_commit is accepted as a parameter (and captured by the test
|
||||
* seam) but is NOT yet wire-encoded — WalCatchupEntry has no leaderCommit
|
||||
* field. Adding that capnp field and the standby-side intake of leader_commit
|
||||
* is deferred to task 3.2; no standby acts on it until then.
|
||||
* A heartbeat is marked by n_entries==0 (zero entries; lsn left 0). */
|
||||
if (n_entries == 1) {
|
||||
entry.lsn = entry_lsn;
|
||||
entry.entry_type = entry_type;
|
||||
entry.term = entry_term;
|
||||
entry.raw_payload = (uint8_t *)(uintptr_t)payload;
|
||||
entry.raw_payload_len = payload_len;
|
||||
}
|
||||
|
||||
wal_catchup_entry_t *ep_arr[1] = { &entry };
|
||||
int rc = xce_wal_batch_req_raw(ep, ep_arr, n_entries == 1 ? 1 : 0);
|
||||
if (rc != 0) {
|
||||
WARN("repl_send_append_entries: send to standby %s:%u failed (rc=%d)",
|
||||
host, port, rc);
|
||||
return rc;
|
||||
}
|
||||
TRACE("repl_send_append_entries: peer=%d prevIdx=%lu prevTerm=%lu "
|
||||
"leaderCommit=%lu n=%d to %s:%u",
|
||||
peer_idx, (unsigned long)prev_log_index, (unsigned long)prev_log_term,
|
||||
(unsigned long)leader_commit, n_entries, host, port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Sync replication — stub in P2-1; wired in P2-3
|
||||
* ============================================================ */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue