myxce/src/lib/testsuite
Rongsong Shen 0941cf9649 fix(ha): close two independent chaotic-failover bugs — term-desync liveness stall and bug-847 WAL batch payload loss
Found via extended chaotic /xce-test-ha soaking on top of the bug-1419 fix
(same session). Both bugs independently caused the 10-round chaotic soak to
fail; sequential and chaotic now pass cleanly and repeatably.

1. Term-desync liveness deadlock (new): a standby that loses a near-
   simultaneous election race to a peer can legitimately end up at a HIGHER
   term than the peer that actually won and became primary. The primary's
   now-stale-term AppendEntries/heartbeats are correctly rejected by the raft
   library, but every one of those rejected messages was ALSO resetting the
   standby's own liveness deadline (ha_on_heartbeat_received), so its
   liveness timeout never fired and it could never start a fresh,
   higher-term election to let the cluster self-heal — a permanent stall.
   Fix: ha_msg_term_is_stale() gates the liveness reset (and
   ha_discover_primary step-down) on the incoming message's own term stamp,
   skipping it for provably-stale messages, in cluster.c's wal_catchup_entry
   and nop/heartbeat handlers.

2. bug-847 (root-caused 2026-06-30, never fixed): a per-sub-entry `payload`
   Data blob in a multi-entry WAL catch-up batch loses its capnp pointer once
   the message crosses a segment boundary — the receiver decodes
   raw_payload_len=0 for entries at/after the boundary and silently persists
   empty WAL entries (structurally intact: correct LSN/term/CRC chain, empty
   content). Only surfaces when a node rejoins and catches up a real batch,
   which is why it needed a live chaotic soak (not the 2-entry unit test) to
   reproduce. Fix: carry every sub-entry's payload concatenated in ONE
   top-level batchPayloads blob instead of one Data blob per entry — a
   single top-level blob does not exhibit the cross-segment loss. Sliced
   back apart on decode using each sub-entry's own wirePayloadLen, a plain
   scalar that (like lsn/entryType/term) decodes via the generic generated
   decode path and was never subject to the bug. Confirmed via targeted
   diagnostic logging that the corrupted entry arrives already-empty over
   the wire (not a receive-side race), and via a faithful reproduction at
   live JOB_SUBMIT payload size (5×3560B) after the original 2-entry/
   14×872B tests turned out not to cross a segment boundary in this
   environment.

Incidental fixes found and fixed along the way:
- A real self-deadlock (persist_entry_term() re-acquiring p->lock from
  inside a caller that already held it) introduced by an initial, ultimately
  unnecessary WAL-read locking fix attempted before bug-847 was pinned down;
  fixed via a new persist_entry_term_locked() lock-free counterpart. Left in
  place as a genuine (if incidental) hardening of ha_stream_wal_to_standby's
  read consistency.
- .claude/skills/xce-test-ha/SKILL.md's `context: fork` frontmatter caused
  the skill's own forked sub-session teardown to SIGHUP the whole
  xce_controller process group mid-soak, killing all 3 nodes silently with
  no error logged, whenever the soak was launched via the Skill tool itself
  (setsid/nohup inside run-ha-soak.sh only protects against a controlling
  terminal's own SIGHUP, not a parent forked-session's teardown). Removed;
  added explicit guidance to launch long soaks via direct Bash from the main
  session instead.

