fix(controller): probe a late-joining standby at once so its catch-up starts without waiting a heartbeat (bug-1913)

After an election where the vote connections are not promoted, both
standbys register later via on_standby_connected; the own-term no-op was
appended before any standby existed, and catch-up waited for heartbeat-
driven state reports (config push one 3 s cycle, WAL stream the next), so
the no-op committed ~6 s late and every client write in that window
failed -110.

ha_send_catchup_probe() sends the ordinary heartbeat request (same fields
as primary_heartbeat_fn; encode + enqueue only; primary only) at
registration and once after the standby's first accepted config-push ACK,
so the decision loop's next action follows at ~RTT. The decision loop,
its one-action-per-report pacing, commit/majority rules and the dispatch
gate are unchanged; WAL streaming still runs only on the vtask. The
one-shot arm is cleared on disconnect and on every become_leader /
become_follower.

Tests: test_late_joiner_catchup_kick.c (6). Controller suite 1285/0.
Live: catch-up starts 0-6 ms after a late registration (was up to ~6 s);
/xce-test-ha sequential 20 rounds, chaotic 6 rounds and sequential 8
rounds under 40-spinner load all PASS with zero job loss.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-03 23:18:10 +00:00
parent 6d9ef3d024
commit 2aefa939db
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
5 changed files with 485 additions and 0 deletions

View file

@ -177,6 +177,7 @@ controller_unit_test_src = [
'testsuite/test_jobdispatch_no_double_notify.c',
'testsuite/test_worker_resync.c',
'testsuite/test_standby_state_peer_idx.c',
'testsuite/test_late_joiner_catchup_kick.c',
'testsuite/test_upgrade_snapshot_restore.c',
'testsuite/test_upgrade_snapshot_apply.c',
'testsuite/test_bug857_regression.c',