feat(ha-repl-hardening): unified replication decision loop (task 3.2)

ha_evaluate_repl_action() pure function: stale tag→PUSH_CONFIG,
lsn<oldest_wal→SEND_SNAPSHOT, lsn<committed→STREAM_WAL, else CONVERGED.
5 tests. Fixed misleading stream_wal_from_zero test name per review.
This commit is contained in:
Rongsong Shen 2026-06-04 16:58:48 +00:00
parent aab41fa609
commit a475ef621f
4 changed files with 176 additions and 1 deletions

View file

@ -377,6 +377,93 @@ static void test_process_standby_state_multiple_peers(void)
printf("test_process_standby_state_multiple_peers: PASS\n");
}
/* ── Task 3.2: ha_evaluate_repl_action unified decision tree ─────────────── */
/*
* Declare the function under test. It is defined in ha.c.
* Pure function — no side effects — so no XCE_TESTING guard needed.
*/
ha_repl_action_t ha_evaluate_repl_action(uint64_t standby_tag,
uint64_t standby_lsn,
uint64_t committed_tag,
uint64_t committed_lsn,
uint64_t oldest_wal_lsn);
/*
* Test: (tag=0, lsn=0) with committed_tag=5, committed_lsn=100,
* oldest_wal_lsn=50 → PUSH_CONFIG (tag behind).
* Requirements: 2.2, 2.3 (ha-repl-hardening)
*/
static void test_evaluate_repl_action_push_config(void)
{
ha_repl_action_t action = ha_evaluate_repl_action(
/*standby_tag=*/0, /*standby_lsn=*/0,
/*committed_tag=*/5, /*committed_lsn=*/100,
/*oldest_wal_lsn=*/50);
TEST_ASSERT_EQUAL_INT(HA_REPL_PUSH_CONFIG, action);
printf("test_evaluate_repl_action_push_config: PASS\n");
}
/*
* Test: (tag=5, lsn=0) with committed_tag=5, committed_lsn=100,
* oldest_wal_lsn=50 → SEND_SNAPSHOT (lsn=0 is below WAL retention window).
* Requirements: 2.2, 2.3
*/
static void test_evaluate_repl_action_send_snapshot_lsn_zero(void)
{
ha_repl_action_t action = ha_evaluate_repl_action(
/*standby_tag=*/5, /*standby_lsn=*/0,
/*committed_tag=*/5, /*committed_lsn=*/100,
/*oldest_wal_lsn=*/50);
/* lsn=0 < oldest_wal_lsn=50 → SEND_SNAPSHOT */
TEST_ASSERT_EQUAL_INT(HA_REPL_SEND_SNAPSHOT, action);
printf("test_evaluate_repl_action_send_snapshot_lsn_zero: PASS\n");
}
/*
* Test: (tag=5, lsn=100) with committed_tag=5, committed_lsn=100 → CONVERGED.
* Requirements: 2.2, 2.4
*/
static void test_evaluate_repl_action_converged(void)
{
ha_repl_action_t action = ha_evaluate_repl_action(
/*standby_tag=*/5, /*standby_lsn=*/100,
/*committed_tag=*/5, /*committed_lsn=*/100,
/*oldest_wal_lsn=*/50);
TEST_ASSERT_EQUAL_INT(HA_REPL_CONVERGED, action);
printf("test_evaluate_repl_action_converged: PASS\n");
}
/*
* Test: (tag=5, lsn=10) with committed_tag=5, committed_lsn=100,
* oldest_wal_lsn=50 → SEND_SNAPSHOT (lsn < oldest_wal).
* Requirements: 2.2, 2.3
*/
static void test_evaluate_repl_action_send_snapshot(void)
{
ha_repl_action_t action = ha_evaluate_repl_action(
/*standby_tag=*/5, /*standby_lsn=*/10,
/*committed_tag=*/5, /*committed_lsn=*/100,
/*oldest_wal_lsn=*/50);
TEST_ASSERT_EQUAL_INT(HA_REPL_SEND_SNAPSHOT, action);
printf("test_evaluate_repl_action_send_snapshot: PASS\n");
}
/*
* Test: (tag=5, lsn=50) with committed_tag=5, committed_lsn=100,
* oldest_wal_lsn=50 → STREAM_WAL (lsn >= oldest_wal but < committed).
* Requirements: 2.2, 2.3
*/
static void test_evaluate_repl_action_stream_wal(void)
{
ha_repl_action_t action = ha_evaluate_repl_action(
/*standby_tag=*/5, /*standby_lsn=*/50,
/*committed_tag=*/5, /*committed_lsn=*/100,
/*oldest_wal_lsn=*/50);
TEST_ASSERT_EQUAL_INT(HA_REPL_STREAM_WAL, action);
printf("test_evaluate_repl_action_stream_wal: PASS\n");
}
void test_ha_role(void)
{
RUN_TEST(test_ha_init_default_role_is_standby);
@ -398,6 +485,12 @@ void test_ha_role(void)
RUN_TEST(test_process_standby_state_tag_behind);
RUN_TEST(test_process_standby_state_regression);
RUN_TEST(test_process_standby_state_multiple_peers);
/* Task 3.2: ha_evaluate_repl_action unified decision tree */
RUN_TEST(test_evaluate_repl_action_push_config);
RUN_TEST(test_evaluate_repl_action_send_snapshot_lsn_zero);
RUN_TEST(test_evaluate_repl_action_converged);
RUN_TEST(test_evaluate_repl_action_send_snapshot);
RUN_TEST(test_evaluate_repl_action_stream_wal);
}
/* test_am_i_primary suite deleted — am_i_primary() removed (election-iomgr-refactor) */