feat(config-upgrade-snapshot): add configcompare tool, extend snapverify (task 8.4)

Build configcompare_paths(), an offline sibling to snapdiff/walcompare that
recomputes each member's active-config sha256 fresh from its own on-disk
workdir/name/conf bytes and reports identical vs. divergent (Req 4.2).
Wire it as a cmd_configcompare CLI subcommand in test.c, matching its
siblings, after review caught it shipped as an unreachable library function
in round 1.

Extend snapverify to match the current on-disk checkpoint format: bump
SNAP_FORMAT_VERSION to 2, replace the stale raw queue-frame decode with the
capnp length-prefixed walk, add host-frame and config-bytes-section
structural walks, and decode (not just presence-check) the header's
cfgref tag/sha. Without this fix snapverify rejected every checkpoint
written by the current codebase.
This commit is contained in:
Rongsong Shen 2026-07-25 09:06:24 +00:00
parent c5d8ecbd7d
commit 1d09631df0
10 changed files with 997 additions and 47 deletions

View file

@ -27,6 +27,7 @@
#include "waldump.h" /* offline WAL inspector (cmd_waldump) */
#include "snapdiff.h" /* byte-level checkpoint-file comparator (cmd_snapdiff) */
#include "snapverify.h" /* offline checkpoint-file structural validator (cmd_snapverify) */
#include "configcompare.h" /* cross-member active-config comparator (cmd_configcompare) */
#include "xce/cluster.h" /* xce_parse_peers, xce_cluster_config_t */
#include "xce/types.h"
@ -133,6 +134,7 @@ extern int cmd_waldump(int, char*[]);
extern int cmd_walcompare(int, char*[]);
extern int cmd_snapverify(int, char*[]);
extern int cmd_snapdiff(int, char*[]);
extern int cmd_configcompare(int, char*[]);
static int worker_house_keeping(void*);
/* Session verdict / loop state (xce-test-e2e-primitives task 1.1).
@ -194,6 +196,7 @@ static command_t top_commands[] = {
{ "walcompare", cmd_walcompare, "Diff member committed WAL prefixes" },
{ "snapverify", cmd_snapverify, "Verify checkpoint file structure" },
{ "snapdiff", cmd_snapdiff, "Byte-diff two checkpoint files" },
{ "configcompare", cmd_configcompare, "Compare active config hash across members" },
{ "quit", cmd_quit, "Quit xce test" },
{ (char*)NULL, (cmd_func_t)NULL, (char*)NULL }
};
@ -237,6 +240,7 @@ static command_t client_commands[] = {
{ "walcompare", cmd_walcompare, "Diff member committed WAL prefixes" },
{ "snapverify", cmd_snapverify, "Verify checkpoint file structure" },
{ "snapdiff", cmd_snapdiff, "Byte-diff two checkpoint files" },
{ "configcompare", cmd_configcompare, "Compare active config hash across members" },
{ "shutdown", cmd_shutdown, "shutdown xce controller" },
{ "jobrun", cmd_jobrun, "run a job" },
{ "jobsub", cmd_jobsub, "submit a job" },
@ -856,6 +860,27 @@ int cmd_snapdiff(int argc, char* argv[])
return (rc == 0) ? 0 : -1;
}
/* configcompare <member_dir> <member_dir> [more...] — cross-member
* active-configuration comparator (config-upgrade-snapshot task 8.4, Req
* 4.2). Each path is a member's `workdir/name` conf-root directory (the
* same directory persist.c's read_active_config_bytes() and config_repl.c's
* local_config_sha256() resolve `conf/current` under) — NOT a raw config
* file path. Recomputes each member's active-config sha256 fresh from its
* own on-disk bytes and reports whether all members match. Returns 0 if
* all members are present and identical, nonzero if any member is
* unreadable or divergent. */
int cmd_configcompare(int argc, char* argv[])
{
if (argc < 3) {
fprintf(stderr, "usage: configcompare <member_dir> <member_dir> [more...]\n");
return -1;
}
configcompare_report_t rep;
int rc = configcompare_paths((const char* const*)&argv[1], argc - 1, stdout, &rep);
return (rc == 0) ? 0 : -1;
}
int cmd_up(int argc, char* argv[])
{
if (active_ctx->parent != NULL) {