feat(raft-commit-safety): commit_index state + on_commit_advanced callback + init (tasks 1.1, 1.2, 2.1)
1.1: raft_state_t.commit_index (monotonic, ==WAL LSN, <=log_len, advanced only via raft_advance_commit, untouched by AE reply) + raft_config_t.initial_commit_index. 1.2: on_commit_advanced(ctx, commit_index) REQUIRED callback — fired only on strict advance, a TRIGGER for the controller commit barrier (never does durability itself). 2.1: raft_init requires it (-EINVAL if NULL) + restores commit_index from initial_commit_index. raft_advance_commit prototype declared (impl in 3.2). Coupled callback wiring to keep the required-callback ripple green: sim wires sim_on_commit_advanced (+per-node commit_index mirror & fire-count), all test cbs builders set it, raft_sim_restart_node restores the durable commit position. Vote signatures unchanged; no commit-advance logic yet. ASAN green (224 tests).
This commit is contained in:
parent
3d6dc9cc56
commit
3ba6f03a9e
5 changed files with 170 additions and 7 deletions
|
|
@ -77,6 +77,13 @@ static int stub_truncate_from(void *ctx, uint64_t index)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* ---- commit-advance callback stub (raft-commit-safety) ---- */
|
||||
|
||||
static void stub_commit_advanced(void *ctx, uint64_t commit_index)
|
||||
{
|
||||
(void)ctx; (void)commit_index;
|
||||
}
|
||||
|
||||
/* Build a fully-populated callbacks struct */
|
||||
static raft_callbacks_t make_full_cbs(void)
|
||||
{
|
||||
|
|
@ -93,6 +100,7 @@ static raft_callbacks_t make_full_cbs(void)
|
|||
cbs.on_get_entry_term = stub_get_entry_term;
|
||||
cbs.on_send_append_entries = stub_send_append_entries;
|
||||
cbs.on_truncate_from = stub_truncate_from;
|
||||
cbs.on_commit_advanced = stub_commit_advanced;
|
||||
/* on_vote_eligible and on_trace left NULL (optional) */
|
||||
return cbs;
|
||||
}
|
||||
|
|
@ -263,6 +271,7 @@ static raft_callbacks_t make_mock_cbs(void)
|
|||
cbs.on_get_entry_term = stub_get_entry_term;
|
||||
cbs.on_send_append_entries = stub_send_append_entries;
|
||||
cbs.on_truncate_from = stub_truncate_from;
|
||||
cbs.on_commit_advanced = stub_commit_advanced;
|
||||
/* on_vote_eligible and on_trace left NULL — tests that need them set explicitly */
|
||||
return cbs;
|
||||
}
|
||||
|
|
@ -482,6 +491,74 @@ static void test_raft_init_log_callbacks_present_succeeds(void)
|
|||
TEST_ASSERT_EQUAL_UINT64(5, s.log_len);
|
||||
}
|
||||
|
||||
/* ---- commit-advance required callback + commit_index restore (raft-commit-safety
|
||||
* task 2.1, Req 2.5, 1.1) ---- */
|
||||
|
||||
/* on_commit_advanced NULL → -EINVAL (Req 2.5), like the other required callbacks */
|
||||
static void test_raft_init_null_commit_advanced_returns_einval(void)
|
||||
{
|
||||
raft_state_t s;
|
||||
raft_callbacks_t cbs = make_full_cbs();
|
||||
raft_config_t cfg;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
cbs.on_commit_advanced = NULL; /* knock out the new required callback */
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.n_peers = 3; cfg.self_idx = 0; cfg.cbs = &cbs;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, raft_init(&s, &cfg));
|
||||
}
|
||||
|
||||
/* on_commit_advanced present → succeeds (Req 2.5) */
|
||||
static void test_raft_init_commit_advanced_present_succeeds(void)
|
||||
{
|
||||
raft_state_t s;
|
||||
raft_callbacks_t cbs = make_full_cbs();
|
||||
raft_config_t cfg;
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.n_peers = 3; cfg.self_idx = 0; cfg.cbs = &cbs;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_init(&s, &cfg));
|
||||
}
|
||||
|
||||
/* commit_index restored from initial_commit_index at init (Req 1.1) */
|
||||
static void test_raft_init_restores_commit_index(void)
|
||||
{
|
||||
raft_state_t s;
|
||||
raft_callbacks_t cbs = make_full_cbs();
|
||||
raft_config_t cfg;
|
||||
|
||||
memset(&s, 0xAB, sizeof(s)); /* poison: prove init writes the field */
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.n_peers = 3;
|
||||
cfg.self_idx = 0;
|
||||
cfg.initial_commit_index = 7; /* committed LSN restored from disk */
|
||||
cfg.cbs = &cbs;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_init(&s, &cfg));
|
||||
TEST_ASSERT_EQUAL_UINT64(7, s.commit_index);
|
||||
}
|
||||
|
||||
/* default commit_index is 0 on first boot (Req 1.1, default 0) */
|
||||
static void test_raft_init_commit_index_default_zero(void)
|
||||
{
|
||||
raft_state_t s;
|
||||
raft_callbacks_t cbs = make_full_cbs();
|
||||
raft_config_t cfg;
|
||||
|
||||
memset(&s, 0xAB, sizeof(s)); /* poison */
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.n_peers = 3;
|
||||
cfg.self_idx = 0;
|
||||
cfg.cbs = &cbs; /* initial_commit_index left 0 */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_init(&s, &cfg));
|
||||
TEST_ASSERT_EQUAL_UINT64(0, s.commit_index);
|
||||
}
|
||||
|
||||
/* n_peers even (>1) → -EINVAL: even-sized clusters have no fault-tolerance
|
||||
* advantage over the next-smaller odd cluster; reject to prevent misconfiguration. */
|
||||
static void test_raft_init_even_peers_returns_einval(void)
|
||||
|
|
@ -5808,6 +5885,10 @@ void test_raft(void)
|
|||
RUN_TEST(test_raft_init_null_send_append_entries_returns_einval);
|
||||
RUN_TEST(test_raft_init_null_truncate_from_returns_einval);
|
||||
RUN_TEST(test_raft_init_log_callbacks_present_succeeds);
|
||||
RUN_TEST(test_raft_init_null_commit_advanced_returns_einval);
|
||||
RUN_TEST(test_raft_init_commit_advanced_present_succeeds);
|
||||
RUN_TEST(test_raft_init_restores_commit_index);
|
||||
RUN_TEST(test_raft_init_commit_index_default_zero);
|
||||
RUN_TEST(test_raft_init_even_peers_returns_einval);
|
||||
RUN_TEST(test_raft_init_oversize_peers_returns_einval);
|
||||
RUN_TEST(test_raft_init_zero_peers_returns_einval);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue