feat(ha-config-bootstrap): add election types and new fields to ha.h (task 1.2)
- Add CTRL_INIT=2, CTRL_ELECTING=3 to ctrl_role_t - Add ha_vote_entry_t per-peer vote tracking struct - Add ha_elect_ctx_t with C99 flexible array votes[] - Add 8 new ha_state_t fields: voted_for_term/name, primary_ctrl_fd, defer_config_fetch, elect_ctx (void * _Atomic), last_known_committed_lsn, heartbeat_seen (sem_t), last_known_committed_config_tag - Declare ha_persist_term(), bootstrap_config_if_needed(), xce_probe_config_tag() - Fix: use void * _Atomic (postfix) not _Atomic void * (prefix) for atomic pointer-to-void — C11 §6.7.2.4 prohibits _Atomic on incomplete type void
This commit is contained in:
parent
6adb7e806f
commit
560d8fa1c8
2 changed files with 75 additions and 4 deletions
|
|
@ -19,6 +19,8 @@
|
|||
*/
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
|
@ -51,10 +53,45 @@ void ha_seed_jitter(uint64_t *seed, int my_priority);
|
|||
|
||||
/* ── Role enum ─────────────────────────────────────────────── */
|
||||
typedef enum {
|
||||
CTRL_PRIMARY = 0,
|
||||
CTRL_STANDBY = 1
|
||||
CTRL_PRIMARY = 0,
|
||||
CTRL_STANDBY = 1,
|
||||
CTRL_INIT = 2, /* Startup: listener open, awaiting heartbeat or election timeout */
|
||||
CTRL_ELECTING = 3 /* Running quorum election via OP_requestVote */
|
||||
} ctrl_role_t;
|
||||
|
||||
/* ── Election types ────────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* ha_vote_entry_t: per-peer state accumulated by the election candidate.
|
||||
* One entry per peer in ha_elect_ctx_t.votes[] (flexible array).
|
||||
*/
|
||||
typedef struct {
|
||||
xce_candidate_t *peer; /* Peer being solicited */
|
||||
uint64_t my_term; /* Term sent in OP_requestVote */
|
||||
uint64_t my_tag; /* config_tag sent in request */
|
||||
uint64_t my_lsn; /* WAL LSN sent in request */
|
||||
int vote_granted; /* 1 if peer granted vote */
|
||||
int replied; /* 1 if peer replied (granted or denied) */
|
||||
uint64_t peer_tag; /* peer's config_tag from reply */
|
||||
uint64_t peer_lsn; /* peer's WAL LSN from reply */
|
||||
} ha_vote_entry_t;
|
||||
|
||||
/*
|
||||
* ha_elect_ctx_t: shared context for one quorum election round.
|
||||
* Allocated as: calloc(1, sizeof(*ctx) + (n_peers-1) * sizeof(ctx->votes[0]))
|
||||
* Stored in ha_state_t.elect_ctx (void * _Atomic) during election; NULL otherwise.
|
||||
*/
|
||||
typedef struct {
|
||||
int n_peers; /* Total peer count (excluding self) */
|
||||
int votes_granted; /* Votes received; starts at 1 (self-vote) */
|
||||
int n_replied; /* Number of peers that replied */
|
||||
uint64_t max_resp_tag; /* Highest config_tag seen in any reply */
|
||||
uint64_t max_resp_lsn; /* Highest WAL LSN seen among max_resp_tag peers */
|
||||
int result; /* 1=primary, 0=standby, <0=error */
|
||||
sem_t done; /* Startup thread blocks; dispatch posts when done */
|
||||
ha_vote_entry_t votes[]; /* C99 flexible array: n_peers-1 entries */
|
||||
} ha_elect_ctx_t;
|
||||
|
||||
/* ── HA configuration ──────────────────────────────────────── */
|
||||
|
||||
/* Default values used by cluster_init(). Tuned for a LAN with ≤5ms RTT. */
|
||||
|
|
@ -108,6 +145,15 @@ typedef struct {
|
|||
void *ssl_ctx; /* SSL_CTX* cast; avoids pulling openssl headers here */
|
||||
uint64_t jitter_seed; /* xorshift64 PRNG state for election jitter */
|
||||
zarray_t *standby_endpoints; /* connected standby controller endpoints for config push */
|
||||
/* ── New fields added by ha-config-bootstrap spec ──────── */
|
||||
uint64_t voted_for_term; /* Req 9: term of last granted vote */
|
||||
char voted_for_name[XCE_MAXNAME_LEN]; /* Req 9: candidate of last granted vote */
|
||||
int primary_ctrl_fd; /* Req 6: persistent channel fd (-1 = none) */
|
||||
bool defer_config_fetch; /* Req 3: tag=0 node defers fetch to post-election */
|
||||
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 */
|
||||
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 */
|
||||
} ha_state_t;
|
||||
|
||||
/* ── Public API ────────────────────────────────────────────── */
|
||||
|
|
@ -231,6 +277,31 @@ void start_heartbeat_listener(cluster_t *c);
|
|||
*/
|
||||
void stop_heartbeat_listener(cluster_t *c);
|
||||
|
||||
/* ── HA Config Bootstrap & Term Durability (ha-config-bootstrap spec) ── */
|
||||
|
||||
/*
|
||||
* ha_persist_term: write ha-term.bin atomically to {workdir}/ha-term.bin.
|
||||
* Uses write + rename for durability (existing pattern in the codebase).
|
||||
* Must be called before any vote is granted or term is incremented.
|
||||
*/
|
||||
void ha_persist_term(ha_state_t *ha, const char *workdir);
|
||||
|
||||
/*
|
||||
* bootstrap_config_if_needed: pre-election config probe.
|
||||
* If the local config tag is 0 and at least one peer has config, sets
|
||||
* ha->defer_config_fetch = true and returns without fetching.
|
||||
* The actual fetch is deferred to post-election (after a winner is known).
|
||||
* Returns 0 on success, -errno on unrecoverable error.
|
||||
*/
|
||||
int bootstrap_config_if_needed(cluster_t *c);
|
||||
|
||||
/*
|
||||
* xce_probe_config_tag: single-RTT OP_probeConfigTag RPC to one peer.
|
||||
* Fills *tag_out with the peer's current config_tag on success.
|
||||
* Returns 0 on success, -errno on connection/protocol error.
|
||||
*/
|
||||
int xce_probe_config_tag(const char *host, uint16_t port, uint64_t *tag_out);
|
||||
|
||||
/* ── P3-6: Test-only failover shim ────────────────────────────── */
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue