feat(ha-term-safety): route AppendEntries intake through library + leaderCommit wire (task 3.2)

This commit is contained in:
Rongsong Shen 2026-06-22 08:42:35 +00:00
parent 6a519a928b
commit 754e586f8f
10 changed files with 508 additions and 168 deletions

View file

@ -202,7 +202,15 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
extract_data_bytes(WalCatchupEntry_get_payload(b_ptr),
&batch->raw_payload_len);
/* Batch sub-entries */
/* ha-term-safety task 3.2: the top-level container's AppendEntries
* metadata (term, prevLogIndex, prevLogTerm, leaderCommit) is decoded
* into batch->{term,prev_log_index,prev_log_term,leader_commit} by the
* generated decode_WalCatchupEntry (all scalar $C.mapname fields) — only
* the Data payload needs manual extraction (above/below). The standby
* intake (ha_consume_append_entries) reads those struct fields directly. */
/* Batch sub-entries: each carries its own lsn/entryType/term (scalar,
* decoded) + payload (Data, extracted here). */
WalCatchupEntry_list wlist = WalCatchupEntry_get_entries(b_ptr);
int wn = (wlist.p.type != CAPN_NULL) ? capn_len(wlist) : 0;
for (int i = 0; i < wn && i < batch->n_entries; i++) {
@ -467,6 +475,9 @@ static void wal_entry_to_wire(struct capn_segment *cs,
memset(w, 0, sizeof(*w));
w->lsn = e->lsn;
w->entryType = e->entry_type;
/* ha-term-safety task 3.2: carry the entry's term stamp so the standby
* intake persists the LEADER's recorded term (R1.3/R1.4), not a substitute. */
w->term = e->term;
if (e->raw_payload && e->raw_payload_len > 0) {
capn_list8 pl = capn_new_list8(cs, (int)e->raw_payload_len);
capn_setv8(pl, 0, e->raw_payload, (int)e->raw_payload_len);
@ -499,7 +510,10 @@ int xce_wal_batch_encode_raw(wal_catchup_entry_t **entries, int n_entries,
Request_ptr rp;
int rc;
if (pkt == NULL || n_entries <= 0 || entries == NULL)
/* n_entries == 0 is a valid AppendEntries HEARTBEAT (ha-term-safety task 3.2):
* entries[0] still carries the AppendEntries metadata (prev tuple +
* leader_commit) on the top-level container, with an empty entries list. */
if (pkt == NULL || n_entries < 0 || entries == NULL)
return -EINVAL;
capn_init_malloc(&c);
@ -508,7 +522,18 @@ int xce_wal_batch_encode_raw(wal_catchup_entry_t **entries, int n_entries,
memset(&rq, 0, sizeof(rq));
memset(&top, 0, sizeof(top));
/* Batch container: header fields zero, entries list carries the batch */
/* ha-term-safety task 3.2: the top-level container carries the AppendEntries
* metadata (prev tuple + leaderCommit) so a 0-entry heartbeat still conveys
* UpdateTerm/logOk inputs. entries[0] is the metadata holder for both the
* heartbeat (n_entries==0) and the single-entry (n_entries==1) AE built by
* repl_send_append_entries. Legacy catch-up batches leave these zero. */
top.term = entries[0]->term;
top.prevLogIndex = entries[0]->prev_log_index;
top.prevLogTerm = entries[0]->prev_log_term;
top.leaderCommit = entries[0]->leader_commit;
/* Batch container: entries list carries the per-entry payload(s) (empty for
* a heartbeat). Each sub-entry carries lsn/entryType/term/payload. */
WalCatchupEntry_list lst = new_WalCatchupEntry_list(cs, n_entries);
for (int i = 0; i < n_entries; i++) {
struct WalCatchupEntry w;