fix: resolve probe_ctx_t type tag, add DEFAULT_MAX_WAL_RECORDS macro, fix atomic type consistency, move ha_seed_jitter to ha.c, add API doc comments

This commit is contained in:
Rongsong Shen 2026-05-13 14:27:07 +00:00
parent 8a9fce1be4
commit 1c4a103a66
5 changed files with 67 additions and 37 deletions

View file

@ -2,18 +2,47 @@
#include "ha.h" #include "ha.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include <unistd.h>
/* Circular-include safety: cluster_def.h includes ha.h (include-guarded), /* Circular-include safety: cluster_def.h includes ha.h (include-guarded),
* and ha.h only forward-declares cluster_t — no cycle. */ * and ha.h only forward-declares cluster_t — no cycle. */
#include "xce/cluster_def.h" #include "xce/cluster_def.h"
#include "persist.h" #include "persist.h"
/* ── PRNG seeding ───────────────────────────────────────────── */
void ha_seed_jitter(uint64_t *seed, int my_priority)
{
uint8_t buf[8] = {0};
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
ssize_t n = 0;
while (n < 8) {
ssize_t r = read(fd, buf + n, (size_t)(8 - n));
if (r <= 0) break;
n += r;
}
close(fd);
if (n == 8) {
memcpy(seed, buf, 8);
if (*seed != 0) return;
}
}
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
*seed = (uint64_t)ts.tv_nsec
^ ((uint64_t)my_priority << 56)
^ ((uint64_t)getpid() << 16);
}
int ha_init(ha_state_t *ha, const ha_config_t *cfg, int my_priority) int ha_init(ha_state_t *ha, const ha_config_t *cfg, int my_priority)
{ {
if (!ha || !cfg) if (!ha || !cfg)

View file

@ -18,15 +18,13 @@
* The watchdog (P1-3) will include netio.h directly where it needs endpoint_t. * The watchdog (P1-3) will include netio.h directly where it needs endpoint_t.
*/ */
#include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include "config.h" /* XCE_MAXNAME_LEN, MAX_STANDBYS, etc. */ #include "config.h" /* XCE_MAXNAME_LEN, MAX_STANDBYS, etc. */
#include "xce/cluster.h" /* xce_candidate_t */
#include "zarray.h" #include "zarray.h"
/* Forward declaration — avoids circular include with cluster_def.h */ /* Forward declaration — avoids circular include with cluster_def.h */
@ -44,30 +42,12 @@ static inline uint64_t xorshift64_next(uint64_t *state)
return x; return x;
} }
static inline void ha_seed_jitter(uint64_t *seed, int my_priority) /*
{ * ha_seed_jitter: seed the xorshift64 PRNG from /dev/urandom (8 bytes).
uint8_t buf[8] = {0}; * Falls back to CLOCK_MONOTONIC ^ priority ^ pid if urandom is unavailable.
int fd = open("/dev/urandom", O_RDONLY); * Not inline — contains syscalls (open/read/close/clock_gettime/getpid).
if (fd >= 0) { */
ssize_t n = 0; void ha_seed_jitter(uint64_t *seed, int my_priority);
while (n < 8) {
ssize_t r = read(fd, buf + n, (size_t)(8 - n));
if (r <= 0) break;
n += r;
}
close(fd);
if (n == 8) {
memcpy(seed, buf, 8);
if (*seed != 0) return;
}
}
/* Fallback: monotonic clock + priority + pid mixing */
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
*seed = (uint64_t)ts.tv_nsec
^ ((uint64_t)my_priority << 56)
^ ((uint64_t)getpid() << 16);
}
/* ── Role enum ─────────────────────────────────────────────── */ /* ── Role enum ─────────────────────────────────────────────── */
typedef enum { typedef enum {
@ -96,11 +76,8 @@ typedef struct {
uint32_t election_priority_step_ms; /* extra ms per priority level (default 500) */ uint32_t election_priority_step_ms; /* extra ms per priority level (default 500) */
} ha_config_t; } ha_config_t;
/* Forward declaration for election probe context */
struct xce_candidate_s;
typedef struct { typedef struct {
struct xce_candidate_s *candidate; /* peer to probe */ xce_candidate_t *candidate; /* peer to probe */
void *ssl_ctx; /* SSL_CTX* (void* to avoid openssl header) */ void *ssl_ctx; /* SSL_CTX* (void* to avoid openssl header) */
uint32_t timeout_ms; uint32_t timeout_ms;
atomic_int *election_done; atomic_int *election_done;
@ -226,9 +203,32 @@ int ha_should_redirect(const ha_state_t *ha);
*/ */
int ha_no_primary_known(const ha_state_t *ha); int ha_no_primary_known(const ha_state_t *ha);
/*
* ha_is_primary: returns 1 if ha->role is CTRL_PRIMARY, 0 otherwise.
* Lock-free. Safe to call from any thread.
*/
int ha_is_primary(const ha_state_t *ha); int ha_is_primary(const ha_state_t *ha);
void ha_check_priority_stepdown(ha_state_t *ha, int sender_priority, uint64_t sender_term);
/*
* ha_check_priority_stepdown: called from the heartbeat handler when
* term == 0 (simultaneous boot). If sender_priority < ha->my_priority
* (higher priority), steps down the local node from PRIMARY to STANDBY.
* No-op if term > 0 or sender is not higher priority.
*/
void ha_check_priority_stepdown(ha_state_t *ha, int sender_priority,
uint64_t sender_term);
/*
* start_heartbeat_listener: start a minimal pre-Phase-2 SSL listener
* that responds to OP_heartbeat probes. Called after am_i_primary()
* returns 1. Records tid in c->heartbeat_listener_tid.
*/
void start_heartbeat_listener(cluster_t *c); void start_heartbeat_listener(cluster_t *c);
/*
* stop_heartbeat_listener: signal and join the heartbeat listener thread.
* Must be called before iomgr_init() to free the listening port (EADDRINUSE).
*/
void stop_heartbeat_listener(cluster_t *c); void stop_heartbeat_listener(cluster_t *c);
/* ── P3-6: Test-only failover shim ────────────────────────────── */ /* ── P3-6: Test-only failover shim ────────────────────────────── */

View file

@ -92,5 +92,6 @@ typedef uint64_t jobid_t;
#define DEFAULT_WORKER_RETAIN_S 86400 #define DEFAULT_WORKER_RETAIN_S 86400
#define DEFAULT_SYNC_TIMEOUT_MS 50 #define DEFAULT_SYNC_TIMEOUT_MS 50
#define DEFAULT_HB_INTERVAL_MS 3000 #define DEFAULT_HB_INTERVAL_MS 3000
#define DEFAULT_MAX_WAL_RECORDS 10000
#endif #endif

View file

@ -33,7 +33,7 @@ typedef struct {
int conf_keep_n; /* versioned file retention count (default 2) */ int conf_keep_n; /* versioned file retention count (default 2) */
uint64_t config_generation; /* monotonic audit counter */ uint64_t config_generation; /* monotonic audit counter */
time_t conf_stale_timeout; /* seconds before saved config flagged stale (default 86400) */ time_t conf_stale_timeout; /* seconds before saved config flagged stale (default 86400) */
int config_stale; /* 1 when saved config older than conf_stale_timeout */ int conf_stale; /* 1 when saved config older than conf_stale_timeout */
} myconf_t; } myconf_t;
extern cluster_config_t* get_default_conf(void*); extern cluster_config_t* get_default_conf(void*);

View file

@ -38,7 +38,7 @@ static inline persist_config_t persist_config_defaults(void)
c.finished_retain_s = DEFAULT_FINISHED_RETAIN_S; c.finished_retain_s = DEFAULT_FINISHED_RETAIN_S;
c.worker_retain_s = DEFAULT_WORKER_RETAIN_S; c.worker_retain_s = DEFAULT_WORKER_RETAIN_S;
c.sync_timeout_ms = DEFAULT_SYNC_TIMEOUT_MS; c.sync_timeout_ms = DEFAULT_SYNC_TIMEOUT_MS;
c.max_wal_records = 10000; c.max_wal_records = DEFAULT_MAX_WAL_RECORDS;
return c; return c;
} }
@ -80,8 +80,8 @@ struct persist_s {
/* Concurrency */ /* Concurrency */
pthread_mutex_t lock; pthread_mutex_t lock;
_Atomic uint32_t wal_record_count; /* records appended since last checkpoint */ atomic_uint wal_record_count; /* records appended since last checkpoint */
_Atomic int checkpoint_in_progress; /* CAS guard: 0=idle, 1=scheduled/running */ atomic_int checkpoint_in_progress; /* CAS guard: 0=idle, 1=scheduled/running */
atomic_int fsync_stop; atomic_int fsync_stop;
atomic_int fsync_thread_running; atomic_int fsync_thread_running;
pthread_t fsync_thread; pthread_t fsync_thread;