feat(ha-heartbeat-liveness): replace hb_timeout_thread with liveness_poll_fn (task 2.1)
Remove hb_vtask_t, hb_timeout_thread, s_hb_vtask from ha.c. Replace with liveness_poll_ctx_t + delaymgr liveness_poll_fn: checks cancelled flag and role==CTRL_STANDBY before acting; re-arms at heartbeat_interval_ms/2. ha_on_primary_ctrl_fd_rdhup calls ha_disarm_heartbeat_timeout_vtask first to prevent duplicate failover (Req 5.4). ha_on_heartbeat_received writes last_activity via clock_gettime. Add test_ha_liveness.c with 7 unit tests.
This commit is contained in:
parent
9f9a6b3c0f
commit
738859fb4e
6 changed files with 465 additions and 90 deletions
|
|
@ -180,10 +180,22 @@ static void test_heartbeat_received_resets_timeout(void)
|
|||
|
||||
/* ── Test 4: heartbeat timeout fires failover ───────────────────────────── */
|
||||
|
||||
/*
|
||||
* liveness_poll_ctx_t and liveness_poll_fn_test_entry: XCE_TESTING seam from ha.c.
|
||||
* Replicate the typedef here to construct a ctx directly.
|
||||
*/
|
||||
typedef struct {
|
||||
ha_state_t *ha;
|
||||
cluster_t *c;
|
||||
_Atomic int cancelled;
|
||||
} liveness_poll_ctx_t_failover;
|
||||
|
||||
extern void liveness_poll_fn_test_entry(liveness_poll_ctx_t_failover *ctx);
|
||||
|
||||
static void test_heartbeat_timeout_fires_failover(void)
|
||||
{
|
||||
ha_state_t ha;
|
||||
/* Use fast config: heartbeat_timeout_ms=2000ms so watchdog fires in ~2s */
|
||||
/* Use fast config: heartbeat_timeout_ms=2000ms */
|
||||
TEST_ASSERT_EQUAL_INT(0, ha_init(&ha, &g_fast_cfg, 1));
|
||||
ha_do_demote(&ha);
|
||||
|
||||
|
|
@ -191,18 +203,24 @@ static void test_heartbeat_timeout_fires_failover(void)
|
|||
TEST_ASSERT_EQUAL_INT(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sv));
|
||||
ha.primary_ctrl_fd = sv[0];
|
||||
|
||||
/* Arm watchdog — no heartbeat will arrive */
|
||||
ha_arm_heartbeat_timeout_vtask(&ha, NULL);
|
||||
/* Set last_activity to beyond the timeout (simulate silence) */
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
ha.last_activity = ts.tv_sec - (time_t)(g_fast_cfg.heartbeat_timeout_ms / 1000) - 5;
|
||||
|
||||
/* Wait 2500ms — past the 2000ms heartbeat_timeout_ms */
|
||||
struct timespec ts = { .tv_sec = 2, .tv_nsec = 500 * 1000000L };
|
||||
nanosleep(&ts, NULL);
|
||||
/* Invoke the liveness poll fn directly (no delaymgr needed) */
|
||||
liveness_poll_ctx_t_failover ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.ha = &ha;
|
||||
ctx.c = NULL; /* NULL cluster — unit test mode */
|
||||
atomic_init(&ctx.cancelled, 0);
|
||||
|
||||
liveness_poll_fn_test_entry(&ctx);
|
||||
|
||||
/* Failover must have fired: role = CTRL_ELECTING, fd = -1 */
|
||||
TEST_ASSERT_EQUAL_INT(CTRL_ELECTING, (int)atomic_load(&ha.role));
|
||||
TEST_ASSERT_EQUAL_INT(-1, ha.primary_ctrl_fd);
|
||||
|
||||
ha_disarm_heartbeat_timeout_vtask(&ha);
|
||||
close(sv[1]); /* sv[0] was closed by failover handler */
|
||||
ha_fini(&ha);
|
||||
printf("test_heartbeat_timeout_fires_failover: PASS\n");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue