feat(ha-heartbeat-committed-propagation): migrate last_known_committed_config_tag to _Atomic (task 1.1)
Mirror the sibling last_known_committed_lsn: all writes release, reads acquire. Prereq for a heartbeat-receive writer + the ctrlstatus reader to interact safely (Req 2.3). Pure access migration, no behavior change; controller suite 0 failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9817813f54
commit
f4327ce4f6
6 changed files with 20 additions and 14 deletions
|
|
@ -4,7 +4,7 @@
|
||||||
> The work runs along a single request path (atomic migration → libxce sender → primary stamp / standby consume → vote-floor verification → E2E), so tasks are sequential by design; no `(P)` markers. The two foundation tasks (1.1 atomic migration, 1.2 sender) touch independent files but are kept ordered for review simplicity. The HA-critical change is the standby consume + term-guard (task 2.2) and its vote-floor interaction (task 3.2).
|
> The work runs along a single request path (atomic migration → libxce sender → primary stamp / standby consume → vote-floor verification → E2E), so tasks are sequential by design; no `(P)` markers. The two foundation tasks (1.1 atomic migration, 1.2 sender) touch independent files but are kept ordered for review simplicity. The HA-critical change is the standby consume + term-guard (task 2.2) and its vote-floor interaction (task 3.2).
|
||||||
|
|
||||||
- [ ] 1. Foundation: atomic state + heartbeat sender
|
- [ ] 1. Foundation: atomic state + heartbeat sender
|
||||||
- [ ] 1.1 Migrate `last_known_committed_config_tag` to `_Atomic`
|
- [x] 1.1 Migrate `last_known_committed_config_tag` to `_Atomic`
|
||||||
- Change `ha->last_known_committed_config_tag` from a plain `uint64_t` to `_Atomic uint64_t` (`src/controller/include/ha.h`) and update its existing writers — `cluster_apply_ctrlhello_reply` (`ha_convergence.c`) and the election safe-commit path (`ha.c`) — to `atomic_store_explicit(..., release)`, and any reader (incl. the controller-status `ctrlstatus_fill`) to `atomic_load_explicit(..., acquire)`. Pure type/access migration — no behavior change.
|
- Change `ha->last_known_committed_config_tag` from a plain `uint64_t` to `_Atomic uint64_t` (`src/controller/include/ha.h`) and update its existing writers — `cluster_apply_ctrlhello_reply` (`ha_convergence.c`) and the election safe-commit path (`ha.c`) — to `atomic_store_explicit(..., release)`, and any reader (incl. the controller-status `ctrlstatus_fill`) to `atomic_load_explicit(..., acquire)`. Pure type/access migration — no behavior change.
|
||||||
- Observable: the controller suite builds clean and passes unchanged; every read/write of the field uses atomic accessors (no plain access remains).
|
- Observable: the controller suite builds clean and passes unchanged; every read/write of the field uses atomic accessors (no plain access remains).
|
||||||
- _Requirements: 2.3_
|
- _Requirements: 2.3_
|
||||||
|
|
|
||||||
|
|
@ -153,10 +153,12 @@ void ctrlstatus_fill(cluster_t* c, xce_ctrlstatus_t* st)
|
||||||
} else {
|
} else {
|
||||||
st->committedLsn = atomic_load_explicit(&ha->last_known_committed_lsn,
|
st->committedLsn = atomic_load_explicit(&ha->last_known_committed_lsn,
|
||||||
memory_order_acquire);
|
memory_order_acquire);
|
||||||
/* last_known_committed_config_tag is a plain (non-atomic) uint64_t
|
/* last_known_committed_config_tag is an _Atomic uint64_t; an acquire
|
||||||
* written under ha->lock; a best-effort aligned 8-byte read is correct
|
* load mirrors last_known_committed_lsn above and must NOT take the
|
||||||
* here and must NOT take the lock on this read path. */
|
* lock on this read path. */
|
||||||
st->committedConfigVersion = ha->last_known_committed_config_tag;
|
st->committedConfigVersion =
|
||||||
|
atomic_load_explicit(&ha->last_known_committed_config_tag,
|
||||||
|
memory_order_acquire);
|
||||||
}
|
}
|
||||||
st->appliedLsn = atomic_load_explicit(&ha->applied_lsn,
|
st->appliedLsn = atomic_load_explicit(&ha->applied_lsn,
|
||||||
memory_order_acquire);
|
memory_order_acquire);
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ typedef struct {
|
||||||
void * _Atomic elect_ctx; /* Req 2: ha_elect_ctx_t* during election; NULL otherwise */
|
void * _Atomic elect_ctx; /* Req 2: ha_elect_ctx_t* during election; NULL otherwise */
|
||||||
_Atomic uint64_t last_known_committed_lsn; /* Req 5: from primary heartbeats */
|
_Atomic uint64_t last_known_committed_lsn; /* Req 5: from primary heartbeats */
|
||||||
sem_t heartbeat_seen; /* Req 1: posted by heartbeat handler during CTRL_INIT */
|
sem_t heartbeat_seen; /* Req 1: posted by heartbeat handler during CTRL_INIT */
|
||||||
uint64_t last_known_committed_config_tag; /* Req 8: from primary heartbeats */
|
_Atomic uint64_t last_known_committed_config_tag; /* Req 8: from primary heartbeats */
|
||||||
_Atomic uint64_t committed_config_tag; /* Req 8.1: set on quorum ACK */
|
_Atomic uint64_t committed_config_tag; /* Req 8.1: set on quorum ACK */
|
||||||
/* ── New fields added by election-iomgr-refactor spec (Task 2.1) ── */
|
/* ── New fields added by election-iomgr-refactor spec (Task 2.1) ── */
|
||||||
void *client_ssl_ctx; /* SSL_CTX* (TLS_client_method); created in myapp_run */
|
void *client_ssl_ctx; /* SSL_CTX* (TLS_client_method); created in myapp_run */
|
||||||
|
|
@ -769,9 +769,9 @@ int handle_request_vote(ha_state_t *ha,
|
||||||
* *out_tag = max(local_config_tag, atomic committed_config_tag)
|
* *out_tag = max(local_config_tag, atomic committed_config_tag)
|
||||||
* *out_lsn = max(local_applied_lsn, atomic last_known_committed_lsn)
|
* *out_lsn = max(local_applied_lsn, atomic last_known_committed_lsn)
|
||||||
*
|
*
|
||||||
* Uses acquire loads of the atomic learned-committed fields; never reads the
|
* Uses acquire loads of the atomic learned-committed fields; never reads
|
||||||
* non-atomic last_known_committed_config_tag (data-race safe). Pure aside from
|
* last_known_committed_config_tag (a heartbeat-learned config tag, not the
|
||||||
* the atomic reads — no state mutation.
|
* vote freshness floor). Pure aside from the atomic reads — no state mutation.
|
||||||
*/
|
*/
|
||||||
void ha_vote_self_freshness(ha_state_t *ha,
|
void ha_vote_self_freshness(ha_state_t *ha,
|
||||||
uint64_t local_config_tag,
|
uint64_t local_config_tag,
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,8 @@ void cluster_apply_ctrlhello_reply(cluster_t *c, uint64_t committed_lsn,
|
||||||
{
|
{
|
||||||
atomic_store_explicit(&c->ha.last_known_committed_lsn, committed_lsn,
|
atomic_store_explicit(&c->ha.last_known_committed_lsn, committed_lsn,
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
c->ha.last_known_committed_config_tag = config_tag;
|
atomic_store_explicit(&c->ha.last_known_committed_config_tag, config_tag,
|
||||||
|
memory_order_release);
|
||||||
INFO("HA: CtrlHelloReply from primary: committed_lsn=%" PRIu64
|
INFO("HA: CtrlHelloReply from primary: committed_lsn=%" PRIu64
|
||||||
" config_tag=%" PRIu64, committed_lsn, config_tag);
|
" config_tag=%" PRIu64, committed_lsn, config_tag);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,7 +361,8 @@ static void test_ctrlstatus_standby_reports_learned_committed(void)
|
||||||
/* learned from the primary's heartbeats */
|
/* learned from the primary's heartbeats */
|
||||||
atomic_store_explicit(&c.ha.last_known_committed_lsn, 36,
|
atomic_store_explicit(&c.ha.last_known_committed_lsn, 36,
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
c.ha.last_known_committed_config_tag = 0x00C0FFEE0000CAFEULL;
|
atomic_store_explicit(&c.ha.last_known_committed_config_tag,
|
||||||
|
0x00C0FFEE0000CAFEULL, memory_order_release);
|
||||||
|
|
||||||
xce_ctrlstatus_t st;
|
xce_ctrlstatus_t st;
|
||||||
ctrlstatus_fill(&c, &st);
|
ctrlstatus_fill(&c, &st);
|
||||||
|
|
@ -393,7 +394,8 @@ static void test_ctrlstatus_standby_prelearn_committed_zero(void)
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
atomic_store_explicit(&c.ha.last_known_committed_lsn, 0,
|
atomic_store_explicit(&c.ha.last_known_committed_lsn, 0,
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
c.ha.last_known_committed_config_tag = 0;
|
atomic_store_explicit(&c.ha.last_known_committed_config_tag, 0,
|
||||||
|
memory_order_release);
|
||||||
/* even if the node-local primary marker had a stale value, off-primary it
|
/* even if the node-local primary marker had a stale value, off-primary it
|
||||||
* must NOT be surfaced — prove the learned (zero) value wins. */
|
* must NOT be surfaced — prove the learned (zero) value wins. */
|
||||||
atomic_store_explicit(&c.persist.committed_lsn, 77, memory_order_release);
|
atomic_store_explicit(&c.persist.committed_lsn, 77, memory_order_release);
|
||||||
|
|
@ -429,7 +431,8 @@ static void test_ctrlstatus_primary_reports_own_committed(void)
|
||||||
/* stale learned values must NOT be surfaced on a primary */
|
/* stale learned values must NOT be surfaced on a primary */
|
||||||
atomic_store_explicit(&c.ha.last_known_committed_lsn, 9,
|
atomic_store_explicit(&c.ha.last_known_committed_lsn, 9,
|
||||||
memory_order_release);
|
memory_order_release);
|
||||||
c.ha.last_known_committed_config_tag = 0x1111ULL;
|
atomic_store_explicit(&c.ha.last_known_committed_config_tag, 0x1111ULL,
|
||||||
|
memory_order_release);
|
||||||
|
|
||||||
xce_ctrlstatus_t st;
|
xce_ctrlstatus_t st;
|
||||||
ctrlstatus_fill(&c, &st);
|
ctrlstatus_fill(&c, &st);
|
||||||
|
|
|
||||||
|
|
@ -724,7 +724,7 @@ static void test_ctrlhello_reply_intake_stores_and_reconciles(void)
|
||||||
atomic_load(&g_dc.ha.last_known_committed_lsn),
|
atomic_load(&g_dc.ha.last_known_committed_lsn),
|
||||||
"CtrlHelloReply intake must store last_known_committed_lsn (existing)");
|
"CtrlHelloReply intake must store last_known_committed_lsn (existing)");
|
||||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(reported_tag,
|
TEST_ASSERT_EQUAL_UINT64_MESSAGE(reported_tag,
|
||||||
g_dc.ha.last_known_committed_config_tag,
|
atomic_load(&g_dc.ha.last_known_committed_config_tag),
|
||||||
"CtrlHelloReply intake must store the config tag (existing)");
|
"CtrlHelloReply intake must store the config tag (existing)");
|
||||||
|
|
||||||
/* Assert (intake integration): the intake also TRIGGERED reconciliation —
|
/* Assert (intake integration): the intake also TRIGGERED reconciliation —
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue