forked from lei.chen/myxce
feat(raft-apply-decoupling): convert job submit to continuation-based commit (task 3.2)
Replace handle_jobsubmit()'s blocking commit call (task 3.1) with a real async registration against commit_submit_async(). On FAILED/timeout, replies failure immediately with no job constructed. On COMMITTED, hands off to a new pending-apply registration (persist_register_pending_apply) for the ordered apply pump (task 6.1, not yet built) to consume -- does not apply or reply directly, per design. Deviates from design.md's stated integration site: appends via the same ha_primary_log_append/persist_wal_async primitive the RETAIN-fact path already uses and calls commit_submit_async directly on p->repl.ce, bypassing repl_wait_majority_ack/repl_submit_majority_commit in repl.c. Verified safe -- majority-ack detection is driven entirely by (ce, lsn) events inside the commit engine, agnostic to which wrapper registered the continuation -- but narrows the reusable primitive design.md intended; documented in tasks.md. Deletes (not preserves-dead) task 3.1's bug-1408 notification fix -- apply_committed_entry()'s WAL_JOB_SUBMIT case (task 2.2) already fires the notification per-entry, immune to the batch-suppression class bug-1408 fixed. IMPORTANT: no live path applies a successfully-committed job submission until task 6 lands (only restart replay does) -- every successful submission commits durably but gets zero reply until then. This commit range must not be soak-tested or deployed against live traffic before task 6 completes.
This commit is contained in:
parent
6f1fa97fab
commit
df4d2fa0ed
5 changed files with 689 additions and 299 deletions
|
|
@ -291,6 +291,17 @@ struct persist_s {
|
|||
atomic_int checkpoint_in_progress; /* CAS guard: 0=idle, 1=scheduled/running */
|
||||
atomic_int fsync_stop;
|
||||
atomic_int fsync_thread_running;
|
||||
|
||||
/* raft-apply-decoupling task 3.2: pending command-apply hand-off list
|
||||
* (producer side; the not-yet-built ordered apply pump, task 6.1/6.2,
|
||||
* is the consumer). Opaque here (pending_command_apply_t is private to
|
||||
* persist.c) — guarded by its OWN dedicated lock, deliberately NOT
|
||||
* `lock` above, since entries are appended from a commit continuation
|
||||
* callback (dispatched off-thread via vtask_enq_classified) that must
|
||||
* never contend with the hot WAL-append path. See
|
||||
* persist_register_pending_apply's doc comment for the full contract. */
|
||||
void* pending_applies;
|
||||
pthread_mutex_t pending_applies_lock;
|
||||
#ifdef XCE_TESTING
|
||||
/* Fault-injection seam (ha-commit-durability task 2.1 error-path test):
|
||||
* when non-zero, every fdatasync site treats the sync as FAILED — it
|
||||
|
|
@ -732,6 +743,91 @@ int persist_submit_wal_and_commit(persist_t* p, xlist_t* jlist);
|
|||
int persist_submit_job_payloads_and_commit(persist_t* p, xlist_t* payloads,
|
||||
uint64_t* out_lsns);
|
||||
|
||||
/* raft-apply-decoupling task 3.2: outcome delivered to a job submission's
|
||||
* per-entry commit continuation once its commit_submit_async() registration
|
||||
* resolves (or, for the LSN-0 / engine-not-wired edge cases, immediately
|
||||
* and synchronously). rc == 0 means the entry committed on a majority;
|
||||
* rc < 0 carries the failure/timeout error code, the same vocabulary
|
||||
* commit_submit's WAIT mode already uses (-ETIMEDOUT, -XCE_ERR_STANDBY,
|
||||
* -ENOMEM, etc.). `lsn` is 0 for an entry that never reached the point of
|
||||
* being assigned a real LSN (e.g. a local encode/append failure) — 0 is
|
||||
* the reserved "no real entry" sentinel already used throughout persist.h.
|
||||
*
|
||||
* Declared with a single plain rc — NOT commit.h's `commit_outcome_t` +
|
||||
* `int rc` pair — so this header never needs to depend on commit.h, which
|
||||
* already depends on this one (commit_engine_t embeds a `persist_t*`).
|
||||
* persist.c's implementation adapts commit_submit_async's real callback
|
||||
* shape to this one internally (see job_commit_resolve_adapter). */
|
||||
typedef void (*persist_job_commit_notify_fn)(void* ctx, uint64_t lsn, int rc);
|
||||
|
||||
/* Async sibling of persist_submit_job_payloads_and_commit (raft-apply-
|
||||
* decoupling task 3.2): WAL-appends each payload the same way (encode +
|
||||
* local append; out_lsns[i] set on a successful append) but never blocks
|
||||
* on the majority-commit barrier. Instead, `fn`/`ctx` is invoked EXACTLY
|
||||
* ONCE per payload in `payloads` — either synchronously, right here (a
|
||||
* local append failure, the once-per-cluster-lifetime LSN-0 edge, an
|
||||
* engine-not-wired context, or a commit_submit_async registration
|
||||
* failure), or later, asynchronously, off this call's stack, once that
|
||||
* payload's entry's commit outcome is known.
|
||||
*
|
||||
* On the FIRST local failure (encode/append), every REMAINING payload in
|
||||
* the batch also resolves immediately with that SAME failure rc — so the
|
||||
* caller always receives exactly `payloads->count` total `fn` invocations,
|
||||
* regardless of where (or whether) a failure occurs; the caller never
|
||||
* needs to separately account for "never going to be called" entries.
|
||||
*
|
||||
* Returns 0 once every payload is durably appended locally and every
|
||||
* payload has an outcome resolution path established (sync or async); the
|
||||
* first negative rc encountered otherwise. Every payload's `fn` invocation
|
||||
* has already happened (or is already guaranteed to happen later) by the
|
||||
* time this function returns, regardless of its return value — the return
|
||||
* value itself carries no information the caller still needs to act on.
|
||||
*
|
||||
* out_lsns[i] is valid for every i whose local append succeeded (same
|
||||
* contract as the synchronous sibling); it is left unset (0, if the caller
|
||||
* zero-initialized it) for a payload whose local append itself failed. */
|
||||
int persist_submit_job_payloads_async(persist_t* p, xlist_t* payloads,
|
||||
uint64_t* out_lsns,
|
||||
persist_job_commit_notify_fn fn,
|
||||
void* ctx);
|
||||
|
||||
/* raft-apply-decoupling task 3.2: producer-side hand-off for a command
|
||||
* whose commit_submit_async() continuation resolved COMMITTED. Per task
|
||||
* 3.2's own contract, a COMMITTED outcome must NOT apply state or reply to
|
||||
* the client directly from the commit continuation — both must happen
|
||||
* from the not-yet-built ordered apply pump (task 6.1/6.2), strictly in
|
||||
* commit order relative to every other entry (command or fact).
|
||||
*
|
||||
* persist_register_pending_apply() is the producer: it records that once
|
||||
* `lsn` is applied (via apply_committed_entry(), the same seam restart
|
||||
* replay already uses), `on_applied(ctx, lsn)` must be invoked immediately
|
||||
* afterward — task 6.2's own stated contract: "fire a command's registered
|
||||
* continuation immediately after its entry applies". Until task 6 exists,
|
||||
* registered entries accumulate here inertly: this is the SAME "durably
|
||||
* committed, not yet applied" posture already accepted for retained facts
|
||||
* during quorum loss (Req 6.1), not a leak — task 6.1 is expected to drain
|
||||
* and free each node as it walks committed LSNs.
|
||||
*
|
||||
* NOTE for whoever builds task 6.1: apply_committed_entry()'s WAL_JOB_SUBMIT
|
||||
* case (persist.c) already constructs the job (via restore_job()) AND fires
|
||||
* the HOOK_DETAIL_JOB_NEW AFTER_EVENT notification, per entry, independently
|
||||
* of any sibling array element — calling apply_committed_entry() for this
|
||||
* lsn is very likely ALL the "apply" work `on_applied`'s caller needs to do
|
||||
* before invoking `on_applied`; `on_applied` itself should very likely only
|
||||
* need to send the client's success reply, not reconstruct or re-notify
|
||||
* anything (see job.c's jobsubmit_apply_complete for the concrete case). */
|
||||
typedef void (*pending_apply_notify_fn)(void* ctx, uint64_t lsn);
|
||||
|
||||
void persist_register_pending_apply(persist_t* p, uint64_t lsn,
|
||||
pending_apply_notify_fn on_applied,
|
||||
void* ctx);
|
||||
|
||||
/* Non-destructive lookup: 1 if `lsn` has a registered, not-yet-consumed
|
||||
* pending-apply hand-off, 0 otherwise. Test/introspection helper only —
|
||||
* task 6.1's real consumer will need its own draining accessor (e.g. "take
|
||||
* and remove"), not built by this task. */
|
||||
int persist_has_pending_apply(persist_t* p, uint64_t lsn);
|
||||
|
||||
int persist_job_hook_fun(int type, int situation, int detail,
|
||||
void* cls, va_list ap);
|
||||
int persist_queue_hook_fun(int type, int situation, int detail,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue