feat(ha-term-safety): wire five callbacks + library init restore (task 2.5)
This commit is contained in:
parent
3470bdfb71
commit
3dd8493fff
3 changed files with 139 additions and 3 deletions
|
|
@ -861,6 +861,106 @@ static void test_on_commit_advanced_wired_feeds_barrier(void)
|
|||
printf("test_on_commit_advanced_wired_feeds_barrier: PASS\n");
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
/* Task 2.5 — library init restore from the recovered WAL floor */
|
||||
/* ================================================================== */
|
||||
|
||||
/* Test: ha_init_raft restores the library log/commit floor from the
|
||||
* recovered WAL instead of booting at LSN 0 (R1.5, R2.1).
|
||||
*
|
||||
* After a member recovers a WAL whose durable tip is LSN 3 (term 7) with a
|
||||
* committed prefix at LSN 2, raft_init must boot with:
|
||||
* raft.log_len == 3 (initial_log_len = persist_durable_floor)
|
||||
* raft.commit_index == 2 (initial_commit_index = recovered committed_lsn)
|
||||
* and the last-log term must be recoverable via the wired on_get_entry_term
|
||||
* (== 7). Booting from an empty log (log_len/commit_index == 0) is the pre-fix
|
||||
* behaviour this test fails on. */
|
||||
static void test_init_restores_recovered_floor(void)
|
||||
{
|
||||
char tmp[256];
|
||||
strcpy(tmp, "/tmp/xce_ts_initrestore_XXXXXX");
|
||||
TEST_ASSERT_NOT_NULL(mkdtemp(tmp));
|
||||
|
||||
const uint64_t T = 7;
|
||||
|
||||
/* Phase 1: lay down a 3-entry WAL (term 7) and flush so the recovered tip
|
||||
* reaches LSN 3. */
|
||||
{
|
||||
cluster_t* c = make_cluster(tmp);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
atomic_store(&c->ha.role, CTRL_PRIMARY);
|
||||
|
||||
uint8_t blob[64];
|
||||
for (uint64_t i = 1; i <= 3; i++) {
|
||||
size_t n = pack_entry(blob, WAL_JOB_UPDATE, "x", 1);
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
ha_cb_persist_entry_for_test(&c->ha, i, T, blob, n));
|
||||
}
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_flush(&c->persist));
|
||||
teardown_cluster(c);
|
||||
}
|
||||
|
||||
/* Phase 2: recover into a fresh cluster; durable floor == 3. Simulate a
|
||||
* recovered committed prefix at LSN 2 (recovery re-derives the durable tip;
|
||||
* the committed position is set by the surrounding boot path). */
|
||||
{
|
||||
cluster_t* c2 = make_cluster(tmp);
|
||||
TEST_ASSERT_NOT_NULL(c2);
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_recover(&c2->persist));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(3, persist_durable_floor(&c2->persist));
|
||||
atomic_store(&c2->persist.committed_lsn, 2ULL);
|
||||
|
||||
/* Initialize the embedded raft as a single-node cluster. The restore
|
||||
* must source the floor from the recovered persist, not LSN 0. */
|
||||
ha_init_raft(&c2->ha, c2, /*n_peers*/1);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(3, c2->ha.raft.log_len);
|
||||
TEST_ASSERT_EQUAL_UINT64(2, c2->ha.raft.commit_index);
|
||||
|
||||
/* The recovered last-log term is surfaced via the wired callback. */
|
||||
TEST_ASSERT_EQUAL_UINT64(T,
|
||||
ha_cb_get_entry_term_for_test(&c2->ha, c2->ha.raft.log_len));
|
||||
|
||||
/* All five term-safety callbacks remain wired (non-NULL). */
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_persist_entry);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_get_entry_term);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_send_append_entries);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_truncate_from);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_commit_advanced);
|
||||
/* Existing election callbacks remain wired. */
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_become_leader);
|
||||
TEST_ASSERT_NOT_NULL(c2->ha.raft.cbs->on_persist);
|
||||
|
||||
teardown_cluster(c2);
|
||||
}
|
||||
|
||||
printf("test_init_restores_recovered_floor: PASS\n");
|
||||
}
|
||||
|
||||
/* Test: an empty-WAL first boot restores a zero floor (no spurious non-zero
|
||||
* indices) — the recovery-read-failure / empty-log edge of R1.5. */
|
||||
static void test_init_empty_wal_zero_floor(void)
|
||||
{
|
||||
char tmp[256];
|
||||
strcpy(tmp, "/tmp/xce_ts_initempty_XXXXXX");
|
||||
TEST_ASSERT_NOT_NULL(mkdtemp(tmp));
|
||||
|
||||
cluster_t* c = make_cluster(tmp);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_recover(&c->persist));
|
||||
TEST_ASSERT_EQUAL_UINT64(0, persist_durable_floor(&c->persist));
|
||||
|
||||
ha_init_raft(&c->ha, c, /*n_peers*/1);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(0, c->ha.raft.log_len);
|
||||
TEST_ASSERT_EQUAL_UINT64(0, c->ha.raft.commit_index);
|
||||
|
||||
teardown_cluster(c);
|
||||
printf("test_init_empty_wal_zero_floor: PASS\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Suite entry point */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -885,4 +985,6 @@ void test_term_safety_adapter(void)
|
|||
RUN_TEST(test_commit_advanced_does_not_bypass_durable_cap);
|
||||
RUN_TEST(test_own_term_gate_retained_no_deadlock);
|
||||
RUN_TEST(test_on_commit_advanced_wired_feeds_barrier);
|
||||
RUN_TEST(test_init_restores_recovered_floor);
|
||||
RUN_TEST(test_init_empty_wal_zero_floor);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue