feat(ha-repl-hardening): batch WAL streaming from decision loop (task 3.4)

ha_stream_wal_to_standby() reads WAL from committed_lsn range, accumulates
entries to 256/64KB per batch, enqueues all via xce_request() (non-blocking).
Partial final batch sent immediately. Wired into STREAM_WAL case.
3 tests: 500-entries/2-batches, partial batch, empty range.
Fixed stale task-number comments per review.
This commit is contained in:
Rongsong Shen 2026-06-04 17:57:55 +00:00
parent d1c7872d73
commit 6b69a47c50
7 changed files with 469 additions and 20 deletions

View file

@ -1017,18 +1017,19 @@ void ha_process_standby_state(ha_state_t *ha, int peer_idx,
* (stored by ha_process_standby_state on the same heartbeat reply), calls
* ha_evaluate_repl_action, and acts on the result:
*
* HA_REPL_PUSH_CONFIG → push_config_to_standbys(c, …) [or test hook]
* HA_REPL_SEND_SNAPSHOT→ stub (Task 3.4)
* HA_REPL_STREAM_WAL → stub (Task 3.5)
* HA_REPL_PUSH_CONFIG → push_config_to_standbys(c, …) [or test hook] (Task 3.3)
* HA_REPL_SEND_SNAPSHOT→ stub (Task 3.5)
* HA_REPL_STREAM_WAL → ha_stream_wal_to_standby(c, ha, idx) (Task 3.4)
* HA_REPL_CONVERGED → no-op
*
* One action per heartbeat cycle per standby (design §Unified Heartbeat-Driven
* Protocol). Returns 0 on success, -EINVAL on bad peer_idx.
*
* Requirements: 1.4 (ha-repl-hardening spec)
* Requirements: 1.4, 5.1–5.5 (ha-repl-hardening spec)
*/
int ha_dispatch_repl_action_for_standby(ha_state_t *ha,
cluster_t *c,
endpoint_t *ep,
int peer_idx,
uint64_t committed_tag,
uint64_t committed_lsn,
@ -1074,17 +1075,36 @@ int ha_dispatch_repl_action_for_standby(ha_state_t *ha,
break;
case HA_REPL_SEND_SNAPSHOT:
/* Task 3.4: snapshot transfer — stub */
/* Task 3.5: snapshot transfer — stub */
INFO("HA repl: standby[%d] lsn=%" PRIu64
" < oldest_wal_lsn=%" PRIu64 " → snapshot (stub)",
peer_idx, standby_lsn, oldest_wal_lsn);
break;
case HA_REPL_STREAM_WAL:
/* Task 3.5: WAL streaming — stub */
/* Task 3.4: batch WAL streaming.
* Enqueues all batch OPCode_walCatchupEntry messages for the range
* [standby_lsn+1, committed_lsn] via ha_stream_wal_to_standby().
* All xce_request() calls are non-blocking (enqueue-only). */
INFO("HA repl: standby[%d] lsn=%" PRIu64
" < committed_lsn=%" PRIu64 " → stream WAL (stub)",
" < committed_lsn=%" PRIu64 " → stream WAL batches",
peer_idx, standby_lsn, committed_lsn);
#ifdef XCE_TESTING
if (ha_repl_stream_wal_hook != NULL) {
const char *wal_path = (c && c->persist.wal_path[0])
? c->persist.wal_path : "";
ha_repl_stream_wal_hook(ep, wal_path, standby_lsn, committed_lsn);
break;
}
#endif
if (c && c->persist.wal_path[0]) {
extern int ha_stream_wal_to_standby(endpoint_t *ep,
const char *wal_path,
uint64_t standby_lsn,
uint64_t committed_lsn);
ha_stream_wal_to_standby(ep, c->persist.wal_path,
standby_lsn, committed_lsn);
}
break;
case HA_REPL_CONVERGED:
@ -1632,6 +1652,16 @@ int (*ha_nop_req_hook)(endpoint_t *ep) = NULL;
*/
int (*ha_repl_push_config_for_standby_hook)(void *c, int peer_idx) = NULL;
/*
* ha_repl_stream_wal_hook: test-only seam for ha_stream_wal_to_standby.
* When non-NULL, ha_dispatch_repl_action_for_standby() calls this INSTEAD OF
* ha_stream_wal_to_standby() when the decision is HA_REPL_STREAM_WAL.
* Requirements: 5.1–5.5 (ha-repl-hardening)
*/
int (*ha_repl_stream_wal_hook)(void *ep, const char *wal_path,
uint64_t standby_lsn,
uint64_t committed_lsn) = NULL;
#endif /* XCE_TESTING */
/* ── Election Evaluation (Req 2.1, 2.7, 2.8) ───────────────────────────────
@ -2211,13 +2241,13 @@ static int repl_ack_decode_and_dispatch(endpoint_t *ep, vote_ep_ctx_t *vctx)
committed_tag, committed_lsn);
/* Task 3.3: run the unified decision loop for this standby.
* oldest_wal_lsn=0 until snapshot transfer is wired (Task 3.4). */
* oldest_wal_lsn=0 until snapshot transfer is wired (Task 3.5). */
uint64_t oldest_wal_lsn = 0;
#ifndef XCE_TESTING
if (c)
oldest_wal_lsn = 0; /* Task 3.4: wire persist->oldest_wal_lsn */
oldest_wal_lsn = 0; /* Task 3.5: wire persist->oldest_wal_lsn */
#endif
ha_dispatch_repl_action_for_standby(ha, c, peer_idx,
ha_dispatch_repl_action_for_standby(ha, c, ep, peer_idx,
committed_tag, committed_lsn,
oldest_wal_lsn);
} else {