Verified: full 78-group unit regression sweep clean (0 failures) after each
change; two consecutive clean 10-round chaotic soaks and one clean 10-round
sequential soak on the final binary (zero committed divergence, zero job
loss, all 3 nodes led in both modes).
2026-07-08 15:21:24 +00:00
..
failover_harness.h feat(libxce-failover): reusable failover test harness — fake masters, status taxonomy, concurrency runner (task 1.2) 2026-06-17 02:32:19 +00:00
test.c feat(raft-drive-async): LivenessAdmission pool reservation + commit fold (tasks 1.6/1.7) 2026-06-26 07:43:19 +00:00
test_bitset.c - introduce bitset 2026-02-13 11:01:33 +08:00
test_cluster.c feat: P2.5-1/2/2a/3/8 xce_cluster_config_t, xce_connect_cluster, xce_reconnect_step 2026-05-08 02:16:08 +00:00
test_cluster_parse.c feat: P2.5-5/6 add --peers/--ca-cert parsing to worker+scheduler with validation 2026-05-08 02:32:56 +00:00
test_config.c fix(test): sync test_config DEFAULT_SYNC_TIMEOUT_MS assertion to 500 2026-06-21 15:18:39 +00:00
test_ctrlstatus.c fix: close bug-1401 ctrlstatus leak, bug-1400 jobsub leak; harden bug-1396 harness 2026-07-03 02:40:52 +00:00
test_delay.c feat(election-iomgr-refactor): add delay_task_ms() for ms-granularity one-shot tasks 2026-05-30 07:15:54 +00:00
test_error.c fix(libxce): xce_strerror_r global-buffer-overflow on HA error codes (utils.c) 2026-06-20 10:13:21 +00:00
test_group.c save works 2026-02-28 17:55:50 +08:00
test_hooks.c fix bug-1060 bug-1067 2026-06-24 17:05:43 +08:00
test_htab.c update controller code 2025-11-05 16:58:57 +08:00
test_htab_int.c update controller code 2025-11-05 16:58:57 +08:00
test_idgen.c jobid format and parse 2025-11-19 14:44:49 +08:00
test_jobj.c save works 2026-03-06 18:20:30 +08:00
test_kv.c use librtti for worker/queue/resource 2026-02-25 16:56:50 +08:00
test_kvmgr.c save works 2026-03-04 18:18:53 +08:00
test_kvmgr_capture.c test: add KVMGR_KEY_INT coverage to kvmgr_capture_keys tests 2026-05-07 11:12:11 +00:00
test_libxce_retry.c fix(libxce-failover): known-dead ctx (XCE_CTX_ERR) triggers reconnect instead of surfacing -EINVAL 2026-06-17 04:46:01 +00:00
test_limit.c save works 2026-02-28 17:55:50 +08:00
test_netio_arm_failure.c fix(libxce-failover): classify and propagate netio I/O errors on failover 2026-06-18 04:17:07 +00:00
test_netio_bounded_connect.c feat(raft-drive-async): bound peer connect (O_NONBLOCK+poll), close tasks 1.3/1.4/1.5 2026-06-26 07:10:53 +00:00
test_netio_close_propagation.c fix(libxce-failover): classify and propagate netio I/O errors on failover 2026-06-18 04:17:07 +00:00
test_netio_on_connected.c feat(election-iomgr-refactor): add on_connected fire-once callback to endpoint_t 2026-05-30 07:34:08 +00:00
test_ordq.c pass pend reason back to controller 2025-12-23 15:29:26 +08:00
test_preason.c support limit 2026-01-12 15:27:54 +08:00
test_qobj.c save works 2026-03-06 18:20:30 +08:00
test_raft.c feat(raft-apply-decoupling): task 7.1 apply-ordering conformance assertions 2026-07-05 05:25:32 +00:00
test_request.c feat(ha-heartbeat-committed-propagation): add xce_ha_heartbeat_req sender (task 1.2) 2026-06-20 11:40:31 +00:00
test_request_send_propagation.c fix(libxce-failover): classify and propagate netio I/O errors on failover 2026-06-18 04:17:07 +00:00
test_resmgr_fixture.c feat: implement Phase 4d const-fold transform for select AST 2026-04-29 08:24:04 +00:00
test_resmgr_fixture.h feat: implement Phase 4d const-fold transform for select AST 2026-04-29 08:24:04 +00:00
test_resreq.c docs: mark Phase 6 complete in phase6-scheduler-integration-plan 2026-04-30 07:17:32 +00:00
test_select.c fix: sel_key_cmp_fn padding-byte mismatch in kvtab lookups 2026-05-12 09:53:59 +00:00
test_utils.c use clang-format to format code 2025-11-25 14:58:09 +08:00
test_vtask_admission.c feat(raft-drive-async): LivenessAdmission pool reservation + commit fold (tasks 1.6/1.7) 2026-06-26 07:43:19 +00:00
test_wal_wire.c fix(ha): close two independent chaotic-failover bugs — term-desync liveness stall and bug-847 WAL batch payload loss 2026-07-08 15:21:24 +00:00
test_wobj.c save works 2026-03-06 18:20:30 +08:00
test_xlist.c support limit 2026-01-12 15:27:54 +08:00
test_xstring.c docs: mark Phase 6 complete in phase6-scheduler-integration-plan 2026-04-30 07:17:32 +00:00
test_xvec.c using xvec related api in scheduler 2026-01-16 18:21:37 +08:00
test_zarray.c use librtti for worker/queue/resource 2026-02-25 16:56:50 +08:00
test_zarray_cow.c feat: implement xzarray COW freeze/thaw with pre-grown shadow array (P0-2) 2026-05-07 09:46:28 +00:00