feat(ha-repl-hardening): wire OPCode_walCatchupEntry standby handler (task 2.1)
Replace stub with ha_apply_wal_catchup_entry() calling apply_wal_entry(). CAS-advance applied_lsn on success; log ERROR and skip on failure. Standby stays in CTRL_STANDBY throughout. 3 new unit tests.
This commit is contained in:
parent
e3ca775f0d
commit
647b88ce57
5 changed files with 251 additions and 1 deletions
|
|
@ -803,6 +803,65 @@ void ha_fini(ha_state_t *ha)
|
|||
}
|
||||
}
|
||||
|
||||
/* ── Task 2.1: WAL catch-up entry application (standby side) ────────────────
|
||||
*
|
||||
* ha_apply_wal_catchup_entry: apply a single WAL catch-up entry received from
|
||||
* the primary to local persistent storage and advance applied_lsn on success.
|
||||
*
|
||||
* On success (rc == 0): atomically advances ha->applied_lsn to entry->lsn+1
|
||||
* so the next heartbeat reply's stateReport reflects the latest state.
|
||||
*
|
||||
* On error (rc < 0): logs ERROR and returns the error code. The dispatch
|
||||
* handler in cluster.c ignores the error and continues, satisfying Req 3.3
|
||||
* (corrupt/disk-error entries are skipped, not crashed).
|
||||
*
|
||||
* applied_lsn is in-memory only (not persisted to disk). On restart the
|
||||
* standby reports lsn=0 and re-bootstraps via the heartbeat decision loop
|
||||
* (design §Durability).
|
||||
*
|
||||
* Exported under XCE_TESTING so unit tests can call it directly without
|
||||
* constructing a full cluster_t or endpoint_t.
|
||||
*/
|
||||
int ha_apply_wal_catchup_entry(ha_state_t *ha, persist_t *p,
|
||||
wal_catchup_entry_t *entry)
|
||||
{
|
||||
if (!ha || !p || !entry)
|
||||
return -EINVAL;
|
||||
|
||||
/* Build a wal_entry_hdr_t from the decoded catch-up entry fields.
|
||||
* payload_len is 0 when entry->payload is NULL (no payload for this
|
||||
* entry type, or single-entry path before task 3.4 encodes real data). */
|
||||
wal_entry_hdr_t hdr;
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
hdr.lsn = entry->lsn;
|
||||
hdr.type = (uint32_t)entry->entry_type;
|
||||
hdr.payload_len = 0;
|
||||
hdr.crc = 0;
|
||||
hdr.prev_crc = 0;
|
||||
|
||||
int rc = apply_wal_entry(p, &hdr, NULL);
|
||||
if (rc != 0) {
|
||||
ERROR("ha_apply_wal_catchup_entry: apply_wal_entry failed for "
|
||||
"lsn=%" PRIu64 " type=%u rc=%d — skipping entry",
|
||||
entry->lsn, (unsigned)entry->entry_type, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Advance applied_lsn atomically and monotonically (lsn+1 mirrors
|
||||
* persist->current_lsn after a successful apply). */
|
||||
uint64_t new_lsn = entry->lsn + 1;
|
||||
uint64_t old_lsn = (uint64_t)atomic_load_explicit(&ha->applied_lsn,
|
||||
memory_order_acquire);
|
||||
while (new_lsn > old_lsn) {
|
||||
if (atomic_compare_exchange_weak_explicit(
|
||||
&ha->applied_lsn, &old_lsn, new_lsn,
|
||||
memory_order_acq_rel, memory_order_acquire))
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Dispatch gate (req 1.3, 1.4) ──────────────────────────────────────────
|
||||
*
|
||||
* Called from myreq_handler() in cluster.c before the opcode switch.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue