docs(raft-ha-core-safety): unify 7.2 round-N blocker via gdb — one restart-stability bug (SIGSEGV / iomgr accept-hang)

Used the now-available gdb (wrapped the release controller under gdb -batch via
XCE_HA_BIN) to expose the round-4 blocker's second face. Findings:

- The release-build SIGSEGV and a gdb-run accept-loop HANG are the SAME restart-
  time I/O-subsystem corruption: under gdb the memory bug is perturbed away from
  a crash and instead wedges the inbound-accept path (ctrl-3 stops accepting at
  03:42:03 while still firing its own election timer), so two restarted survivors
  can never exchange PreVotes -> permanent round-N stall.
- This is a restart-STABILITY bug, NOT a consensus-logic gap: PreVote/eligibility/
  freshness (incl. 5.3) are correct; the restarted node is simply I/O-dead. It
  predates this session (baseline 21e26a1 failed round 3-4).
- Heisenbug: invisible under ASAN and under gdb; no usable core (read-only
  core_pattern -> non-running systemd-coredump). Needs a non-ASAN DEBUG (-O0 -g)
  build under gdb, or a host with a live coredump pipeline, to backtrace.

Updates research.md, refines task 5.4 (the unified restart-stability bug +
the debug-build path), updates bug-1281. Consensus correctness (Phase-2/5.2/5.3)
is unaffected and validated for every round that completes (2-round soak PASSES;
6-round rounds 1-3 elect; zero job loss; committed prefix byte-identical).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019dbTHR9CsEYK9RsqmQpQpC
This commit is contained in:
Rongsong Shen 2026-06-30 03:57:51 +00:00
parent 75243fbb86
commit e80abd5cc4
3 changed files with 72 additions and 13 deletions

View file

@ -401,3 +401,42 @@ completes. The clean ≥6-round acceptance verdict is blocked by the restart seg
follow-up **task 5.4** (root-cause+fix the chaotic-restart crash; needs core+gdb / debug env)
and **bug-1281**. The harness `committedLsn`-boundary fix is a separate Phase-1 accuracy
improvement.
---
# Task 7.2/5.4 — UNIFIED root cause via gdb: round-N stall is a restart-stability bug (I/O wedge / SIGSEGV), not a consensus-logic gap
Wrapped the release `xce_controller` under `gdb -batch` (handles RT signals; dumps all-thread
bt on SIGSEGV) via `XCE_HA_BIN=<gdb-wrapper>` and re-ran the chaotic soak on `~/tmp`.
## Decisive findings
- **Two round-4 faces, ONE root cause.** Plain release: a restarted node **SIGSEGVs**
(captured `Segmentation fault (core dumped) … --myname ctrl-3`). Under gdb the crash does NOT
fire (ptrace perturbs the memory bug); instead the same restarted node's **inbound-accept /
iomgr path WEDGES** — ctrl-3's last `cluster.c:737 server sock accepted` is 03:42:03, yet it
keeps firing its own election timer (`pre_vote_start`) to 03:51:59. It campaigns but can
neither accept peers' PreVote connections nor get answered; ctrl-2's outbound PreVote connects
hang on the dead listener (zero connect-fail logs). The two restarted survivors can't exchange
votes → permanent round-4 stall. Release SEGV and gdb accept-hang = the same restart-time
corruption manifesting differently.
- **Restart-STABILITY bug, not a consensus-logic gap.** PreVote/eligibility/freshness (incl. the
5.3 fix) are correct; the node is simply I/O-dead after restart. Predates this session —
baseline `21e26a1` failed round 3–4 (requirements.md symptom 3). 5.2/5.3 sit in the
restart/failover path so 5.4 must rule them out during the backtrace, but they did not
introduce the instability.
- **Cannot backtrace in this sandbox.** Heisenbug: invisible under ASAN (no SEGV) AND under gdb
(becomes a hang, not a catchable signal); `core_pattern` is read-only (pipes to a non-running
systemd-coredump) so the release core is discarded. Needs a non-ASAN DEBUG build (-O0 -g) run
directly under gdb so the SIGSEGV is caught with symbols, or a host with a live coredump pipe.
## Net for 7.2
Elections + job safety are sound for every round that COMPLETES (2-round soak PASSES; 6-round
rounds 1–3 elect higher-term primaries; committed prefix byte-identical; zero job loss). The
≥6-round acceptance verdict is blocked by the restart-stability bug (bug-1281 / task 5.4),
reachable at round 4 (kill the up-to-date node → two restarted survivors). Spec-owned consensus
correctness (Phase-2/5.2/5.3) is unaffected.
## Harness wins
`start_node` now captures per-node stdout+stderr (committed 75243fb) — this exposed the SEGV.
The gdb-wrapper (`XCE_HA_BIN=<wrapper>` → `gdb -batch … --args <release ctrl>`) is the reusable
technique to catch a restarted node's fault once a debug+core env is available.

View file

@ -121,8 +121,8 @@
- _Note: UNIT clause DONE (RED→GREEN, commit bf88681; `test_ha_core_safety` 20 Tests / 0 Failures). The 6-round-soak clause shares 7.2's environment block — the soak re-run still stalls at round 4, but the cause is the bug-924 WAL-fdatasync wedge (a survivor jams its persist flusher on `fdatasync`→EINVAL and stops answering PreVotes), NOT the eligibility deadlock this task fixes. See `research.md` §"Task 5.3 + 7.2 re-run". The eligibility fix is a real latent-defect fix proven at unit level; its live confirmation rides on 7.2's env resolution._
- [ ] 5.4 Root-cause + fix the chaotic-restart controller SEGFAULT (NEW — the real 7.2 6-round blocker)
- _Root cause (task 7.2 on ~/tmp, `research.md` §"Task 7.2 on ~/tmp"; bug-1281): with per-node stderr finally captured (start_node had discarded it), a release `xce_controller` **Segmentation faults a few seconds after being restarted** into the churning cluster (during recovery→failover/PreVote). One restarted node down → cluster at 1-of-3 → no quorum → round-N "election stall." This is NOT consensus, NOT the in_catch_up deadlock (5.3 fixes that), NOT bug-924. Elections + job safety hold for every round that completes (2-round soak completes; 6-round rounds 1–3 elect)._
- Investigate: reproduce under a debug-non-ASAN build with core dumps enabled + gdb (the crash is ASAN-INVISIBLE — heisenbug, memory-layout/timing dependent — and the sandbox has no gdb and discards the core via a non-running systemd-coredump). Get a backtrace; classify whether the crashing site is in this spec's surface (election/5.2/5.3) or a recovery/rejoin/persist path (route upstream if so). Add a RED that reproduces the crash deterministically before fixing.
- _Root cause (task 7.2 on ~/tmp + gdb, `research.md` §"Task 7.2/5.4 — UNIFIED root cause"; bug-1281): a restart-STABILITY bug, UNIFIED via gdb. A restarted `xce_controller` becomes unable to participate at round 4 (kill the up-to-date node → two restarted survivors). Release build **SIGSEGVs** ("Segmentation fault (core dumped) --myname ctrl-3"); under gdb the SAME corruption manifests as a **wedged inbound-accept/iomgr path** (last accept logged while the election timer keeps firing) so the two survivors can't exchange PreVotes → permanent round-N stall. NOT a consensus-logic gap (PreVote/eligibility/freshness incl. 5.3 are correct; the node is I/O-dead after restart), NOT the in_catch_up deadlock, NOT bug-924. Predates this session (baseline 21e26a1 failed round 3–4). Elections + job safety hold for every round that completes (2-round soak PASSES; 6-round rounds 1–3 elect)._
- Investigate: the bug is a HEISENBUG — invisible under ASAN (no SEGV) AND under gdb (becomes an accept-hang, not a catchable signal), and the sandbox has no usable core (read-only core_pattern → non-running systemd-coredump). Reproduce under a **non-ASAN DEBUG build (-O0 -g) run directly under gdb** so the SIGSEGV is caught with symbols, OR a host with a live coredump pipeline. Get a backtrace; classify whether the crashing site is this spec's surface (election/5.2/5.3) or a recovery/rejoin/persist/iomgr path (route upstream if so). Diagnostic enablers already landed: per-node stderr capture (commit 75243fb) + the gdb-wrapper technique (`XCE_HA_BIN=<wrapper>` → `gdb -batch … --args <release ctrl>`). Add a RED that reproduces the crash deterministically before fixing.
- Observable completion: the chaotic-restart crash is root-caused to a concrete site with a backtrace, fixed (or routed to its owning spec), and a restarted node no longer segfaults across a ≥6-round chaotic soak
- _Requirements: 5.1, 6.2, 7.4, 8.3_
- _Boundary: Standby Reconvergence, Election Liveness_