#pragma once /* * waldump.h — read-only WAL segment inspector for failover/durability triage. * * Decodes the on-disk WAL (`wal_entry_hdr_t` + payload, see wal_types.h), * lists every physical entry, decodes JOB_SUBMIT jobids via the Cap'n Proto * Job schema, and — the decisive signal for the committed-job-loss bug — * flags LSN gaps (a missing prefix, or any non-contiguous hole) and reports * the CRC-chain "recovered tip" a real recovery would stop at. * * Strictly read-only: opens segments O_RDONLY, applies no entries, mutates no * persist_t state. Safe to run against a live node's data dir. */ #include #include /* Aggregate result of scanning one path (a seg file, a wal/ dir, or a node * data dir). All counts are over the physically-present, CRC-valid entries. */ typedef struct { uint64_t first_lsn; /* lowest LSN seen (0 = no entries) */ uint64_t last_lsn; /* highest LSN seen */ uint64_t recovered_tip; /* highest LSN reachable via an intact CRC chain */ uint32_t n_entries; /* total entries scanned */ uint32_t n_submits; /* WAL_JOB_SUBMIT entries */ uint32_t n_gaps; /* LSN discontinuities (missing prefix counts as 1) */ uint32_t n_segments; /* segment files scanned */ uint32_t n_dup_submits; /* JOB_SUBMITs with same jobid as the immediately- * preceding entry at an adjacent LSN (bug-845) */ uint32_t n_dup_identical; /* of n_dup_submits, those whose payload is * byte-identical to the prior entry */ int crc_broken; /* nonzero if a CRC/chain break truncated a segment */ int leading_gap_explained; /* nonzero if the leading (prefix) gap's * upper LSN is covered by a sibling * data/snap/snap-.capnp file — i.e. * normal checkpoint-triggered WAL trim * (persist.c trim_wal_segments), not * genuine loss. 0 if there was no leading * gap, or no covering snapshot was found. */ } waldump_stats_t; /* One physically-present, CRC-valid WAL entry, surfaced to a visitor so a * caller (e.g. walcompare) can index the log by `(lsn -> term, pcrc)` without * re-implementing the segment/CRC parse. `pcrc` is the payload-only CRC32 (0 * for an empty payload) — the same fingerprint waldump prints, isolating the * blob from the header so a same-LSN content divergence is detectable. */ typedef struct { uint64_t lsn; uint64_t term; /* raw on-disk term (legacy 0 kept verbatim) */ uint32_t type; /* WAL entry type (wal_types.h) */ uint32_t payload_len; /* payload byte count */ uint32_t pcrc; /* CRC32 of payload only (0 if payload_len == 0) */ } waldump_entry_t; /* Per-entry visitor. Invoked once per CRC-valid entry in ascending scan order * (before the gap line is printed). Return 0 to continue, non-zero to stop the * scan early. `ctx` is the caller's opaque pointer. */ typedef int (*waldump_visit_fn)(void* ctx, const waldump_entry_t* e); /* Dump every WAL entry under `path` to `out` (human-readable) and fill * `*stats` when non-NULL. `path` may be: * - a single seg-*.wal file, * - a directory holding seg-*.wal files (a wal/ dir), or * - a node/data dir whose wal/ subdir (data/wal or wal) holds the segments. * Returns 0 on success, or a negative errno on a hard I/O error (e.g. the * path does not exist / no segments found). Gaps and CRC breaks are reported * through `out`/`stats`, NOT via the return code. */ int waldump_path(const char* path, FILE* out, waldump_stats_t* stats); /* Same as waldump_path, but also invokes `visit(ctx, &entry)` for every * CRC-valid entry so a caller can collect the `(lsn -> term, pcrc)` index. * `visit` may be NULL (then identical to waldump_path). `out` may be a sink * the caller does not care about (e.g. a /dev/null FILE) — the human dump is * still produced. The single canonical WAL decoder is reused; no second * segment parser. */ int waldump_path_visit(const char* path, FILE* out, waldump_stats_t* stats, waldump_visit_fn visit, void* ctx);