feat(raft-apply-decoupling): add commit engine continuation registration (task 1.1)

commit_submit_async() lets a WAIT-mode caller register a continuation
against an appended LSN instead of blocking on pthread_cond_timedwait,
reusing the existing admission-cap discipline (wait_admit_locked/
wait_release_locked) so the bounded-capacity guarantee carries over
unchanged. Purely additive: no existing call site (repl_wait_majority_ack)
is touched yet -- that switch lands in task 3.2 once commit_resolve()
(task 1.2) exists to drive it.
This commit is contained in:
Rongsong Shen 2026-07-03 07:55:35 +00:00
parent 3ecb724984
commit 10d157f8c5
6 changed files with 457 additions and 1 deletions

View file

@ -23,6 +23,32 @@
typedef enum { COMMIT_MODE_WAIT, COMMIT_MODE_RETAIN } commit_mode_t;
/* raft-apply-decoupling task 1.1: event-driven resolve outcome, delivered to
* a registered continuation in place of a COMMIT_MODE_WAIT caller's blocking
* return value. COMMITTED mirrors commit_submit's rc==0; FAILED mirrors any
* of commit_submit's negative outcomes (timeout, demotion, truncation), with
* the specific cause carried in the continuation's `rc` argument. */
typedef enum { COMMIT_OUTCOME_COMMITTED, COMMIT_OUTCOME_FAILED } commit_outcome_t;
/* Invoked exactly once per registered lsn when its outcome becomes known.
* Delivered via commit_resolve (wired by a follow-up task onto
* commit_advance_locked's majority path and the existing timeout/demotion/
* truncation failure handling), dispatched off the calling thread via
* vtask_enq_classified -- never inline on the replication-ACK or iomgr
* path. */
typedef void (*commit_continuation_fn)(void* ctx, uint64_t lsn,
commit_outcome_t outcome, int rc);
/* raft-apply-decoupling task 1.1: one registered COMMIT_MODE_WAIT-equivalent
* continuation. lsn == 0 marks a free slot -- LSN 0 is the reserved
* empty/none sentinel (see persist.h reserve-lsn-0), so it is never a real
* registration and is safe as the "unused" marker. */
typedef struct commit_continuation {
uint64_t lsn;
commit_continuation_fn fn;
void* ctx;
} commit_continuation_t;
/*
* Commit engine state — in-memory only (commit state volatile, Raft-conformant).
* Per-standby view delegated to standby_conn_t.acked_lsn / in_catch_up via
@ -52,6 +78,18 @@ typedef struct commit_engine {
* non-decreasing, set under ce->lock via commit_set_raft_commit_index. */
uint64_t raft_commit_index;
uint64_t scratch[MAX_STANDBYS]; /* sorted matchIndex scratch array */
/* raft-apply-decoupling task 1.1: per-LSN continuation registry for
* commit_submit_async (event-driven resolve, replacing the
* COMMIT_MODE_WAIT blocking-park model). Sized to n_vcpus at init: the
* SAME admission-cap discipline (wait_admit_locked/wait_cap, the shared
* inflight_waits budget) bounds concurrent registrations to
* wait_cap(ce) <= n_vcpus, so n_vcpus slots always suffice regardless of
* the blocking/async call mix. Resolution (commit_resolve, invoked from
* commit_advance_locked and the timeout/demotion/truncation failure
* paths) is wired by a follow-up task; until then a registered slot is
* inert -- nothing reads it back. */
commit_continuation_t* continuations;
int n_continuations; /* capacity, == n_vcpus */
} commit_engine_t;
/* Lifecycle. init validates n_peers >= 0, n_vcpus >= 1, non-NULL persist_t;
@ -76,6 +114,19 @@ void commit_set_pool(commit_engine_t* ce, vtaskmgr_t* pool);
int commit_submit(commit_engine_t* ce, uint64_t lsn, commit_mode_t mode,
uint32_t timeout_ms);
/* raft-apply-decoupling task 1.1: register a continuation for lsn instead of
* parking the calling thread -- the event-driven equivalent of
* commit_submit(..., COMMIT_MODE_WAIT, ...). Returns 0 on successful
* registration; -EINVAL on bad args (ce/p NULL, lsn == 0, fn == NULL);
* -XCE_ERR_NOT_READY when the admission cap is exhausted (the same shared
* discipline commit_submit's WAIT mode uses -- see wait_admit_locked).
* Never blocks: the calling thread returns immediately, before lsn's
* outcome is known. Registering here does not yet cause `fn` to be
* invoked -- delivery is commit_resolve's job, added by a follow-up task
* that wires it onto the majority-commit and failure paths. */
int commit_submit_async(commit_engine_t* ce, uint64_t lsn,
commit_continuation_fn fn, void* ctx);
/* iomgr path: record standby ACK, evaluate majority, advance committed_lsn
* contiguously, wake satisfied waiters. Never blocks. */
void commit_on_ack(commit_engine_t* ce, int standby_idx, uint64_t acked_lsn);