feat(raft-apply-decoupling): defer job construction past the commit barrier (task 3.1)
Reorder handle_jobsubmit() so job objects are constructed via create_job() only after the WAL commit succeeds, not before. Adds jobid_range_exists() for a fast-fail pre-check with no WAL work, and persist_submit_job_payloads_and_commit() (persist.c) operating on raw xce_job_t* payloads instead of pre-existing job_t*. Commit remains synchronous -- converting to the continuation-based commit API is task 3.2. Fixes bug-1408 (found during review): the post-commit array- construction loop suppressed notification for an entire array batch if any one sibling element failed to construct, stranding other durably-committed and successfully-constructed jobs invisible until the next restart's replay. Notification now fires unconditionally for every successfully-constructed element, independent of sibling failures; the client reply still separately reports the batch's construction status.
This commit is contained in:
parent
8febd663d5
commit
6f1fa97fab
6 changed files with 871 additions and 399 deletions
|
|
@ -3610,6 +3610,44 @@ static int wal_hook_append_classified(persist_t* p, uint32_t wal_type,
|
|||
return rc; /* local append rc only — facts have no commit timeout */
|
||||
}
|
||||
|
||||
/* Shared step for a single WAL_JOB_SUBMIT entry (raft-apply-decoupling task
|
||||
* 3.1): encode `job`, append, and block on the majority-commit barrier.
|
||||
* Returns 0 when the entry committed on a majority; a negative rc otherwise
|
||||
* (-ETIMEDOUT: no majority within the deadline; -XCE_ERR_NOT_READY: admission
|
||||
* cap; -ENOMEM: encode failure; or a local-write error). Whenever the LOCAL
|
||||
* WAL write succeeded (rc == 0, -ETIMEDOUT, or -XCE_ERR_NOT_READY — the entry
|
||||
* was appended locally and the LSN advanced even though replication is
|
||||
* degraded), *out_lsn is set to the entry's assigned LSN so a caller can tag
|
||||
* it for egress-watermark purposes even on a rejected submission. */
|
||||
static int submit_job_wal_entry_and_commit(persist_t* p, const xce_job_t* job,
|
||||
uint64_t* out_lsn)
|
||||
{
|
||||
uint8_t* buf = NULL;
|
||||
size_t sz = 0;
|
||||
int rc;
|
||||
|
||||
if (capnp_encode_job(job, &buf, &sz) != 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ha-convergence-recovery (3.3): snapshot the LSN this submit entry
|
||||
* will receive BEFORE the append. persist_wal_async assigns
|
||||
* hdr.lsn = current_lsn then increments, so the pre-append current_lsn
|
||||
* is exactly the written entry's LSN. */
|
||||
pthread_mutex_lock(&p->lock);
|
||||
uint64_t submit_lsn = p->current_lsn;
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
|
||||
int sync_rc = 0;
|
||||
rc = persist_wal_sync_append(p, WAL_JOB_SUBMIT, buf, (uint32_t)sz,
|
||||
&sync_rc);
|
||||
free(buf);
|
||||
|
||||
if (rc == 0 || rc == -ETIMEDOUT || rc == -XCE_ERR_NOT_READY)
|
||||
*out_lsn = submit_lsn;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Explicit controller-side raft step for a job submission (raft-apply-decoupling
|
||||
* item #1 / bug-1306): WAL-append each job in `jlist` and block on the
|
||||
* majority-commit barrier, returning the commit verdict DIRECTLY to the caller
|
||||
|
|
@ -3633,32 +3671,36 @@ int persist_submit_wal_and_commit(persist_t* p, xlist_t* jlist)
|
|||
|
||||
for (int i = 0; i < jlist->count && rc == 0; i++) {
|
||||
job_t* j = (job_t*)xlist_get(jlist, i);
|
||||
uint8_t* buf = NULL;
|
||||
size_t sz = 0;
|
||||
if (capnp_encode_job(&j->job, &buf, &sz) != 0) {
|
||||
rc = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
/* ha-convergence-recovery (3.3): snapshot the LSN this submit entry
|
||||
* will receive BEFORE the append. persist_wal_async assigns
|
||||
* hdr.lsn = current_lsn then increments, so the pre-append current_lsn
|
||||
* is exactly the written entry's LSN. */
|
||||
pthread_mutex_lock(&p->lock);
|
||||
uint64_t submit_lsn = p->current_lsn;
|
||||
pthread_mutex_unlock(&p->lock);
|
||||
uint64_t lsn = 0;
|
||||
|
||||
int sync_rc = 0;
|
||||
rc = persist_wal_sync_append(p, WAL_JOB_SUBMIT, buf, (uint32_t)sz,
|
||||
&sync_rc);
|
||||
free(buf);
|
||||
|
||||
/* Tag the job with its submit entry's LSN whenever the LOCAL WAL write
|
||||
* succeeded. persist_wal_sync_append returns the persist_wal_async
|
||||
* error only on local-write failure; on a replication-degraded outcome
|
||||
* (0 / -ETIMEDOUT / -ENODEV / -XCE_ERR_NOT_READY) the entry was appended
|
||||
* locally and the LSN advanced, so the egress tag is set. */
|
||||
rc = submit_job_wal_entry_and_commit(p, &j->job, &lsn);
|
||||
if (rc == 0 || rc == -ETIMEDOUT || rc == -XCE_ERR_NOT_READY)
|
||||
j->submit_lsn = submit_lsn;
|
||||
j->submit_lsn = lsn;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Sibling of persist_submit_wal_and_commit for a job submission whose job
|
||||
* object(s) do not exist yet (raft-apply-decoupling task 3.1 / Req 2.1, 2.3):
|
||||
* `payloads` is an xlist_t of xce_job_t* that have never been registered in
|
||||
* any job table. On full success (return 0), out_lsns[i] holds payloads[i]'s
|
||||
* committed LSN — the caller constructs and tags the job object AFTER this
|
||||
* call returns, so a failed/timed-out commit never leaves a job object
|
||||
* behind (there is nothing to construct until this succeeds). Returns the
|
||||
* first negative rc on failure. */
|
||||
int persist_submit_job_payloads_and_commit(persist_t* p, xlist_t* payloads,
|
||||
uint64_t* out_lsns)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (p == NULL || payloads == NULL || out_lsns == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
for (int i = 0; i < payloads->count && rc == 0; i++) {
|
||||
xce_job_t* j = (xce_job_t*)xlist_get(payloads, i);
|
||||
|
||||
rc = submit_job_wal_entry_and_commit(p, j, &out_lsns[i]);
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
@ -3685,17 +3727,13 @@ int persist_job_hook_fun(int type, int situation, int detail,
|
|||
mode = wal_classify(wal_type, detail);
|
||||
|
||||
if (mode == WAL_MODE_WAIT) {
|
||||
/* WAL_JOB_SUBMIT durability (WAL append + majority-commit barrier) was
|
||||
* MOVED OUT of this hook into the explicit raft step
|
||||
* persist_submit_wal_and_commit(), called directly by handle_jobsubmit
|
||||
* (raft-apply-decoupling item #1 / bug-1306). Rationale: xce_run_hooks
|
||||
* returns only the LAST-registered hook's rc, so a later JOB AFTER_EVENT
|
||||
* subscriber (sched/apiserver, returning 0) CLOBBERED this branch's
|
||||
* -ETIMEDOUT — handle_jobsubmit then replied `jobsubmit ok` for an
|
||||
* entry that never reached majority, and a new-term leader legally
|
||||
* discarded it (committed-job-loss). The commit verdict must be read
|
||||
* from the protocol step directly, never as a hook return. This branch
|
||||
* is now a no-op; the AFTER_EVENT hook chain is notification-only. */
|
||||
/* WAL_JOB_SUBMIT durability (WAL append + majority-commit barrier) is
|
||||
* an explicit step invoked directly by handle_jobsubmit —
|
||||
* persist_submit_job_payloads_and_commit(), called strictly before
|
||||
* any job object is constructed (raft-apply-decoupling task 3.1 /
|
||||
* bug-1306) — never from this hook. This hook fires only after that
|
||||
* commit has already succeeded, purely as a notification, so there
|
||||
* is nothing left for it to do here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue