feat(raft-apply-decoupling): build apply_committed_entry single dispatch seam (task 2.2)
Add apply_committed_entry(persist_t*, wal_type, lsn, payload, len) in
persist.c/persist.h as the single seam that turns a committed WAL
entry into an in-memory mutation plus its post-mutation notification
hook, covering job submit, job-state facts, queue control, and
worker control. Job-state facts route through 2.1's guard-only
apply_job_update_transition() core and fire a generic
HOOK_DETAIL_JOB_UPDATE, not per-type hooks — the wire format
(wal_jobstate_t {jobid, state}) cannot supply what the type-specific
cores/hooks require. This is a real, documented deviation from
design.md's per-type dispatch wording; tasks.md's Implementation
Notes flags it as unresolved and blocking for whoever wires this
into a live path (4.1/6.2/6.3).
Not yet wired into any live call site (replay wiring is 2.3).
This commit is contained in:
parent
41d36204d5
commit
8c00f20cec
6 changed files with 689 additions and 1 deletions
|
|
@ -2578,6 +2578,186 @@ int apply_wal_entry(persist_t* p, uint32_t type, uint64_t lsn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* raft-apply-decoupling task 2.2 — see persist.h for the full contract doc
|
||||
* (routing table, the job-state-family hook-mapping deviation, and what this
|
||||
* function deliberately does NOT own). Not yet wired into any live call
|
||||
* site — replay (2.3) and the live apply pump (6.1) will call this later. */
|
||||
int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
|
||||
const void* payload, uint32_t len)
|
||||
{
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
if (!p->cluster)
|
||||
return 0; /* unit tests without a full cluster — nothing to dispatch into */
|
||||
|
||||
cluster_t* c = p->cluster;
|
||||
|
||||
switch (wal_type) {
|
||||
case WAL_JOB_SUBMIT: {
|
||||
int rc = restore_job(c, 0, payload, len, lsn);
|
||||
if (rc == -EEXIST)
|
||||
return 0; /* idempotent re-apply (replay / resync) — no hook */
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Recover the jobid via a cheap raw capnp field read (read_Job only
|
||||
* reads the fixed uint64 field — no allocation, unlike decode_Job)
|
||||
* so the just-constructed job can be looked up for the notification
|
||||
* hook without changing restore_job's own contract. */
|
||||
struct capn jc_ctx;
|
||||
capn_init_mem(&jc_ctx, (const uint8_t*)payload, len, 0);
|
||||
Job_ptr jp;
|
||||
jp.p = capn_getp(capn_root(&jc_ctx), 0, 1);
|
||||
struct Job cj;
|
||||
read_Job(&cj, jp);
|
||||
uint64_t jobid = cj.jobid;
|
||||
capn_free(&jc_ctx);
|
||||
|
||||
job_t* j = NULL;
|
||||
lock(c->jobmgr, RTTI_LOCK_READ);
|
||||
key_find(c->jobmgr, (void*)jobid, (void**)&j);
|
||||
unlock(c->jobmgr);
|
||||
if (!j)
|
||||
return 0; /* defensive: should not happen after a successful restore */
|
||||
|
||||
void* jptr = j;
|
||||
xlist_t jlist = { .count = 1, .caps = 1, .delta = 1, .data = &jptr };
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_JOB, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_NEW, c, &jlist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WAL_JOB_START:
|
||||
case WAL_JOB_FINISH:
|
||||
case WAL_JOB_FAIL:
|
||||
case WAL_JOB_STOP:
|
||||
case WAL_JOB_RESUME:
|
||||
case WAL_JOB_UPDATE:
|
||||
case WAL_JOB_RUSAGE:
|
||||
case WAL_JOB_CKPT:
|
||||
case WAL_JOB_PREASON: {
|
||||
/* Every job-state fact type shares one wire shape — wal_jobstate_t
|
||||
* {jobid, state} — and therefore one guarded core. See persist.h's
|
||||
* doc comment on apply_committed_entry for why this does not
|
||||
* dispatch to the type-specific apply_job_{start,finish,fail}_
|
||||
* transition() cores (their richer parameter structs are not
|
||||
* present on this wire format). */
|
||||
if (len < sizeof(wal_jobstate_t))
|
||||
return 0; /* undersized payload — skip, mirrors apply_wal_entry */
|
||||
|
||||
const wal_jobstate_t* js = (const wal_jobstate_t*)payload;
|
||||
job_t* j = NULL;
|
||||
lock(c->jobmgr, RTTI_LOCK_READ);
|
||||
key_find(c->jobmgr, (void*)js->jobid, (void**)&j);
|
||||
unlock(c->jobmgr);
|
||||
if (!j)
|
||||
return -ENOENT;
|
||||
|
||||
lock(j, RTTI_LOCK_WRITE);
|
||||
job_transition_result_t result =
|
||||
apply_job_update_transition(j, (int)js->state);
|
||||
unlock(j);
|
||||
|
||||
if (result != JOB_TRANSITION_APPLIED)
|
||||
return 0; /* rejected-backward / no-op-duplicate — no hook */
|
||||
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_JOB, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_JOB_UPDATE, c, j);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WAL_QUEUE_CTRL: {
|
||||
if (len <= sizeof(wal_queuehdr_t))
|
||||
return 0; /* undersized payload — skip, mirrors apply_wal_entry */
|
||||
|
||||
const wal_queuehdr_t* qh = (const wal_queuehdr_t*)payload;
|
||||
const char* name =
|
||||
(const char*)((const uint8_t*)payload + sizeof(wal_queuehdr_t));
|
||||
|
||||
/* restore_queue_state's -ENOENT ("queue not yet built from config")
|
||||
* and -EINVAL (bad name) are both benign skips here, matching
|
||||
* apply_wal_entry's existing behavior of never fencing on this
|
||||
* entry type's restore outcome. */
|
||||
if (restore_queue_state(c, name, qh->status) < 0)
|
||||
return 0;
|
||||
|
||||
queue_t* q = NULL;
|
||||
lock(c->qmgr, RTTI_LOCK_READ);
|
||||
key_find(c->qmgr, (void*)name, (void**)&q);
|
||||
unlock(c->qmgr);
|
||||
if (!q)
|
||||
return 0;
|
||||
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_QUEUE, HOOK_AFTER_EVENT,
|
||||
HOOK_DETAIL_QUEUE_UPDATE, c, q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WAL_WORKER_CTRL: {
|
||||
if (len <= WORKER_WAL_HDR_SIZE)
|
||||
return 0; /* undersized payload — skip, mirrors apply_wal_entry */
|
||||
|
||||
uint32_t hook_detail;
|
||||
memcpy(&hook_detail, payload, sizeof(hook_detail));
|
||||
|
||||
int rc = worker_from_capnp(c, payload, len);
|
||||
if (rc == -EEXIST)
|
||||
return 0; /* idempotent re-apply (replay / resync) — no hook */
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Recover the worker's name via a cheap raw capnp field read
|
||||
* (read_Worker's capn_get_text is a borrowed, NUL-terminated pointer
|
||||
* into the decode buffer — no allocation, unlike decode_Worker) so
|
||||
* the just-restored worker can be looked up for the notification
|
||||
* hook without changing worker_from_capnp's own contract. */
|
||||
const uint8_t* wpayload = (const uint8_t*)payload + WORKER_WAL_HDR_SIZE;
|
||||
size_t wlen = len - WORKER_WAL_HDR_SIZE;
|
||||
struct capn wc_ctx;
|
||||
capn_init_mem(&wc_ctx, wpayload, wlen, 0);
|
||||
Worker_ptr wp;
|
||||
wp.p = capn_getp(capn_root(&wc_ctx), 0, 1);
|
||||
struct Worker cw;
|
||||
read_Worker(&cw, wp);
|
||||
|
||||
worker_t* w = NULL;
|
||||
lock(c->workermgr, RTTI_LOCK_READ);
|
||||
key_find(c->workermgr, (void*)cw.name.str, (void**)&w);
|
||||
unlock(c->workermgr);
|
||||
capn_free(&wc_ctx);
|
||||
if (!w)
|
||||
return 0; /* defensive: should not happen after a successful restore */
|
||||
|
||||
switch (hook_detail) {
|
||||
case HOOK_DETAIL_WORKER_JOIN:
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_WORKER,
|
||||
HOOK_AFTER_EVENT, HOOK_DETAIL_WORKER_JOIN, c,
|
||||
w->host.name, w);
|
||||
break;
|
||||
case HOOK_DETAIL_WORKER_UPDATE:
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_WORKER,
|
||||
HOOK_AFTER_EVENT, HOOK_DETAIL_WORKER_UPDATE, c, w);
|
||||
break;
|
||||
case HOOK_DETAIL_WORKER_DEAD:
|
||||
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_WORKER,
|
||||
HOOK_AFTER_EVENT, HOOK_DETAIL_WORKER_DEAD, c, w);
|
||||
break;
|
||||
default:
|
||||
break; /* unrecognized discriminator — mutation already applied,
|
||||
* notification skipped rather than guessed at */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
/* WAL_NOOP / WAL_CONFIG_MARKER / WAL_CHECKPOINT / unknown — no
|
||||
* entity effect at this seam, no hook (unchanged from
|
||||
* apply_wal_entry's existing treatment of these types). */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read WAL segment files in LSN order; replay entries with lsn > from_lsn.
|
||||
*
|
||||
* Segment filename format: seg-<start_lsn_hex16>.wal
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue