fix(waldump): decode WAL_CONFIG_MARKER, distinguish trimmed gaps from real loss
wal_type_name() never had a case for WAL_CONFIG_MARKER (type 50) -- every config-change marker entry printed as UNKNOWN instead of a real, defined type. Added the case plus payload decode (tag/sha256) matching config_repl.c's config_marker_encode/_decode byte layout, hand-decoded here rather than linking config_repl.c into xce_test's link graph. Gap detection also had no way to tell "committed prefix missing!" (real loss) apart from a normal checkpoint-triggered WAL trim (persist.c's trim_wal_segments, floor-guarded so it never removes a segment a standby still needs) -- every long-running node's WAL, once checkpointed, would false-alarm on every inspection. Cross-checks the sibling data/snap/ directory (snapshot LSN is literally in the filename, snap-<hex>.capnp) and downgrades the message when a snapshot covers the gap. Adds waldump_stats_t.leading_gap_explained and 3 new tests plus one assertion on the existing gap test.
This commit is contained in:
parent
0ca96359f5
commit
a86de77f91
3 changed files with 228 additions and 5 deletions
|
|
@ -2,6 +2,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
|
@ -152,6 +154,135 @@ static void test_missing_prefix_flagged_as_gap(void)
|
|||
TEST_ASSERT_EQUAL_UINT64(13, st.first_lsn);
|
||||
/* prefix [1..12] absent → exactly one gap */
|
||||
TEST_ASSERT_EQUAL_UINT(1, st.n_gaps);
|
||||
/* g_dir has no sibling data/snap/ at all → nothing can explain it */
|
||||
TEST_ASSERT_EQUAL_INT(0, st.leading_gap_explained);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* WAL_CONFIG_MARKER must decode as its own named type, not fall into */
|
||||
/* the generic UNKNOWN bucket (a real, defined type — wal_types.h). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_config_marker_type_decoded(void)
|
||||
{
|
||||
fresh_dir();
|
||||
char path[512];
|
||||
FILE* fp = fopen(seg_path(1, path, sizeof(path)), "wb");
|
||||
TEST_ASSERT_NOT_NULL(fp);
|
||||
|
||||
uint32_t last = WAL_CRC_NONE;
|
||||
put_entry(fp, 0, 1, WAL_NOOP, NULL, 0, &last, 1);
|
||||
|
||||
uint8_t marker[WAL_CONFIG_MARKER_PAYLOAD_LEN];
|
||||
uint64_t tag = 0x1122334455667788ULL;
|
||||
for (int i = 0; i < 8; i++)
|
||||
marker[i] = (uint8_t)(tag >> (56 - i * 8));
|
||||
memset(marker + 8, 0xab, 32);
|
||||
put_entry(fp, 3, 2, WAL_CONFIG_MARKER, marker, sizeof(marker), &last, 0);
|
||||
fclose(fp);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t bufsz = 0;
|
||||
FILE* mem = open_memstream(&buf, &bufsz);
|
||||
TEST_ASSERT_NOT_NULL(mem);
|
||||
|
||||
waldump_stats_t st;
|
||||
int rc = waldump_path(g_dir, mem, &st);
|
||||
fclose(mem);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "CONFIG_MARKER"));
|
||||
TEST_ASSERT_NULL(strstr(buf, "UNKNOWN"));
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "tag=1122334455667788"));
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* A leading gap covered by a sibling data/snap/ snapshot is normal, */
|
||||
/* checkpoint-triggered WAL trim — waldump must say so, not alarm. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_leading_gap_explained_by_covering_snapshot(void)
|
||||
{
|
||||
fresh_dir();
|
||||
char wal_dir[300], snap_dir[300];
|
||||
snprintf(wal_dir, sizeof(wal_dir), "%s/data/wal", g_dir);
|
||||
snprintf(snap_dir, sizeof(snap_dir), "%s/data/snap", g_dir);
|
||||
char data_dir[300];
|
||||
snprintf(data_dir, sizeof(data_dir), "%s/data", g_dir);
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(data_dir, 0755));
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(wal_dir, 0755));
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(snap_dir, 0755));
|
||||
|
||||
/* Snapshot at LSN 12 covers the trimmed prefix [1..12] below. */
|
||||
char snap_file[400];
|
||||
snprintf(snap_file, sizeof(snap_file), "%s/snap-%016x.capnp", snap_dir,
|
||||
0xc);
|
||||
FILE* sf = fopen(snap_file, "wb");
|
||||
TEST_ASSERT_NOT_NULL(sf);
|
||||
fclose(sf);
|
||||
|
||||
char segp[400];
|
||||
snprintf(segp, sizeof(segp), "%s/seg-%016lx.wal", wal_dir,
|
||||
(unsigned long)13);
|
||||
FILE* fp = fopen(segp, "wb");
|
||||
TEST_ASSERT_NOT_NULL(fp);
|
||||
uint32_t last = WAL_CRC_NONE;
|
||||
put_entry(fp, 0, 13, WAL_NOOP, NULL, 0, &last, 1);
|
||||
fclose(fp);
|
||||
|
||||
char* buf = NULL;
|
||||
size_t bufsz = 0;
|
||||
FILE* mem = open_memstream(&buf, &bufsz);
|
||||
TEST_ASSERT_NOT_NULL(mem);
|
||||
|
||||
waldump_stats_t st;
|
||||
int rc = waldump_path(g_dir, mem, &st);
|
||||
fclose(mem);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_UINT(1, st.n_gaps);
|
||||
TEST_ASSERT_NOT_EQUAL(0, st.leading_gap_explained);
|
||||
TEST_ASSERT_NOT_NULL(strstr(buf, "covered by snapshot"));
|
||||
TEST_ASSERT_NULL(strstr(buf, "committed prefix missing!"));
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* A snapshot that does NOT cover the full gap must NOT explain it — */
|
||||
/* the alarming wording stays, since the gap could still be real loss. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
static void test_leading_gap_not_explained_by_insufficient_snapshot(void)
|
||||
{
|
||||
fresh_dir();
|
||||
char wal_dir[300], snap_dir[300], data_dir[300];
|
||||
snprintf(data_dir, sizeof(data_dir), "%s/data", g_dir);
|
||||
snprintf(wal_dir, sizeof(wal_dir), "%s/data/wal", g_dir);
|
||||
snprintf(snap_dir, sizeof(snap_dir), "%s/data/snap", g_dir);
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(data_dir, 0755));
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(wal_dir, 0755));
|
||||
TEST_ASSERT_EQUAL_INT(0, mkdir(snap_dir, 0755));
|
||||
|
||||
/* Snapshot at LSN 5 does NOT cover the trimmed prefix [1..12] below. */
|
||||
char snap_file[400];
|
||||
snprintf(snap_file, sizeof(snap_file), "%s/snap-%016x.capnp", snap_dir,
|
||||
0x5);
|
||||
FILE* sf = fopen(snap_file, "wb");
|
||||
TEST_ASSERT_NOT_NULL(sf);
|
||||
fclose(sf);
|
||||
|
||||
char segp[400];
|
||||
snprintf(segp, sizeof(segp), "%s/seg-%016lx.wal", wal_dir,
|
||||
(unsigned long)13);
|
||||
FILE* fp = fopen(segp, "wb");
|
||||
TEST_ASSERT_NOT_NULL(fp);
|
||||
uint32_t last = WAL_CRC_NONE;
|
||||
put_entry(fp, 0, 13, WAL_NOOP, NULL, 0, &last, 1);
|
||||
fclose(fp);
|
||||
|
||||
waldump_stats_t st;
|
||||
int rc = waldump_path(g_dir, stdout, &st);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_UINT(1, st.n_gaps);
|
||||
TEST_ASSERT_EQUAL_INT(0, st.leading_gap_explained);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -288,6 +419,9 @@ void test_waldump(void)
|
|||
UnitySetTestFile(__FILE__);
|
||||
RUN_TEST(test_contiguous_log_has_no_gaps);
|
||||
RUN_TEST(test_missing_prefix_flagged_as_gap);
|
||||
RUN_TEST(test_config_marker_type_decoded);
|
||||
RUN_TEST(test_leading_gap_explained_by_covering_snapshot);
|
||||
RUN_TEST(test_leading_gap_not_explained_by_insufficient_snapshot);
|
||||
RUN_TEST(test_duplicate_submit_detection);
|
||||
RUN_TEST(test_interior_gap_flagged);
|
||||
RUN_TEST(test_crc_break_sets_recovered_tip);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue