fix(xce-test-ha): bug-1422 verify_no_loss false-positive after election
40-round sequential HA soak FAIL at round 36: verify_no_loss reported "0/111 jobs confirmed present" right after a fresh election, even though the new primary's own log confirmed it stayed CTRL_PRIMARY throughout and successfully committed the new jobs. Not a real data-safety bug. Root cause: the client's failover retry loop (client_failover.c) can exhaust its 20-attempt budget during a rapid post-election retry storm without ever reaching the new primary, and the budget-exhaustion path only overrides the surfaced error for RETRY_BACKOFF exhaustion (-E_XCE_NOT_READY) — not for RETRY_NOW exhaustion, so a stray E_XCE_STANDBY from a standby the retry loop happened to land on gets surfaced as if it were a hard, permanent rejection. verify_no_loss() did a single one-shot check with no retry at the driver level, unlike the job-submit loop in the same file which already retries on this exact "primary likely just promoted" condition. Adds a bounded retry (3 attempts, 2s settle sleep) around the whole verify_no_loss check, mirroring the existing submit-retry rationale. Left client_failover.c untouched (battle-tested library, many other consumers) — fixing at the test-driver level is safer and fully local. Validated: 40-round sequential (123 jobs, zero loss, all 3 nodes led) and 40-round chaotic (123 jobs, zero committed divergence, zero committed job loss) soaks both PASS.
This commit is contained in:
parent
327dbb9bb3
commit
016c598f07
1 changed files with 30 additions and 12 deletions
|
|
@ -49,7 +49,7 @@
|
|||
# XCE_HA_TOP deploy dir (default /home/claude/tmp/xce-ha-deploy)
|
||||
# XCE_HA_DATADIR config/certs source (default <repo>/ha/data)
|
||||
# XCE_HA_MODE sequential | chaotic | force-snapshot (default sequential)
|
||||
# XCE_HA_ROUNDS kill/restart rounds (default 6, clamp 5..10)
|
||||
# XCE_HA_ROUNDS kill/restart rounds (default 6, clamp 5..50)
|
||||
# XCE_HA_JOBS jobs per submit batch (default 3)
|
||||
# XCE_HA_WAITMS wait-primary budget (default 30000)
|
||||
# MAX_WAL_RECORDS --max-wal-records passed to every node (default: unset
|
||||
|
|
@ -134,14 +134,14 @@ DEPLOY="$REPO/src/tools/deploy-ha.sh"
|
|||
# Rounds precedence: bare-integer positional (ROUNDS_CLI) > $XCE_HA_ROUNDS > 6.
|
||||
# Default soak floor stays 5; an EXPLICIT small request (CLI or env) is honored
|
||||
# down to 2 so a short detection/smoke run (task 1.5: `--chaotic 2 <dir>`) can
|
||||
# reach its verdict quickly. Upper bound clamped to 10.
|
||||
# reach its verdict quickly. Upper bound clamped to 50.
|
||||
ROUNDS="${ROUNDS_CLI:-${XCE_HA_ROUNDS:-6}}"
|
||||
if [ -n "$ROUNDS_CLI" ] || [ -n "${XCE_HA_ROUNDS:-}" ]; then
|
||||
[ "$ROUNDS" -lt 2 ] && ROUNDS=2 # explicit request: honor down to 2
|
||||
else
|
||||
[ "$ROUNDS" -lt 5 ] && ROUNDS=5 # default soak floor
|
||||
fi
|
||||
[ "$ROUNDS" -gt 10 ] && ROUNDS=10
|
||||
[ "$ROUNDS" -gt 50 ] && ROUNDS=50
|
||||
NJOBS="${XCE_HA_JOBS:-3}"
|
||||
WAITMS="${XCE_HA_WAITMS:-30000}"
|
||||
# raft-ha-core-safety task 5.10a / ha-entity-snapshots task 7.1: force-snapshot
|
||||
|
|
@ -493,8 +493,17 @@ quit"
|
|||
# Judged by OUTPUT, not exit code (ASAN leaks pollute the exit code): require one
|
||||
# "expect-job: ok" per tracked job and zero "not found". Returns non-zero on any
|
||||
# missing job or short count.
|
||||
#
|
||||
# Retries the WHOLE check up to 3 times on failure (same "primary likely just
|
||||
# promoted" rationale as the submit-retry loop above): a fresh election can hit
|
||||
# a narrow client-side race where libxce's failover retry budget (20 attempts,
|
||||
# client_failover.c) is exhausted by a rapid-fire retry storm that unluckily
|
||||
# never lands on the new primary before budget runs out, surfacing a stale
|
||||
# last-seen status (e.g. E_XCE_STANDBY from a peer standby) that looks like a
|
||||
# hard rejection but is actually transient churn. A job still missing after a
|
||||
# real settle window + 2 retries is a genuine loss, not this race.
|
||||
verify_no_loss() {
|
||||
local script out want oks
|
||||
local script out want oks attempt
|
||||
want="${#ALL_JOBIDS[@]}"
|
||||
script="set masters $MASTERS
|
||||
set myname verify
|
||||
|
|
@ -507,19 +516,28 @@ expect-job $jid"
|
|||
done
|
||||
script+="
|
||||
quit"
|
||||
out="$(printf '%s\n' "$script" | xtest)"
|
||||
for attempt in 1 2 3; do
|
||||
out="$(printf '%s\n' "$script" | xtest)"
|
||||
if ! echo "$out" | grep -q "not found"; then
|
||||
oks="$(echo "$out" | grep -c "expect-job: ok")"
|
||||
if [ "$oks" -eq "$want" ]; then
|
||||
log "verified all $want jobs present"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if [ "$attempt" -lt 3 ]; then
|
||||
log "verify_no_loss attempt $attempt/3: recheck failed (likely post-election client retry-storm race) — settling 2s and retrying whole check"
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
if echo "$out" | grep -q "not found"; then
|
||||
echo "$out" | grep "not found" >&2
|
||||
return 1
|
||||
fi
|
||||
oks="$(echo "$out" | grep -c "expect-job: ok")"
|
||||
if [ "$oks" -ne "$want" ]; then
|
||||
echo "verify_no_loss: only $oks/$want jobs confirmed present" >&2
|
||||
echo "$out" | tail -8 >&2
|
||||
return 1
|
||||
fi
|
||||
log "verified all $want jobs present"
|
||||
return 0
|
||||
echo "verify_no_loss: only $oks/$want jobs confirmed present after 3 attempts" >&2
|
||||
echo "$out" | tail -8 >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
# Source the walcompare committed-boundary from the LIVE, authoritative
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue