#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 #include #include #include #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 */