forked from lei.chen/myxce
feat: P2-2 apply_wal_entry — standby WAL replay with LSN advance and type guards
Upgrade apply_wal_entry() from P0 static no-op stub to the real public standby replay path: - Reject oversized payloads (> MAX_WAL_PAYLOAD_BYTES) with -EMSGSIZE - Skip WAL_CHECKPOINT (100) entries silently — local-only markers, never replicated to standbys; current_lsn is NOT advanced on skip - Skip unknown types (> WAL_ENTRY_TYPE_MAX) with WARN log and rc=0 for forward compatibility with WAL written by newer controller versions - Advance current_lsn to hdr->lsn+1 and update last_crc under p->lock for all accepted known entries - Gate restore_job()/worker_from_capnp() calls on p->cluster != NULL so unit tests work without a full cluster wired in - Add NULL guard returning -EINVAL for missing p or hdr Add public declaration in persist.h with forward-declare stubs for restore_job/worker_from_capnp in persist.c (avoids heavy cluster.h chain). Test suite: 5 new Unity tests in test_repl_apply.c covering all branches. Weak stubs for restore_job/worker_from_capnp in the test file satisfy the linker without linking job.c/worker.c's heavy dependency graph. All 5 new tests pass; full controller suite green.
This commit is contained in:
parent
8ef64a7ee7
commit
abefc8a128
5 changed files with 275 additions and 7 deletions
|
|
@ -23,6 +23,14 @@
|
|||
#include <unistd.h>
|
||||
#include <zlib.h>
|
||||
|
||||
/* Forward declarations for P0 restore stubs (defined in job.c / worker.c).
|
||||
* Avoids pulling in the heavy cluster.h / worker.h header chains here.
|
||||
* Full Cap'n Proto wiring happens in P2-4+. */
|
||||
extern int restore_job(cluster_t* c, uint64_t jobid,
|
||||
const void* capnp_payload, size_t len);
|
||||
extern int worker_from_capnp(cluster_t* c,
|
||||
const void* capnp_payload, size_t len);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Constants */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -527,14 +535,68 @@ static int load_snapshot(persist_t* p, const char* path, uint64_t lsn)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* P0 stub: no job/worker deserialization yet — wired in Task 12+/15. */
|
||||
static int apply_wal_entry(persist_t* p, const wal_entry_hdr_t* hdr,
|
||||
const void* payload)
|
||||
/* P2-2: Standby WAL replay — apply a single replicated entry to in-memory state.
|
||||
*
|
||||
* No hooks fire on standbys; the hook system is empty until promotion.
|
||||
*
|
||||
* Returns 0 on success (including skip for checkpoint / unknown types),
|
||||
* negative errno on error (NULL args, oversized payload). */
|
||||
int apply_wal_entry(persist_t* p, const wal_entry_hdr_t* hdr, const void* payload)
|
||||
{
|
||||
(void)p;
|
||||
(void)hdr;
|
||||
(void)payload;
|
||||
/* Full implementation in Task 12+ (hook wiring) and Task 15 (cluster wiring) */
|
||||
if (!p || !hdr)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reject oversized payloads BEFORE any allocation */
|
||||
if (hdr->payload_len > MAX_WAL_PAYLOAD_BYTES) {
|
||||
fprintf(stderr,
|
||||
"apply_wal_entry: payload_len %u exceeds MAX_WAL_PAYLOAD_BYTES %u\n",
|
||||
hdr->payload_len, MAX_WAL_PAYLOAD_BYTES);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* WAL_CHECKPOINT (100) is a local-only marker — standbys never receive it
|
||||
* via replication. Skip silently; do NOT advance LSN. */
|
||||
if (hdr->type == WAL_CHECKPOINT)
|
||||
return 0;
|
||||
|
||||
/* Unknown types above WAL_ENTRY_TYPE_MAX: log WARN and skip for forward
|
||||
* compatibility — an older controller can boot on WAL written by a newer
|
||||
* one without hard-failing on unrecognised entry types. */
|
||||
if (hdr->type > WAL_ENTRY_TYPE_MAX) {
|
||||
fprintf(stderr,
|
||||
"WARN apply_wal_entry: unknown type %u > WAL_ENTRY_TYPE_MAX %u"
|
||||
" (lsn=%" PRIu64 ") — skipping for forward compatibility\n",
|
||||
hdr->type, WAL_ENTRY_TYPE_MAX, hdr->lsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Advance LSN chain: track the highest replicated LSN seen. */
|
||||
pthread_mutex_lock(&p->lock);
|
||||
if (hdr->lsn + 1 > p->current_lsn)
|
||||
p->current_lsn = hdr->lsn + 1;
|
||||
p->last_crc = hdr->crc;
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
|
||||
/* Entity-creating entries: delegate to existing restore stubs.
|
||||
* Gate on p->cluster != NULL so unit tests without a full cluster work. */
|
||||
switch (hdr->type) {
|
||||
case WAL_JOB_SUBMIT:
|
||||
/* restore_job() is a P0 stub — full Cap'n Proto decode in P2-4+. */
|
||||
if (p->cluster)
|
||||
restore_job(p->cluster, 0 /* jobid decoded from payload in P2-4 */,
|
||||
payload, hdr->payload_len);
|
||||
break;
|
||||
case WAL_WORKER_CTRL:
|
||||
/* worker_from_capnp() is a P0 stub — full decode in P2-4+. */
|
||||
if (p->cluster)
|
||||
worker_from_capnp(p->cluster, payload, hdr->payload_len);
|
||||
break;
|
||||
default:
|
||||
/* All other known types: LSN/CRC already advanced above.
|
||||
* Full per-type handling wired in P2-4 (Cap'n Proto decode). */
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue