feat(config-upgrade-snapshot): gate primary serving and promotion on the activation fence
Wires task 6.1's upgrade_materialization_verify/upgrade_materialization_fenced into ha_dispatch_gate's CTRL_PRIMARY branch (sequential block alongside, not merged into, the existing config_serve_fenced check -- same replication/read-only opcode allowlist, refuses state-changing ops with -XCE_ERR_NOT_READY while fenced) and the promotion gate (raft_cb_become_leader refuses to become CTRL_PRIMARY while fenced, introduces no new WAL/log-completeness eligibility mechanism, never self-terminates). Adds a 4-function self-triggering retry trampoline (upgrade_materialization_retry_fn/_vtask_entry/_delaymgr_trampoline/ schedule_upgrade_materialization_retry) mirroring config_activate_retry_fn's exact shape, hooked into ha_cb_commit_advanced_stub alongside the existing config-byte retry. Fixes a real bug caught on review: the fence had no production path that could ever engage it from its zero-initialized default -- every setter call site was gated on the fence already being set (closed loop), making the new gate checks permanently dead code. Fixed with two unconditional upgrade_materialization_verify() call sites: persist.c's apply_committed_entry WAL_CONFIG_MARKER case (right after persist_checkpoint_marker_triggered returns, regardless of its rc) and myapp.c's boot sequence (right after config_activate_committed, for Req 5.2 crash/restart resume). New test drives the fence through the real production trigger (a forced -EBUSY checkpoint miss on a real marker commit+apply), not a direct verify() call, proving the end-to-end path actually works. Task 6.2 of 19. Requirements: 3.1, 3.2, 3.3, 3.4, 4.3, 5.5
This commit is contained in:
parent
7b507156a0
commit
89b85e1680
6 changed files with 805 additions and 7 deletions
|
|
@ -42,6 +42,19 @@ extern int restore_queue_state(cluster_t* c,
|
|||
* from load_snapshot_entities' host-frame loop below (task 4). */
|
||||
extern int restore_host_state(cluster_t* c, const char* name,
|
||||
uint32_t admin_status);
|
||||
/* upgrade_materialization_verify defined in ha.c (config-upgrade-snapshot
|
||||
* task 6.1/6.2, Req 1.8/3.2/3.4/5.2/5.5) -- the Activation Fence's own
|
||||
* verify function. Called unconditionally from apply_committed_entry's
|
||||
* WAL_CONFIG_MARKER case below (task 6.2): checkpoint completion is the
|
||||
* design-intended trigger that re-evaluates this fence (design.md's
|
||||
* Activation Fence component, "naturally re-evaluates against the newer
|
||||
* marker once that checkpoint's write_snapshot call runs"), since it is
|
||||
* the ONLY event that can newly set OR clear it from a running process --
|
||||
* every other call path (upgrade_materialization_retry_fn and its
|
||||
* callers) is itself gated on the fence already being set, so without
|
||||
* this call site the fence could never transition from its zero-
|
||||
* initialized default. */
|
||||
extern int upgrade_materialization_verify(cluster_t* c);
|
||||
|
||||
/* raft-apply-decoupling task 3.2: pending command-apply hand-off list node
|
||||
* (producer side; the not-yet-built ordered apply pump, task 6.1/6.2, is
|
||||
|
|
@ -3780,9 +3793,7 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
|
|||
TRACE("apply_committed_entry: WAL_CONFIG_MARKER lsn=%" PRIu64
|
||||
" checkpoint already in progress -- skipping, "
|
||||
"self-resolving", lsn);
|
||||
return 0;
|
||||
}
|
||||
if (rc != 0) {
|
||||
} else if (rc != 0) {
|
||||
/* A genuine checkpoint failure (I/O error, etc.) -- propagate
|
||||
* so the caller's existing halt-and-retry-from-same-position
|
||||
* handling engages (persist_apply_pump_run / replay_wal),
|
||||
|
|
@ -3790,12 +3801,47 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
|
|||
* dispatch. last_applied_lsn is NOT advanced past this marker,
|
||||
* so the next kick/retry re-attempts the checkpoint fresh
|
||||
* (Req 5.2 crash-safety: resume materialization from the
|
||||
* marker). */
|
||||
* marker). upgrade_materialization_verify below still runs
|
||||
* even on this path (it reads its own signals -- the
|
||||
* committed marker + c->persist.cfgref_* -- independently of
|
||||
* this rc; a failed checkpoint attempt here does not by
|
||||
* itself change whether a PRIOR checkpoint already
|
||||
* materialized under the current marker). */
|
||||
ERROR("apply_committed_entry: WAL_CONFIG_MARKER lsn=%" PRIu64
|
||||
" forced checkpoint failed (rc=%d)", lsn, rc);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
|
||||
/* config-upgrade-snapshot task 6.2 (Req 1.8, 3.2, 3.4, 5.5):
|
||||
* unconditional Activation Fence re-evaluation right after every
|
||||
* checkpoint attempt tied to this marker, regardless of whether
|
||||
* persist_checkpoint_marker_triggered just above returned 0,
|
||||
* -EBUSY, or a genuine error -- upgrade_materialization_verify
|
||||
* itself already handles "materialization not done yet" correctly
|
||||
* (it fences and returns -EAGAIN rather than erroring), so gating
|
||||
* this call on rc would only reintroduce the closed loop this call
|
||||
* site exists to break (every OTHER caller of
|
||||
* upgrade_materialization_verify is itself gated on the fence
|
||||
* already being set -- see upgrade_materialization_retry_fn and
|
||||
* its callers in ha.c -- so without one unconditional trigger the
|
||||
* fence could never transition from its zero-initialized default).
|
||||
* Checkpoint completion is the design-intended trigger for this
|
||||
* re-evaluation (design.md's Activation Fence component). Safe to
|
||||
* call here: neither upgrade_materialization_verify nor anything
|
||||
* it calls (config_committed_marker, conf_read_current_filename,
|
||||
* conf_read_raw) acquires apply_pump_lock or p->apply_pump_lock --
|
||||
* they only do atomic reads of c->persist.committed_lsn /
|
||||
* c->persist.cfgref_* and plain file I/O (opendir/pread-style WAL
|
||||
* scan, conf_read_raw) -- so no reentrant-lock hazard even though
|
||||
* this call runs while persist_apply_pump_run holds
|
||||
* apply_pump_lock across this entire dispatch (same hazard class
|
||||
* as task 5's own persist_checkpoint_marker_triggered, avoided the
|
||||
* same way: never touch apply_pump_lock). Return value
|
||||
* intentionally ignored here -- ha_dispatch_gate and the
|
||||
* promotion gate (ha.c) are what actually consume the fence;
|
||||
* this call's only job is to keep it current. */
|
||||
(void)upgrade_materialization_verify(c);
|
||||
|
||||
return (rc == -EBUSY) ? 0 : rc;
|
||||
}
|
||||
|
||||
default:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue