forked from lei.chen/myxce
feat(ha-commit-durability): loud bounded FATAL on unrecoverable recovery (task 3.3)
This commit is contained in:
parent
6483ee8afd
commit
e3fc42c11e
4 changed files with 324 additions and 9 deletions
|
|
@ -35,6 +35,47 @@ typedef enum {
|
|||
* (consumed by the promotion gate, task 4.3). */
|
||||
} recover_verdict_t;
|
||||
|
||||
/* ============================================================
|
||||
* Boot-policy recovery decision (ha-commit-durability task 3.3, Req 2.4)
|
||||
*
|
||||
* The outcome of evaluating a member's boot/promotion-eligibility state
|
||||
* against the Boot Policy Decision Table (design.md "Boot Policy Decision
|
||||
* Table (task 3.2)"). This task (3.3) owns the FATAL terminal; the fence +
|
||||
* catch-up + release path is task 4.3.
|
||||
*
|
||||
* ELIGIBLE — may promote (no fence). Rows 1, 6.
|
||||
* FENCE — ineligible to promote; catch up from a healthy peer, regain
|
||||
* eligibility on success (Req 2.6). Rows 2, 3, 5. A reachable
|
||||
* healthy peer NEVER yields FATAL.
|
||||
* FATAL — fail loud: cannot recover locally AND cannot catch up within
|
||||
* the bounded window. Terminate with the truncation/boundary
|
||||
* logged rather than serve divergent state (Req 2.4). Rows 4, 4b.
|
||||
*
|
||||
* AUTHORITATIVE INVARIANT (design.md): the fence is never a silent indefinite
|
||||
* wait — a truncated/behind member resolves to exactly one of FENCE→ELIGIBLE
|
||||
* or a BOUNDED FATAL. There is no "fenced forever, silently divergent" state.
|
||||
* ============================================================ */
|
||||
typedef enum {
|
||||
RECOVER_DECISION_ELIGIBLE = 0,
|
||||
RECOVER_DECISION_FENCE = 1,
|
||||
RECOVER_DECISION_FATAL = 2,
|
||||
} recover_decision_t;
|
||||
|
||||
/* Policy inputs evaluated at boot / promotion-eligibility (design.md table).
|
||||
* Modelled as a plain value so the decision is a pure, unit-testable function
|
||||
* of its inputs — the live catch-up machinery (replication / task 4.3) supplies
|
||||
* these; this task does not build it. */
|
||||
typedef struct {
|
||||
recover_verdict_t verdict; /* RECOVER_CLEAN / RECOVER_TRUNCATED (task 3.1) */
|
||||
bool durable_below_hwm; /* local durable position < cluster committed HWM */
|
||||
bool healthy_peer_reachable; /* a healthy peer is reachable for catch-up */
|
||||
bool catch_up_succeeded; /* catch-up reached a committed-consistent state
|
||||
* within the bounded window (only meaningful
|
||||
* 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 */
|
||||
} recover_policy_inputs_t;
|
||||
|
||||
/* ============================================================
|
||||
* Persistence configuration (Part 2.6)
|
||||
* ============================================================ */
|
||||
|
|
@ -276,6 +317,54 @@ uint64_t persist_durable_lsn(persist_t* p);
|
|||
recover_verdict_t persist_recovery_verdict(persist_t* p);
|
||||
bool persist_recovery_truncated(persist_t* p);
|
||||
|
||||
/* Bounded catch-up window (ha-commit-durability task 3.3, Req 2.4).
|
||||
* The FINITE deadline a recovery-truncated / behind member is given to reach a
|
||||
* committed-consistent state from a healthy peer before the boot policy FATALs.
|
||||
* The fence must NEVER be an indefinite wait (design.md authoritative
|
||||
* invariant), so the window is always finite. Default 30s — generous enough for
|
||||
* a healthy-peer catch-up stream yet bounded; the verification mode overrides it
|
||||
* via the inputs/timeout the caller (task 4.3 boot path) supplies. */
|
||||
#define DEFAULT_RECOVERY_CATCHUP_WINDOW_MS 30000u
|
||||
|
||||
/* persist_recover_decision (ha-commit-durability task 3.3, Req 2.4).
|
||||
*
|
||||
* Pure decision over the Boot Policy Decision Table: maps the policy inputs to
|
||||
* RECOVER_DECISION_{ELIGIBLE,FENCE,FATAL}. No side effects, no termination — it
|
||||
* only classifies, so it is unit-testable in isolation. The FATAL terminal is
|
||||
* acted on by persist_recover_fatal_if_unrecoverable (which logs + terminates).
|
||||
*
|
||||
* Contract (design.md rows): a reachable healthy peer NEVER returns FATAL
|
||||
* (rows 3, 5); FATAL is returned ONLY when the member is truncated or behind
|
||||
* the HWM, has NO healthy peer, and bounded catch-up did not reach a
|
||||
* committed-consistent state (rows 4, 4b). */
|
||||
recover_decision_t persist_recover_decision(const recover_policy_inputs_t* in);
|
||||
|
||||
/* persist_recover_fatal_if_unrecoverable (ha-commit-durability task 3.3,
|
||||
* Req 2.4).
|
||||
*
|
||||
* Evaluate persist_recover_decision over `in`; if the outcome is FATAL, log at
|
||||
* ERROR LOUDLY — INCLUDING the truncation boundary (durable_floor_lsn) and the
|
||||
* cluster high-water mark — and then TERMINATE (fail loud rather than serve
|
||||
* divergent state). Returns the decision so a non-FATAL caller can fence/proceed
|
||||
* (FENCE/ELIGIBLE are acted on by task 4.3).
|
||||
*
|
||||
* Termination routes through persist_fatal_hook: in production the hook is NULL
|
||||
* and the function flushes logs and abort()s (NEVER returns on FATAL); a test
|
||||
* build may install a hook to capture the FATAL decision + boundary log without
|
||||
* the process dying (mirrors the ha_fatal_hook seam in ha_convergence.c). */
|
||||
recover_decision_t
|
||||
persist_recover_fatal_if_unrecoverable(const recover_policy_inputs_t* in);
|
||||
|
||||
/* persist_fatal_hook: test-only seam for the FATAL (abort) contract of
|
||||
* persist_recover_fatal_if_unrecoverable (ha-commit-durability task 3.3,
|
||||
* Req 2.4). Mirrors ha_fatal_hook (ha_convergence.c).
|
||||
*
|
||||
* When non-NULL, the FATAL path invokes this INSTEAD OF abort(), so a test
|
||||
* binary can assert the FATAL decision was taken and the truncation boundary
|
||||
* was logged without the process dying. Production leaves it NULL and actually
|
||||
* terminates. Signature: void hook(const char *msg). */
|
||||
extern void (*persist_fatal_hook)(const char* msg);
|
||||
|
||||
/* Register a durable-advance wake callback (ha-commit-durability task 2.2).
|
||||
* The group-commit flusher calls fn(ctx) after it advances durable_lsn and
|
||||
* after releasing p->lock, so a consumer (the commit engine) can re-evaluate
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue