diff --git a/src/controller/testsuite/test_config_repl.c b/src/controller/testsuite/test_config_repl.c index 770a619..34335b7 100644 --- a/src/controller/testsuite/test_config_repl.c +++ b/src/controller/testsuite/test_config_repl.c @@ -1991,6 +1991,23 @@ static void destroy_test_persist_catchup(persist_t *p) free(p); } +/* bug-850/851 contiguity guard (persist.c:839): persist_wal_write_replicated + * REFUSES a forward gap (entry.lsn > current_lsn → -EAGAIN) so a standby never + * creates a WAL hole. A fresh persist has current_lsn == 1 (LSN 0 reserved, + * persist.c:542). The catch-up tests below choose their own non-1 starting LSNs + * (42, 10, 20, …); seed current_lsn to the first applied entry's LSN so the + * first entry is contiguous (entry.lsn == current_lsn) and the guard accepts + * the in-order stream — mirroring the real catch-up streamer, which streams + * contiguously from current_lsn. current_lsn is a plain lock-protected field; + * persist.c sets it directly under p->lock (e.g. persist.c:542,715), so do the + * same here. */ +static void seed_persist_current_lsn(persist_t *p, uint64_t next_lsn) +{ + pthread_mutex_lock(&p->lock); + p->current_lsn = next_lsn; + pthread_mutex_unlock(&p->lock); +} + /* Test 2.1-A: successful single WAL entry advances applied_lsn */ static void test_wal_catchup_entry_advances_applied_lsn(void) { @@ -2008,6 +2025,9 @@ static void test_wal_catchup_entry_advances_applied_lsn(void) TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)atomic_load_explicit(&ha.applied_lsn, memory_order_acquire)); + /* Seed current_lsn so the entry at LSN 42 is contiguous (bug-850 guard). */ + seed_persist_current_lsn(p, 42); + /* Build a single-entry WAL catch-up with LSN=42, no payload */ wal_catchup_entry_t entry; memset(&entry, 0, sizeof(entry)); @@ -2111,6 +2131,9 @@ static void test_wal_catchup_batch_advances_applied_lsn(void) TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)atomic_load_explicit(&ha.applied_lsn, memory_order_acquire)); + /* Seed current_lsn so the batch [10,11,12] is contiguous (bug-850 guard). */ + seed_persist_current_lsn(p, 10); + /* Build 3 sub-entries at LSNs 10, 11, 12 */ wal_catchup_entry_t e0, e1, e2; memset(&e0, 0, sizeof(e0)); @@ -2163,6 +2186,9 @@ static void test_wal_catchup_partial_batch(void) /* Pre-set applied_lsn to 5 to verify monotonic advance */ atomic_store_explicit(&ha.applied_lsn, 5, memory_order_release); + /* Seed current_lsn so the batch [20,21] is contiguous (bug-850 guard). */ + seed_persist_current_lsn(p, 20); + wal_catchup_entry_t e0, e1; memset(&e0, 0, sizeof(e0)); memset(&e1, 0, sizeof(e1)); @@ -2209,6 +2235,11 @@ static void test_wal_catchup_batch_error_entry_skipped(void) cfg.election_timeout_ms = 1500; ha_init(&ha, &cfg, 0); + /* Seed current_lsn so the first good entry at LSN 30 is contiguous (bug-850 + * guard). The NULL sub-entry is skipped (no write, current_lsn stays at 31); + * the test only requires applied_lsn >= 30 (the first good entry held). */ + seed_persist_current_lsn(p, 30); + /* Good entry at LSN 30, then a NULL pointer (simulates corrupt entry), * then good entry at LSN 32. */ wal_catchup_entry_t e0, e2; @@ -2260,6 +2291,10 @@ static void test_wal_catchup_batch_zero_entries_fallback(void) cfg.election_timeout_ms = 1500; ha_init(&ha, &cfg, 0); + /* Seed current_lsn so the fallback single entry at LSN 50 is contiguous + * (bug-850 guard). */ + seed_persist_current_lsn(p, 50); + /* n_entries == 0 → single-entry path from task 2.1 */ wal_catchup_entry_t entry; memset(&entry, 0, sizeof(entry)); @@ -2303,6 +2338,10 @@ static void test_wal_catchup_batch_256_entries(void) cfg.election_timeout_ms = 1500; ha_init(&ha, &cfg, 0); + /* Seed current_lsn so the consecutive batch starting at LSN 1000 is + * contiguous (bug-850 guard). */ + seed_persist_current_lsn(p, 1000); + /* Allocate 256 sub-entries at consecutive LSNs starting at 1000 */ const int N = 256; wal_catchup_entry_t entries[N]; @@ -2371,6 +2410,9 @@ static void test_wal_catchup_batch_applied_lsn_only_after_full_batch(void) * from full advance. */ atomic_store_explicit(&ha.applied_lsn, 100, memory_order_release); + /* Seed current_lsn so the batch [200..203] is contiguous (bug-850 guard). */ + seed_persist_current_lsn(p, 200); + /* Build a batch of 4 entries at LSNs 200, 201, 202, 203 */ wal_catchup_entry_t e0, e1, e2, e3; memset(&e0, 0, sizeof(e0));