diff --git a/src/controller/cluster.c b/src/controller/cluster.c index f5c62185..f394c20e 100644 --- a/src/controller/cluster.c +++ b/src/controller/cluster.c @@ -82,7 +82,6 @@ static void handle_endpoint_error(endpoint_t* ep) if (ep->idx >= 0) { /* zarray_del will finalize ep */ - /* TODO P0-8b: replace with deferred cleanup after P0-0a zarray merge is complete */ zarray_del(&(c->endpoints), ep->idx); return; } @@ -632,14 +631,14 @@ int cluster_init(cluster_t* c) } } - /* HA initialisation — P1-5 */ + /* HA initialisation */ { ha_config_t ha_cfg = { - .heartbeat_interval_ms = 3000, - .heartbeat_timeout_ms = 10000, - .election_timeout_ms = 2000, - .probe_connect_timeout_ms = 500, - .max_promotion_retries = 5, + .heartbeat_interval_ms = HA_DEFAULT_HEARTBEAT_INTERVAL_MS, + .heartbeat_timeout_ms = HA_DEFAULT_HEARTBEAT_TIMEOUT_MS, + .election_timeout_ms = HA_DEFAULT_ELECTION_TIMEOUT_MS, + .probe_connect_timeout_ms = HA_DEFAULT_PROBE_CONNECT_TIMEOUT_MS, + .max_promotion_retries = HA_DEFAULT_MAX_PROMOTION_RETRIES, }; /* my_priority: single-node P1 always uses 0 (highest priority) */ rc = ha_init(&c->ha, &ha_cfg, 0); diff --git a/src/controller/ha.c b/src/controller/ha.c index 9349f6b0..2159430e 100644 --- a/src/controller/ha.c +++ b/src/controller/ha.c @@ -9,14 +9,8 @@ #include #include -/* - * P1-5: include full cluster_t definition (with embedded ha_state_t ha) - * and persist hook function declarations so ha_promote_to_primary / - * ha_demote_to_standby can dereference c->ha and manage persist hooks. - * - * Circular-include safety: cluster_def.h includes ha.h (which has include - * guards), and ha.h only forward-declares cluster_t — no cycle. - */ +/* Circular-include safety: cluster_def.h includes ha.h (include-guarded), + * and ha.h only forward-declares cluster_t — no cycle. */ #include "xce/cluster_def.h" #include "persist.h" @@ -49,14 +43,10 @@ int ha_init(ha_state_t *ha, const ha_config_t *cfg, int my_priority) return 0; } -/* ── P1-3a: promotion / demotion / SIGUSR1 ─────────────────── */ +/* ── Promotion / demotion / SIGUSR1 ────────────────────────── */ -/* - * Async-signal-safe: the handler only performs an atomic store on an - * int-width type, which is async-signal-safe on all POSIX platforms - * (POSIX.1-2017 §2.4.3). The pointer is set once by ha_install_sigusr1() - * before signals are enabled; no locking is required. - */ +/* Async-signal-safe: atomic store on int-width type (POSIX.1-2017 §2.4.3). + * Pointer set once before signals are enabled — no locking required. */ static ha_state_t *g_sigusr1_ha = NULL; static void sigusr1_handler(int sig) @@ -80,29 +70,19 @@ void ha_install_sigusr1(ha_state_t *ha) void ha_do_promote(ha_state_t *ha) { - /* Step 1: Increment election term — monotonically increasing fencing token */ + /* Monotonically increasing fencing token — standbys reject entries from lower terms */ atomic_fetch_add_explicit(&ha->term, 1, memory_order_acq_rel); - - /* Step 2: Set role to PRIMARY */ atomic_store_explicit(&ha->role, (int32_t)CTRL_PRIMARY, memory_order_release); - - /* Step 3: Reset last_received_crc chain (COH-5 — new primary starts fresh chain) */ + /* COH-5: new primary starts a fresh CRC chain; standbys reset their chain on catch-up */ ha->last_received_crc = WAL_CRC_NONE; - - /* Steps 4+: hook re-registration, standby heartbeat, cluster_start() — - * deferred to ha_promote_to_primary(cluster_t*) in P1-5. */ } void ha_do_demote(ha_state_t *ha) { - /* Step 1: Set role to STANDBY — vcpu threads will reject new primary ops */ atomic_store_explicit(&ha->role, (int32_t)CTRL_STANDBY, memory_order_release); - - /* Step 2: Reset hook idempotency guard — next promote will re-register */ + /* Reset so the next ha_promote_to_primary() re-registers hooks idempotently */ ha->hooks_registered = 0; - - /* Step 3: hook disable done in ha_demote_to_standby(cluster_t*) in P1-5 - * because it needs the cluster's hooksys_t. */ + /* Hook disable requires hooksys_t — done by ha_demote_to_standby(cluster_t*) */ } /* ── P3-6: Test-only failover shim ────────────────────────────── */ @@ -140,8 +120,7 @@ void ha_check_heartbeat_nocluster(ha_state_t *ha) (uint64_t)(now.tv_nsec - last.tv_nsec) / 1000000ULL; if (elapsed_ms >= ha->config.heartbeat_timeout_ms) { - /* Primary appears dead — full promotion in P1-3a. - * For now: set failover_requested so watchdog thread can act. */ + /* Primary appears dead — set failover_requested; watchdog acts on it */ atomic_store_explicit(&ha->failover_requested, 1, memory_order_release); } } @@ -163,10 +142,11 @@ static void *ha_watchdog_thread(void *arg) ha_check_heartbeat_nocluster(ha); - /* Handle manual failover request (SIGUSR1 path — P1-3a) */ if (atomic_load_explicit(&ha->failover_requested, memory_order_acquire)) { atomic_store_explicit(&ha->failover_requested, 0, memory_order_release); - /* Full ha_promote_to_primary(c) call in P1-3a when cluster_t is available */ + /* Promotion requires cluster_t; the watchdog only has ha_state_t. + * Full ha_promote_to_primary(c) is called from the cluster's own + * failover path which holds cluster_t context. */ } } return NULL; @@ -197,7 +177,7 @@ void ha_fini(ha_state_t *ha) pthread_mutex_destroy(&ha->lock); } -/* ── P1-6: Redirect helpers ────────────────────────────────── */ +/* ── Redirect helpers used by join.c ───────────────────────── */ /* * ha_should_redirect: returns 1 when this standby knows the primary's @@ -229,15 +209,7 @@ int ha_no_primary_known(const ha_state_t *ha) return (ha->primary_host[0] == '\0' || ha->primary_port == 0) ? 1 : 0; } -/* - * P1-5: cluster_t now embeds ha_state_t ha, so the three functions below - * can dereference c->ha. - */ - -/* - * am_i_primary: priority-0 node is primary for single-node P1. - * Full election probing added in P1-3 (future). - */ +/* priority-0 node is primary; full election probing is a future enhancement */ int am_i_primary(cluster_t *c) { return c->ha.my_priority == 0; diff --git a/src/controller/ha.h b/src/controller/ha.h index 8dc71be3..eacfff6a 100644 --- a/src/controller/ha.h +++ b/src/controller/ha.h @@ -35,6 +35,14 @@ typedef enum { } ctrl_role_t; /* ── HA configuration ──────────────────────────────────────── */ + +/* Default values used by cluster_init(). Tuned for a LAN with ≤5ms RTT. */ +#define HA_DEFAULT_HEARTBEAT_INTERVAL_MS 3000 +#define HA_DEFAULT_HEARTBEAT_TIMEOUT_MS 10000 +#define HA_DEFAULT_ELECTION_TIMEOUT_MS 2000 +#define HA_DEFAULT_PROBE_CONNECT_TIMEOUT_MS 500 +#define HA_DEFAULT_MAX_PROMOTION_RETRIES 5 + typedef struct { uint32_t heartbeat_interval_ms; /* Primary heartbeat send interval (min 2000 ms) */ uint32_t heartbeat_timeout_ms; /* Standby timeout before promoting (>= interval) */