feat(ha-heartbeat-committed-propagation): add reconcile-free ha_observe_heartbeat_committed (task 2.1)

Strict-monotonic updater for a standby's learned committed position: advance
last_known_committed_lsn only when strictly greater (zero/lower no-op), set the
config tag together (tag-before-lsn release ordering for reader safety). No
ha_rejoin_reconcile, no truncation, no other mutation (Req 2.1-2.3, 3.1, 3.3).
4 revert-detecting unit tests; controller suite 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-06-20 11:52:18 +00:00
parent 282a82ff7e
commit 8bfa861be2
6 changed files with 273 additions and 1 deletions

View file

@ -4919,6 +4919,46 @@ void ha_on_heartbeat_received(ha_state_t *ha)
}
}
/*
* ha_observe_heartbeat_committed: reconcile-free monotonic learn of the
* primary's committed position from a heartbeat (ha-heartbeat-committed-
* propagation, Req 2.1, 2.2, 2.3, 3.1, 3.3).
*
* Single-receiver-thread path (controller request handling), so a relaxed
* compare-then-store is sufficient. The config tag is written BEFORE the LSN
* (both release-ordered) so a concurrent acquire-load reader that observes the
* advanced LSN never pairs it with a stale tag.
*
* Advisory only: NO ha_rejoin_reconcile / NO WAL truncation / NO change to any
* produced/persisted committed, role, term, or applied state. This is the
* explicit distinction from cluster_apply_ctrlhello_reply (which truncates).
*/
void ha_observe_heartbeat_committed(cluster_t *c, uint64_t committed_lsn,
uint64_t committed_config_tag)
{
if (!c) return;
ha_state_t *ha = &c->ha;
uint64_t cur = atomic_load_explicit(&ha->last_known_committed_lsn,
memory_order_acquire);
/* Monotonic no-op: a zero (absent/liveness-only) or non-advancing LSN must
* leave the learned position — and its config tag — untouched (Req 2.2). */
if (committed_lsn == 0 || committed_lsn <= cur)
return;
/* Tag moves with the LSN, never independently regressed (Req 2.3): publish
* the tag first so the LSN's release store carries the matching tag. */
atomic_store_explicit(&ha->last_known_committed_config_tag,
committed_config_tag, memory_order_release);
atomic_store_explicit(&ha->last_known_committed_lsn, committed_lsn,
memory_order_release);
/* Reconcile-free: nothing else is touched (Req 3.1, 3.3). */
tlog(TLOG_DEBUG3,
"HA: learned committed_lsn=%" PRIu64 " config_tag=%" PRIu64
" from heartbeat (was %" PRIu64 ")",
committed_lsn, committed_config_tag, cur);
}
void ha_disarm_heartbeat_timeout_vtask(ha_state_t *ha)
{
(void)ha;