feat: persistence hook functions wired to WAL write on AFTER_EVENT (P0-6)

Implements persist_job_hook_fun, persist_queue_hook_fun, and
persist_worker_hook_fun — AFTER_EVENT hooks that write WAL entries
whenever state mutations occur in the controller.

- detail_to_wal_type() maps all 15 HOOK_DETAIL_* values to WAL types;
  unknown details return 0 and are silently skipped
- HOOK_DETAIL_JOB_NEW uses the sync replication path (WAL_JOB_SUBMIT);
  all others use persist_wal_async
- P0 payload: 4-byte detail value; full Cap'n Proto serialization in Task 15
- Hook signature matches hook_fn_t exactly:
  int fn(int type, int situation, int detail, void* cls, va_list ap)
- Added <stdarg.h> to persist.h for va_list; added hook.h and
  xce/cluster_def.h includes to persist.c
- 18 new unit tests in test_persist_hooks.c covering all 15 detail→WAL
  mappings, skip-on-unknown, and sequential LSN advancement
This commit is contained in:
Rongsong Shen 2026-05-07 12:28:20 +00:00
parent 045d9e3287
commit 6f3b98c6b5
5 changed files with 500 additions and 0 deletions

View file

@ -1,6 +1,8 @@
#define _GNU_SOURCE
#include "persist.h"
#include "config.h"
#include "hook.h"
#include "xce/cluster_def.h"
#include <assert.h>
#include <dirent.h>
@ -785,3 +787,89 @@ void persist_log_entry_metadata(const wal_entry_hdr_t* hdr)
hdr->payload_len,
hdr->crc);
}
/* ------------------------------------------------------------------ */
/* Hook functions — AFTER_EVENT handlers that write WAL entries */
/* ------------------------------------------------------------------ */
/* Map HOOK_DETAIL_* values to WAL entry types.
* Returns 0 for details that have no WAL representation (skip). */
static uint32_t detail_to_wal_type(int detail)
{
switch (detail) {
case HOOK_DETAIL_JOB_NEW: return WAL_JOB_SUBMIT;
case HOOK_DETAIL_JOB_START: return WAL_JOB_START;
case HOOK_DETAIL_JOB_EXIT: return WAL_JOB_FINISH;
case HOOK_DETAIL_JOB_FAIL: return WAL_JOB_FAIL;
case HOOK_DETAIL_JOB_STOP: return WAL_JOB_STOP;
case HOOK_DETAIL_JOB_RESUME: return WAL_JOB_RESUME;
case HOOK_DETAIL_JOB_UPDATE: return WAL_JOB_UPDATE;
case HOOK_DETAIL_JOB_CKPT: return WAL_JOB_CKPT;
case HOOK_DETAIL_JOB_RUSAGE_UPDATE: return WAL_JOB_RUSAGE;
case HOOK_DETAIL_SETPREASON: return WAL_JOB_PREASON;
case HOOK_DETAIL_CLRPREASON: return WAL_JOB_PREASON;
case HOOK_DETAIL_QUEUE_UPDATE: return WAL_QUEUE_CTRL;
case HOOK_DETAIL_WORKER_JOIN: return WAL_WORKER_CTRL;
case HOOK_DETAIL_WORKER_UPDATE: return WAL_WORKER_CTRL;
case HOOK_DETAIL_WORKER_DEAD: return WAL_WORKER_CTRL;
default: return 0; /* unknown — skip */
}
}
/* P0 payload: just the detail value (4 bytes).
* Full Cap'n Proto serialization is wired in Task 15. */
int persist_job_hook_fun(int type, int situation, int detail,
void* cls, va_list ap)
{
(void)type;
(void)situation;
(void)ap;
persist_t* p = cls;
uint32_t wal_type = detail_to_wal_type(detail);
if (wal_type == 0)
return 0; /* unknown detail — skip */
uint32_t payload = (uint32_t)detail;
if (wal_type == WAL_JOB_SUBMIT) {
/* Job submission uses sync replication path */
int sync_rc = 0;
persist_wal_sync_append(p, wal_type, &payload, sizeof(payload), &sync_rc);
return sync_rc;
}
return persist_wal_async(p, wal_type, &payload, sizeof(payload));
}
int persist_queue_hook_fun(int type, int situation, int detail,
void* cls, va_list ap)
{
(void)type;
(void)situation;
(void)ap;
persist_t* p = cls;
uint32_t wal_type = detail_to_wal_type(detail);
if (wal_type == 0)
return 0;
uint32_t payload = (uint32_t)detail;
return persist_wal_async(p, wal_type, &payload, sizeof(payload));
}
int persist_worker_hook_fun(int type, int situation, int detail,
void* cls, va_list ap)
{
(void)type;
(void)situation;
(void)ap;
persist_t* p = cls;
uint32_t wal_type = detail_to_wal_type(detail);
if (wal_type == 0)
return 0;
uint32_t payload = (uint32_t)detail;
return persist_wal_async(p, wal_type, &payload, sizeof(payload));
}