feat(raft-apply-decoupling): resolve pending continuations on demotion/truncation (task 1.4)
commit_resolve_pending_above(ce, lsn_floor, rc) fails every registered continuation for lsn > lsn_floor, reusing commit_resolve()'s existing exactly-once/idempotent guarantee (no parallel bookkeeping path). Truncation: a new on_truncate/truncate_cb_ctx callback on persist_t (same inversion-of-control shape as the existing on_durable_advance hook) fires from persist_truncate_wal_above after releasing p->lock, only when entries were actually removed. This one wrapper covers all three real truncation call sites: raft_cb_become_leader's post-election truncate, the library's on_truncate_from conflict-reconcile, and ha_demotion_convergence_fn's post-demotion truncate. Demotion: raft_cb_become_follower calls commit_resolve_pending_above(&c->commit, 0, -XCE_ERR_STANDBY) -- lsn_floor=0 fails everything, since a demoted node can never authoritatively confirm anything still pending (CP posture). Without this, a demoted or truncated node would leave any pending async command permanently unresolved -- the requester would simply never get a reply, worse than today's bounded-timeout behavior. Also closes bug-1407 (pre-existing test_config_repl flake, unrelated to this feature) -- confirmed fixed as a side effect of prior tasks in this series, 70/70 passing across 3 independent runs.
This commit is contained in:
parent
530a02bda3
commit
9b4b805a27
9 changed files with 748 additions and 301 deletions
|
|
@ -340,6 +340,22 @@ struct persist_s {
|
|||
void (*on_durable_advance)(void* ctx);
|
||||
void* durable_cb_ctx;
|
||||
|
||||
/* Truncation wake hook (raft-apply-decoupling task 1.4). Persistence owns
|
||||
* this slot; the commit engine REGISTERS a callback (ctx = the
|
||||
* commit_engine_t) via persist_set_truncate_cb, mirroring
|
||||
* on_durable_advance's registration pattern and dependency direction
|
||||
* (persist only stores+calls an opaque function pointer; it never
|
||||
* depends on commit.c). persist_truncate_wal_above invokes it AFTER it
|
||||
* actually removes entries above the new tail and AFTER releasing
|
||||
* p->lock, so any commit continuation registered for an LSN above the
|
||||
* new tail (which can never commit -- its entry no longer exists) is
|
||||
* resolved FAILED promptly instead of waiting out the commit engine's
|
||||
* timeout sweep. NOT invoked on a no-op truncate (nothing above
|
||||
* cutoff_lsn -- current_lsn unchanged). NULL until registered (a
|
||||
* truncate is then a silent no-op wake). */
|
||||
void (*on_truncate)(void* ctx, uint64_t cutoff_lsn);
|
||||
void* truncate_cb_ctx;
|
||||
|
||||
/* Checkpoint (stubs for now — wired in Task 10) */
|
||||
atomic_int checkpoint_requested;
|
||||
pthread_cond_t checkpoint_done;
|
||||
|
|
@ -590,6 +606,16 @@ void persist_clear_recovery_unrecoverable(persist_t* p);
|
|||
* unregister. Safe to call before persist_start (no flusher running yet). */
|
||||
void persist_set_durable_cb(persist_t* p, void (*fn)(void* ctx), void* ctx);
|
||||
|
||||
/* Register a truncation wake callback (raft-apply-decoupling task 1.4).
|
||||
* persist_truncate_wal_above calls fn(ctx, cutoff_lsn) after it actually
|
||||
* truncates entries above cutoff_lsn (the new log tail) and after releasing
|
||||
* p->lock -- fn MUST NOT take p->lock (same rule as persist_set_durable_cb;
|
||||
* the commit engine's callback takes only ce->lock, keeping the lock order
|
||||
* p->lock → ce->lock cycle-free). Not called for a no-op truncate (nothing
|
||||
* above cutoff_lsn). Pass fn = NULL to unregister. */
|
||||
void persist_set_truncate_cb(persist_t* p, void (*fn)(void* ctx, uint64_t cutoff_lsn),
|
||||
void* ctx);
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
/* ============================================================
|
||||
* Durability-verification test seam (ha-commit-durability Req 3.2, 3.3)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue