feat(raft-apply-decoupling): route DIVERGENT verdict through existing fence (task 5.2)
Add bool apply_divergent to the existing recover_policy_inputs_t;
persist_recover_decision() unconditionally short-circuits to the
existing RECOVER_DECISION_FENCE_UNRECOVERABLE when true, reusing the
same sticky recovery_unrecoverable flag and
persist_recover_loud_fence_if_unrecoverable() -- no new fence
mechanism, no ha.c changes needed (the existing promotion gate
already reads the flag independently), no new self-termination path.
Fixes a real bug found during review: the fence's operator-facing
ERROR() log unconditionally asserted a "no healthy peer reachable...
durable_floor_lsn < hwm" clause that was only truthful for the
pre-existing structural-fence trigger. For the new apply_divergent
trigger this produced literally false claims in test output
("100 < 100", "2 < 0"). Fixed by branching the log call itself on
apply_divergent so it states only the verified reason.
This commit is contained in:
parent
c005d40741
commit
099ee1a216
4 changed files with 417 additions and 24 deletions
|
|
@ -85,6 +85,21 @@ typedef struct {
|
|||
* when a catch-up was attempted) */
|
||||
uint64_t durable_floor_lsn; /* highest intact replayed LSN (truncation boundary) */
|
||||
uint64_t cluster_hwm_lsn; /* cluster committed high-water mark, for logging */
|
||||
|
||||
/* raft-apply-decoupling task 5.2 (Req 8.3/8.4): the post-replay
|
||||
* apply_verdict_t computed a semantic mismatch (APPLY_DIVERGENT) — the
|
||||
* materialized state does not match the committed log tip even though
|
||||
* the WAL bytes replayed cleanly. This is a NEW INPUT to the existing
|
||||
* Boot Policy Decision Table, not a new fence mechanism: unlike a
|
||||
* structurally truncated/behind log (which a healthy peer's missing
|
||||
* entries can repair), an apply-time defect is not something streaming
|
||||
* more entries from a peer proves fixed, so persist_recover_decision()
|
||||
* treats this field as an UNCONDITIONAL fence trigger — set it true and
|
||||
* every other field in this struct becomes irrelevant to the outcome
|
||||
* (see persist_recover_decision's implementation). Defaults to false
|
||||
* (an ordinary structural-only evaluation) when left unset by a
|
||||
* designated initializer, matching every pre-existing call site. */
|
||||
bool apply_divergent;
|
||||
} recover_policy_inputs_t;
|
||||
|
||||
/* ============================================================
|
||||
|
|
@ -108,9 +123,11 @@ typedef enum {
|
|||
* expectation (Req 8.2). */
|
||||
APPLY_DIVERGENT = 1, /* materialized state does NOT match the log-derived
|
||||
* expectation (Req 8.3) — a hidden apply defect.
|
||||
* Routing this into the existing non-promotable
|
||||
* fence mechanism is raft-apply-decoupling task 5.2;
|
||||
* this type only classifies, it does not fence. */
|
||||
* Routed into the existing non-promotable loud-fence
|
||||
* mechanism via recover_policy_inputs_t.apply_divergent
|
||||
* (raft-apply-decoupling task 5.2, Req 8.4) — this
|
||||
* type itself still only classifies; persist_recover()
|
||||
* is what acts on it. */
|
||||
} apply_verdict_t;
|
||||
|
||||
/* Lightweight post-replay expectation, derived purely from scanning the
|
||||
|
|
@ -340,8 +357,9 @@ struct persist_s {
|
|||
* defaults to APPLY_CLEAN before recovery runs, mirroring
|
||||
* recovery_verdict's default-clean convention. Written single-threaded
|
||||
* during recovery; read by persist_apply_verdict/persist_last_applied_lsn
|
||||
* thereafter (task 5.2 will additionally consume apply_verdict to route a
|
||||
* DIVERGENT result into the existing loud-fence mechanism). */
|
||||
* thereafter. A DIVERGENT result is additionally routed by persist_recover()
|
||||
* into the existing loud-fence mechanism (task 5.2, Req 8.4) via
|
||||
* recover_policy_inputs_t.apply_divergent. */
|
||||
_Atomic uint64_t last_applied_lsn;
|
||||
apply_verdict_t apply_verdict;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue