feat(ha-heartbeat-committed-propagation): standby OP_nop consume branch + term-guard (task 2.2)

Append a committed-consume branch to the OP_nop handler (existing liveness/reply
byte-unchanged): ha_consume_heartbeat_committed applies the non-primary gate
(3.4) + stale-term guard (accept hb->term >= ha->term, 3.2), then delegates to
the monotonic ha_observe_heartbeat_committed (1.2). Absent body / zero / failed
gate are no-ops (4.2). No vote-floor edit — safety is the term-guard. 4 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-06-20 12:02:15 +00:00
parent 8bfa861be2
commit 90e7179a89
5 changed files with 200 additions and 1 deletions

View file

@ -4959,6 +4959,38 @@ void ha_observe_heartbeat_committed(cluster_t *c, uint64_t committed_lsn,
committed_lsn, committed_config_tag, cur);
}
/*
* ha_consume_heartbeat_committed: standby OP_nop consume branch — the
* non-primary gate (Req 3.4) + the stale-term guard (Req 3.2), then the
* monotonic observe (Req 1.2). Reconcile-free; updates nothing on a NULL body,
* a failed gate, or a zero committed value (Req 4.2). Pure additive helper —
* the caller runs the unchanged liveness/reply path first (Req 4.1).
*/
void ha_consume_heartbeat_committed(cluster_t *c, const heartbeat_msg_t *hb)
{
if (!c || !hb)
return;
/* (3.4) A primary uses its own authoritative committed — never an inbound
* heartbeat value. Only non-primary nodes consume. */
ctrl_role_t role =
(ctrl_role_t)atomic_load_explicit(&c->ha.role, memory_order_acquire);
if (role == CTRL_PRIMARY)
return;
/* (3.2) Term-guard: a heartbeat from a primary whose term is behind ours is
* stale/partitioned — reject its committed so it cannot inflate the
* vote-freshness floor. Equal-or-newer term passes. */
uint64_t myterm = atomic_load_explicit(&c->ha.term, memory_order_acquire);
if (hb->term < myterm)
return;
/* (1.2) Monotonic, reconcile-free learn. Zero/non-advancing LSN no-ops
* inside the observe (Req 4.2). */
ha_observe_heartbeat_committed(c, hb->committed_lsn,
hb->committed_config_tag);
}
void ha_disarm_heartbeat_timeout_vtask(ha_state_t *ha)
{
(void)ha;