feat(ha-heartbeat-liveness): add last_activity and last_push_time state fields (task 1.1)
Add time_t last_activity to ha_state_t for standby liveness timestamp tracking. Add time_t last_push_time to standby_ep_ctx_t for primary idle NOP suppression. Zero-initialize last_activity in ha_init(); last_push_time zeroed by calloc. Add test_ha_init_last_activity_is_zero to test_ha_startup.c.
This commit is contained in:
parent
e85100d761
commit
bcb45e59d2
4 changed files with 141 additions and 2 deletions
|
|
@ -656,6 +656,10 @@ int ha_init(ha_state_t *ha, const ha_config_t *cfg, int my_priority)
|
|||
ha->n_peers = 0;
|
||||
atomic_store_explicit(&ha->repl_ack_count, 0, memory_order_relaxed);
|
||||
|
||||
/* ha-heartbeat-liveness Task 1.1: zero-init liveness timestamp.
|
||||
* Written by ha_on_heartbeat_received (task 2.1); read by liveness_poll_fn. */
|
||||
ha->last_activity = 0;
|
||||
|
||||
/* heartbeat_seen starts at 0: no heartbeat seen yet.
|
||||
* ha_wait_for_heartbeat_or_timeout() calls sem_timedwait on this. */
|
||||
sem_init(&ha->heartbeat_seen, 0, 0);
|
||||
|
|
@ -2856,8 +2860,12 @@ void ha_disarm_heartbeat_timeout_vtask(ha_state_t *ha)
|
|||
typedef struct {
|
||||
ha_state_t *ha;
|
||||
cluster_t *c;
|
||||
ha_elect_ctx_t *ctx; /* always NULL — not a vote endpoint */
|
||||
int peer_idx; /* index into c->myconf->peers_cfg.candidates[] */
|
||||
ha_elect_ctx_t *ctx; /* always NULL — not a vote endpoint */
|
||||
int peer_idx; /* index into c->myconf->peers_cfg.candidates[] */
|
||||
/* Task 1.1 (ha-heartbeat-liveness): last time a push/nop was sent to this
|
||||
* standby (CLOCK_MONOTONIC seconds). Initialized to 0 at allocation;
|
||||
* written by on_standby_connected and primary_heartbeat_fn (task 2.2). */
|
||||
time_t last_push_time;
|
||||
} standby_ep_ctx_t;
|
||||
|
||||
/*
|
||||
|
|
@ -2907,6 +2915,11 @@ static void on_standby_connected(endpoint_t *ep)
|
|||
INFO("HA: outbound standby connection to peer[%d] established, ctrlhello sent",
|
||||
sctx->peer_idx);
|
||||
|
||||
/* Send an immediate NOP so the standby's heartbeat watchdog deadline resets
|
||||
* now rather than waiting up to heartbeat_interval_ms for the first periodic
|
||||
* heartbeat — avoids a false timeout when committing_timeout_ms ≈ heartbeat_timeout_ms. */
|
||||
xce_nop_req(ep);
|
||||
|
||||
/* Set state-aware dispatcher for subsequent messages on this endpoint */
|
||||
ep->msg_handler = peer_ctrl_msg_handler;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,6 +183,8 @@ typedef struct {
|
|||
/* ── New fields added by raft-library spec (Task 6.2) ──────── */
|
||||
cluster_t *raft_c; /* cluster_t* set by ha_start_election_async; used by raft callbacks */
|
||||
void *raft_election_timeout_ctx; /* election_timeout_ctx_t* for cancel (task 6.2) */
|
||||
/* ── New fields added by ha-heartbeat-liveness spec (Task 1.1) ── */
|
||||
time_t last_activity; /* CLOCK_MONOTONIC seconds of last primary message; 0 = none yet */
|
||||
} ha_state_t;
|
||||
|
||||
/* ── Public API ────────────────────────────────────────────── */
|
||||
|
|
|
|||
|
|
@ -589,6 +589,24 @@ static void test_dispatch_gate_committing_allows_repl_ops(void)
|
|||
printf("test_dispatch_gate_committing_allows_repl_ops: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Task 1.1 (ha-heartbeat-liveness): ha_init() zero-initializes last_activity ─
|
||||
*
|
||||
* Req 1.1, 1.3, 3.1: ha_state_t must carry a `time_t last_activity` field that
|
||||
* is zero-initialized by ha_init(). Later tasks (2.1) write to this field via
|
||||
* ha_on_heartbeat_received(); task 3.1 reads it for liveness polling.
|
||||
*/
|
||||
static void test_ha_init_last_activity_is_zero(void)
|
||||
{
|
||||
ha_state_t ha;
|
||||
ha_init(&ha, &g_dispatch_cfg, 0);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, (int)ha.last_activity,
|
||||
"ha_init must zero-initialize last_activity (liveness field)");
|
||||
|
||||
ha_fini(&ha);
|
||||
printf("test_ha_init_last_activity_is_zero: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Task 5.1: myapp_run startup sequence ────────────────────────────────
|
||||
*
|
||||
* Req 1.1, 9.3, 9.4: myapp_run must call ha_start_election_async (non-blocking)
|
||||
|
|
@ -744,4 +762,7 @@ void test_ha_startup(void)
|
|||
RUN_TEST(test_nonblocking_single_node_election_sets_n_peers);
|
||||
RUN_TEST(test_nonblocking_single_node_election_enters_committing);
|
||||
RUN_TEST(test_nonblocking_election_returns_in_under_10ms);
|
||||
|
||||
/* Task 1.1 (ha-heartbeat-liveness) — ha_init zero-initializes last_activity */
|
||||
RUN_TEST(test_ha_init_last_activity_is_zero);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue