fix bug-1060 bug-1067

This commit is contained in:
Rongsong Shen 2026-06-24 17:05:43 +08:00
parent 6067ba6c94
commit 61e1391cd0
28 changed files with 7021 additions and 2926 deletions

View file

@ -2586,7 +2586,8 @@ static void test_wal_stream_500_entries_two_batches(void)
g_wal_stream_last_n_entries = 0;
/* Stream entries [1..500]: standby_lsn=0, committed_lsn=500 */
rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir,
rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir, /*p=*/NULL,
/*leader_term=*/7,
/*standby_lsn=*/0,
/*committed_lsn=*/500);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "ha_stream_wal_to_standby failed");
@ -2627,7 +2628,8 @@ static void test_wal_stream_partial_batch(void)
g_wal_stream_last_n_entries = 0;
/* Stream entries [6..14]: standby_lsn=5, committed_lsn=14 */
rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir,
rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir, /*p=*/NULL,
/*leader_term=*/7,
/*standby_lsn=*/5,
/*committed_lsn=*/14);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "ha_stream_wal_to_standby failed");
@ -2661,7 +2663,8 @@ static void test_wal_stream_empty_range_no_batches(void)
g_wal_stream_total_entries = 0;
/* standby_lsn == committed_lsn: nothing to stream */
int rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir,
int rc = ha_stream_wal_to_standby(/*ep=*/NULL, tmpdir, /*p=*/NULL,
/*leader_term=*/7,
/*standby_lsn=*/100,
/*committed_lsn=*/100);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Empty range must return 0");
@ -2676,6 +2679,89 @@ static void test_wal_stream_empty_range_no_batches(void)
printf("test_wal_stream_empty_range_no_batches: PASS\n");
}
/*
* bug-1028 — the catch-up streamer must source each streamed entry's term and
* the AppendEntries prev_log_term from the AUTHORITATIVE per-LSN term index
* (persist_entry_term), NOT the raw on-disk hdr.term. A legacy-0 entry has
* hdr.term==0 on disk but persist_entry_term maps it to WAL_LEGACY_TERM(1) —
* which is exactly what the standby's logOk and the library send path compute.
* If the streamer sends the raw 0, prev_log_term(0) != standby's term(1) → logOk
* rejects the new-term entry forever (the "stream WAL batches" wedge).
*
* Fixture: LSN 1 appended at term 0 (legacy), LSN 2 at term 3. Streaming
* (standby_lsn=1, committed=2] must emit LSN 2 with term=3 and prev_log_term=1
* (NOT 0). RED before the fix (streamer sent hdr.term=0 for the predecessor).
*/
static uint64_t g_cap_term_for_lsn2 = 0;
static uint64_t g_cap_prevterm_for_lsn2 = 0;
static int g_cap_saw_lsn2 = 0;
static int capture_terms_wal_stream_hook(endpoint_t *ep, xce_request_t *req)
{
(void)ep;
if (!req || !req->body.walcatchupentrymsg) return 0;
wal_catchup_entry_t *batch =
(wal_catchup_entry_t *)req->body.walcatchupentrymsg;
for (int i = 0; i < batch->n_entries; i++) {
wal_catchup_entry_t *e = batch->entries[i];
if (e && e->lsn == 2) {
g_cap_saw_lsn2 = 1;
g_cap_term_for_lsn2 = e->term;
g_cap_prevterm_for_lsn2 = e->prev_log_term;
}
}
return 0;
}
static void test_wal_stream_term_from_index_not_raw_hdr(void)
{
char tmp[256];
strcpy(tmp, "/tmp/xce_cr_streamterm_XXXXXX");
TEST_ASSERT_NOT_NULL(mkdtemp(tmp));
cluster_t *c = t42_make_cluster(tmp);
TEST_ASSERT_NOT_NULL(c);
/* LSN 1: legacy entry (term 0 on disk → persist_entry_term maps to 1). */
persist_set_term(&c->persist, 0);
TEST_ASSERT_EQUAL_INT(0, persist_wal_async(&c->persist, WAL_JOB_UPDATE,
NULL, 0));
/* LSN 2: real new-term entry (term 3). */
persist_set_term(&c->persist, 3);
TEST_ASSERT_EQUAL_INT(0, persist_wal_async(&c->persist, WAL_JOB_UPDATE,
NULL, 0));
/* Sanity: the authoritative index normalises legacy-0 → 1, keeps 3. */
TEST_ASSERT_EQUAL_UINT64(WAL_LEGACY_TERM, persist_entry_term(&c->persist, 1));
TEST_ASSERT_EQUAL_UINT64(3, persist_entry_term(&c->persist, 2));
xce_wal_stream_request_hook = capture_terms_wal_stream_hook;
g_cap_saw_lsn2 = 0; g_cap_term_for_lsn2 = 0; g_cap_prevterm_for_lsn2 = 0;
/* Standby holds LSN 1, catching up to committed 2 — streams just LSN 2,
* whose prev tuple references LSN 1 (the legacy entry). */
int rc = ha_stream_wal_to_standby(/*ep=*/NULL, c->persist.wal_path,
&c->persist,
/*leader_term=*/atomic_load_explicit(
&c->ha.term, memory_order_acquire),
/*standby_lsn=*/1, /*committed_lsn=*/2);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "ha_stream_wal_to_standby failed");
xce_wal_stream_request_hook = NULL;
TEST_ASSERT_TRUE_MESSAGE(g_cap_saw_lsn2, "LSN 2 must be streamed");
TEST_ASSERT_EQUAL_UINT64_MESSAGE(3, g_cap_term_for_lsn2,
"streamed entry term must come from persist_entry_term (3)");
TEST_ASSERT_EQUAL_UINT64_MESSAGE(WAL_LEGACY_TERM, g_cap_prevterm_for_lsn2,
"bug-1028: prev_log_term must be the index-normalised legacy term (1), "
"NOT raw on-disk hdr.term (0) — else the standby's logOk rejects forever");
t42_teardown(c);
char cmd[512];
snprintf(cmd, sizeof(cmd), "rm -rf %s", tmp);
system(cmd);
printf("test_wal_stream_term_from_index_not_raw_hdr: PASS\n");
}
/* ── Task 3.5: Snapshot chunk transfer ──────────────────────── */
static snapshot_transfer_t g_snaptest_chunks[16];
@ -3363,6 +3449,7 @@ void test_config_repl(void)
RUN_TEST(test_wal_stream_500_entries_two_batches);
RUN_TEST(test_wal_stream_partial_batch);
RUN_TEST(test_wal_stream_empty_range_no_batches);
RUN_TEST(test_wal_stream_term_from_index_not_raw_hdr); /* bug-1028 */
/* Task 3.5: snapshot chunk transfer (sender side) */
RUN_TEST(test_snapshot_transfer_two_chunks);
RUN_TEST(test_snapshot_transfer_small_less_than_one_chunk);