forked from lei.chen/myxce
fix(ha-wal-replication): apply_wal_entry must not own the WAL CRC chain (bug-778)
On the standby replicated-apply path, ha_apply_single_entry wrote the entry via persist_wal_write_replicated (which correctly set the self-computed CRC chain link p->last_crc), then called apply_wal_entry with a SYNTHETIC header whose crc=0. apply_wal_entry's `p->last_crc = hdr->crc` clobbered the chain link with 0, so every subsequent replicated write stamped prev_crc=0 on disk. The on-disk CRC chain broke at the second replicated entry — invisible while live (standbys serve in-memory state) but fatal on COLD RESTART: replay hit the break, returned RECOVER_TRUNCATED, recovered only the config prefix, and lost all committed jobs (joblist=0). The sticky truncated verdict then drove a promotion-fence availability wedge (bug-777) when every survivor was truncated. Fix — single ownership of the WAL header + CRC chain: - apply_wal_entry is now LOGICAL entity-restore only: signature (p, type, lsn, payload, payload_len); no header. It advances the current_lsn high-water (monotonic) but NEVER touches p->last_crc. - The physical layer is the sole owner of the CRC chain: persist_wal_write_- replicated on the live path, replay_wal/persist_recover on recovery. - ha_apply_single_entry drops the fabricated header and passes entry fields directly. The persist_recover replay caller is updated to the new signature. Verified: new RED->GREEN test test_replicated_apply_chain_survives_cold_restart (drives the real ha_apply_wal_catchup_entry path, cold-replays -> RECOVER_CLEAN, all N recovered); controller suite 0 failures; 3-node rolling-SIGKILL soak on a real ext2/3 fs now passes 5/5 (was wedge+loss at round 3); full cold restart recovers all 6 committed jobs (was 0) with CLEAN verdicts and a normal election. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
276e26c37b
commit
64ab210cbf
7 changed files with 136 additions and 45 deletions
|
|
@ -532,11 +532,18 @@ int persist_queue_hook_fun(int type, int situation, int detail,
|
|||
int persist_worker_hook_fun(int type, int situation, int detail,
|
||||
void* cls, va_list ap);
|
||||
|
||||
/* Apply a single WAL entry to in-memory state (standby replay path).
|
||||
/* Apply a single WAL entry to in-memory state (LOGICAL entity restore only).
|
||||
* No hooks fire — hook system is empty on standbys until promotion.
|
||||
*
|
||||
* This restores entity state from (type, lsn, payload) ONLY. It does NOT touch
|
||||
* the on-disk WAL header, the CRC chain (p->last_crc), or current_lsn — those
|
||||
* are owned exclusively by the physical layer: persist_wal_write_replicated on
|
||||
* the live replicated path, and replay_wal/persist_recover on recovery. (bug-778)
|
||||
*
|
||||
* Returns 0 on success (including skip for checkpoint/unknown types),
|
||||
* negative errno on error (oversized payload, NULL arguments). */
|
||||
int apply_wal_entry(persist_t* p, const wal_entry_hdr_t* hdr, const void* payload);
|
||||
* negative errno on error (oversized payload, NULL persist). */
|
||||
int apply_wal_entry(persist_t* p, uint32_t type, uint64_t lsn,
|
||||
const void* payload, uint32_t payload_len);
|
||||
|
||||
/* Load a snapshot file from path, set p->checkpoint_lsn and p->current_lsn.
|
||||
* lsn hint is informational — the actual LSN is read from the file header.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue