feat(ha-config-bootstrap): complete quorum election with mutex safety (task 4.3)
- am_i_primary_quorum(): per-peer pthreads, connect_ssl_raw + OP_requestVote - pthread_mutex_t lock on ha_elect_ctx_t guards votes_granted/n_replied/max_resp - done_posted guard in ha_elect_evaluate_and_signal prevents double sem_post - Replace am_i_primary() with am_i_primary_quorum() in myapp.c - Delete ha_check_priority_stepdown and ha_check_heartbeat_nocluster - INFO logs: election outcome (Req 10.1), FSM transitions (Req 10.5) - 24 election tests + 24 startup tests, 0 failures
This commit is contained in:
parent
0f88d5d32a
commit
0c9c720b03
4 changed files with 390 additions and 98 deletions
|
|
@ -68,30 +68,54 @@ int myapp_run(myconf_t* myconf)
|
|||
vtaskmgr_init(&(c.vtaskmgr), nvcpus);
|
||||
}
|
||||
|
||||
/* Election: determine role before loading config.
|
||||
* Capture result once — am_i_primary() probes peers and is expensive. */
|
||||
int is_primary = am_i_primary(&c);
|
||||
if (is_primary) {
|
||||
/* Primary: load config from conf/current (FATAL if missing) */
|
||||
rc = load_primary_config(&c);
|
||||
if (rc != 0) {
|
||||
cluster_fini(&c);
|
||||
myconf_fini(myconf);
|
||||
return rc;
|
||||
/* Election: quorum-based role determination (Req 2.1, 2.9).
|
||||
* Build the peer pointer array (excluding self) for am_i_primary_quorum().
|
||||
* peers_cfg.candidates includes this node; n_peers = candidates_count.
|
||||
* my_priority is the index of this node in the candidates list. */
|
||||
{
|
||||
const xce_cluster_config_t *pcfg = &myconf->peers_cfg;
|
||||
int n_peers = (pcfg->candidates_count > 0) ? pcfg->candidates_count : 1;
|
||||
|
||||
/* Build array of pointers to other candidates (excluding self by index) */
|
||||
xce_candidate_t *peer_ptrs[MAX_HA_CANDIDATES];
|
||||
int n_other = 0;
|
||||
for (int i = 0; i < pcfg->candidates_count; i++) {
|
||||
if (i != c.ha.my_priority)
|
||||
peer_ptrs[n_other++] = (xce_candidate_t *)&pcfg->candidates[i];
|
||||
}
|
||||
/* Record election outcome so cluster_init_phase2() sees CTRL_PRIMARY.
|
||||
* ha_init() unconditionally defaults ha->role to CTRL_STANDBY. */
|
||||
atomic_store_explicit(&c.ha.role, (int32_t)CTRL_PRIMARY,
|
||||
memory_order_release);
|
||||
} else {
|
||||
/* Standby: fetch config from primary before Phase 2 */
|
||||
rc = fetch_config_from_primary(&c);
|
||||
if (rc != 0) {
|
||||
cluster_fini(&c);
|
||||
myconf_fini(myconf);
|
||||
return rc;
|
||||
|
||||
uint64_t my_tag = myconf->config_version_tag;
|
||||
uint64_t my_lsn = (uint64_t)atomic_load_explicit(&c.ha.applied_lsn,
|
||||
memory_order_acquire);
|
||||
|
||||
ctrl_role_t elected = am_i_primary_quorum(
|
||||
&c.ha,
|
||||
n_peers,
|
||||
(n_other > 0) ? peer_ptrs : NULL,
|
||||
my_tag,
|
||||
my_lsn);
|
||||
|
||||
if (elected == CTRL_PRIMARY) {
|
||||
/* Primary: load config from conf/current (FATAL if missing) */
|
||||
rc = load_primary_config(&c);
|
||||
if (rc != 0) {
|
||||
cluster_fini(&c);
|
||||
myconf_fini(myconf);
|
||||
return rc;
|
||||
}
|
||||
/* am_i_primary_quorum logs FSM transition; set role explicitly */
|
||||
atomic_store_explicit(&c.ha.role, (int32_t)CTRL_PRIMARY,
|
||||
memory_order_release);
|
||||
} else {
|
||||
/* Standby: fetch config from primary before Phase 2 */
|
||||
rc = fetch_config_from_primary(&c);
|
||||
if (rc != 0) {
|
||||
cluster_fini(&c);
|
||||
myconf_fini(myconf);
|
||||
return rc;
|
||||
}
|
||||
/* Role stays CTRL_STANDBY — already set by ha_init(). */
|
||||
}
|
||||
/* Role stays CTRL_STANDBY — already set by ha_init(). */
|
||||
}
|
||||
|
||||
/* Req 4.4: warn if config file contains deprecated 'vcpus' key */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue