myxce/src/controller/ha.h
Rongsong Shen a289edb777 feat: ha_state_t, ha_config_t, ha_init/fini, role enum + config validation (P1-2)
- Full ha.h: ctrl_role_t enum (CTRL_PRIMARY/CTRL_STANDBY), ha_config_t,
  ha_state_t with _Atomic fields (term, role, applied_lsn, promotion
  counters, watchdog_stop), pthread_mutex_t lock, and public API.
- endpoint_t* omitted from ha_state_t — including netio.h would pull
  OpenSSL headers and risk circular deps; watchdog (P1-3) includes
  netio.h directly where needed.
- ha_init validates: heartbeat_interval_ms >= 2000, heartbeat_timeout_ms
  >= heartbeat_interval_ms, election_timeout_ms >= 500; returns -EINVAL
  on bad config; zeros struct and sets initial role to CTRL_STANDBY.
- ha_fini destroys mutex; watchdog thread join deferred to P1-3.
- am_i_primary / ha_promote_to_primary / ha_demote_to_standby are
  no-op stubs; cluster_t->ha field not yet wired (P1-5), so these avoid
  incomplete-type dereferences until then.
- TDD: 5 new tests in test_ha_role.c confirmed RED before implementation,
  all 5 GREEN after; all prior controller tests remain green.
2026-05-07 14:08:15 +00:00

100 lines
4.4 KiB
C

#ifndef HA_H
#define HA_H
/*
* XCE Controller HA — role management and election (P1).
*
* P1-1 audit: the existing create_ssl_context() + iomgr_set_ssl_ctx() path
* in cluster.c (line 559, set at line 583) covers all controller-to-controller SSL.
* HA listeners (heartbeat, election probe, replication accept) reuse the same
* ssl_ctx already installed on c->iomgr.
* No new TLS infrastructure is needed — XCE runs on a trusted management LAN.
* Peer identity is validated by host:port match against the --peers candidate list.
* See design doc Part 5.9 and memory: Persistence & HA Trusted LAN Decision.
*
* P1-2: full struct definitions and API.
* endpoint_t* (primary_ep) is intentionally omitted here — including netio.h
* would pull in heavy OpenSSL headers and create circular dependency risks.
* The watchdog (P1-3) will include netio.h directly where it needs endpoint_t.
*/
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <time.h>
#include "config.h" /* XCE_MAXNAME_LEN, MAX_STANDBYS, etc. */
/* Forward declaration — avoids circular include with cluster_def.h */
typedef struct cluster_s cluster_t;
/* ── Role enum ─────────────────────────────────────────────── */
typedef enum {
CTRL_PRIMARY = 0,
CTRL_STANDBY = 1
} ctrl_role_t;
/* ── HA configuration ──────────────────────────────────────── */
typedef struct {
uint32_t heartbeat_interval_ms; /* Primary heartbeat send interval (min 2000 ms) */
uint32_t heartbeat_timeout_ms; /* Standby timeout before promoting (>= interval) */
uint32_t election_timeout_ms; /* Election probe shared deadline (min 500 ms) */
uint32_t probe_connect_timeout_ms; /* Per-probe TCP connect timeout */
uint32_t max_promotion_retries; /* Max consecutive failed promotion attempts */
} ha_config_t;
/* ── HA runtime state (embedded in cluster_t as `ha_state_t ha` in P1-5) ── */
typedef struct {
_Atomic uint64_t term; /* Election term — fencing token */
_Atomic int32_t role; /* Cast to ctrl_role_t at access sites */
int my_priority; /* Index in --peers list (lower = higher priority) */
_Atomic uint64_t applied_lsn; /* Standby: last applied WAL LSN */
char primary_host[XCE_MAXNAME_LEN]; /* Hostname of current primary */
uint32_t primary_port; /* Port of current primary */
struct timespec last_heartbeat; /* CLOCK_MONOTONIC timestamp of last heartbeat */
_Atomic int promotion_failed;
_Atomic int promotion_retry_count;
_Atomic int in_catch_up; /* Non-zero while processing historical WAL entries */
_Atomic int failover_requested; /* Set by SIGUSR1 handler; watchdog executes */
int hooks_registered; /* Idempotency guard for xce_add_hook calls */
uint32_t last_received_crc; /* Chained CRC of last received replicated entry */
ha_config_t config;
pthread_mutex_t lock; /* Protects primary_host/port + last_heartbeat */
/* Watchdog thread — started in P1-3 */
_Atomic int watchdog_stop;
pthread_t watchdog_thread;
} ha_state_t;
/* ── Public API ────────────────────────────────────────────── */
/*
* ha_init: zero-initialise *ha, validate cfg, store config and my_priority.
* Returns 0 on success, -EINVAL on bad config.
* Initial role is always CTRL_STANDBY.
*/
int ha_init(ha_state_t *ha, const ha_config_t *cfg, int my_priority);
/*
* ha_fini: destroy mutex. Watchdog thread join added in P1-3.
*/
void ha_fini(ha_state_t *ha);
/*
* am_i_primary: stub — priority-0 node is primary.
* Full election probing added in P1-3.
*/
int am_i_primary(cluster_t *c);
/*
* ha_promote_to_primary: stub — atomically sets role to PRIMARY.
* Full promotion sequence (hooks, WAL flush, peer notify) added in P1-3a.
*/
int ha_promote_to_primary(cluster_t *c);
/*
* ha_demote_to_standby: stub — atomically sets role to STANDBY.
* Full demotion sequence added in P1-3a.
*/
int ha_demote_to_standby(cluster_t *c);
#endif /* HA_H */