test(ha-convergence-recovery): RED demotion-convergence + self-restart tests + seams (task 2.1)

This commit is contained in:
Rongsong Shen 2026-06-14 03:12:25 +00:00
parent b9ddfa60cb
commit c8e55947c2
7 changed files with 668 additions and 1 deletions

View file

@ -239,6 +239,41 @@ void ha_do_promote(ha_state_t *ha);
void ha_do_demote(ha_state_t *ha);
/* ── Demotion convergence + self-restart primitive ─────────────
* (ha-convergence-recovery Requirement 1: a demoted ex-primary discards its
* uncommitted, potentially divergent state and rejoins from committed state
* only via truncation + process self-restart through boot recovery.)
*/
/*
* controller_saved_argv: accessor for the NULL-terminated argv captured by
* main() before argp_parse, used by ha_self_restart to re-exec the controller
* (ha-convergence-recovery Req 1.3, design "Self-Restart Primitive").
* Returns NULL if argv was never saved (e.g. unit-test binaries).
*/
char **controller_saved_argv(void);
/*
* ha_self_restart: the truncate-and-restart primitive's restart half.
*
* Production contract (Req 1.3, 1.4): flush logs, execv(controller_saved_argv())
* to rebuild in-memory state through boot recovery from the persisted committed
* state only; if exec cannot be performed, terminate FATALLY (abort) rather than
* continue running with potentially divergent state — NEVER returns in
* production.
*
* Test contract (Req 1.6): under XCE_TESTING, invoke ha_restart_hook instead of
* execv and return its result (0 on the success path); a failing ha_restart_hook
* routes to ha_fatal_hook in place of abort so tests can assert the FATAL path
* without dying.
*
* Preconditions: persistence hooks already disabled; truncation completed
* before this call. Invariant: no new WAL entry is appended by this node
* between hook-disable and exec.
*/
int ha_self_restart(cluster_t *c);
/* ── Redirect helpers used by join.c ──────────────────────── */
/*
@ -768,6 +803,47 @@ extern void (*ha_rdhup_election_hook)(ha_state_t *ha, cluster_t *c,
* Requirements: task 7.3 (ha-peer-connection-reuse).
*/
void vote_ep_ctx_set_cluster_for_test(struct endpoint_t *ep, cluster_t *c);
/*
* ha_restart_hook: test-only seam for ha_self_restart's process re-execution
* (ha-convergence-recovery Req 1.6).
*
* When non-NULL, ha_self_restart() invokes this INSTEAD OF execv() so unit
* tests can observe the eventual restart deterministically (the demotion
* convergence task and rejoin reconciliation both end in ha_self_restart).
* The hook's return value is ha_self_restart's return value on the
* test-success path; a NON-ZERO return models an exec failure and routes
* ha_self_restart to the FATAL contract via ha_fatal_hook (Req 1.4).
*
* Signature: int hook(cluster_t *c)
* Returns 0 to model a successful restart; non-zero to model exec failure.
*/
extern int (*ha_restart_hook)(cluster_t *c);
/*
* ha_fatal_hook: test-only seam for the FATAL (abort) contract of
* ha_self_restart (ha-convergence-recovery Req 1.4).
*
* When non-NULL, ha_self_restart() invokes this INSTEAD OF abort() when the
* restart cannot be performed, so a test binary can assert the FATAL path was
* taken without the process dying. ha_self_restart returns after invoking it
* (test builds only — production aborts).
*
* Signature: void hook(const char *msg)
*/
extern void (*ha_fatal_hook)(const char *msg);
/*
* raft_cb_become_follower_with_cluster_for_test: drive the follower path with
* a LIVE cluster pointer (ha-convergence-recovery task 2.1 demotion-sequence
* tests). Unlike raft_cb_become_follower_for_test (which forces raft_c = NULL
* to suppress production calls), this shim sets ha->raft_c = c first so the
* cluster-dependent demotion-convergence branch (task 2.2: hook disable +
* scheduled truncate/restart) is exercised against a real persist/hooksys/
* delaymgr fixture. Mirrors the real-cluster seam ha_promote_to_primary(c)
* used by the promotion-side lifecycle tests.
*/
void raft_cb_become_follower_with_cluster_for_test(cluster_t *c, uint64_t term);
#endif
#endif /* HA_H */