feat(ha-term-safety): on_persist_entry callback body (task 2.1)
Replaces the iter-1 stub: persist_set_term(p, term) stamps the leader/library term
into hdr.term, then role-routes the write — primary via persist_wal_async, standby
via persist_wal_write_replicated(p, index, ...). Persist failure returns non-zero
(no false ACK; durable_lsn unchanged); bug-850 contiguity guard + persist-before-ACK
preserved beneath. In-flight raft_entry_blob_t {type,payload_len} envelope added to
wal_types.h (never enters the 40-byte header). 3 tests, reviewer APPROVED.
Deferred (recorded in tasks.md): task 3.1 must retire the hand-rolled primary append
so current_lsn tracks the library index; task 3.2 must not call on_persist_entry on
heartbeats. Per-group 0 failures (pre-existing test_commit_barrier_reply leak aside).
This commit is contained in:
parent
d540ebd183
commit
8f3d3ce8f4
5 changed files with 284 additions and 4 deletions
|
|
@ -1332,7 +1332,7 @@ static void raft_cb_trace(void *ctx, const char *event, const char *detail)
|
|||
* These placeholders exist ONLY to restore the controller build (tasks 1.1/1.2);
|
||||
* they perform NO persistence, NO replication, and NO truncation. See CONCERNS.
|
||||
*
|
||||
* on_persist_entry → task 2.1 (durable WAL append/overwrite)
|
||||
* on_persist_entry → task 2.1 (durable WAL append/overwrite) — IMPLEMENTED
|
||||
* on_get_entry_term → task 2.2 (WAL entry-term lookup; persist_entry_term, 1.3)
|
||||
* on_send_append_entries→ task 2.3 (AppendEntries transmit)
|
||||
* on_truncate_from → task 2.4 (conflict truncation)
|
||||
|
|
@ -1343,11 +1343,75 @@ static void raft_cb_trace(void *ctx, const char *event, const char *detail)
|
|||
* grants on lastLogTerm >= 0 — i.e. it does NOT yet tighten the existing
|
||||
* config_tag/last_log_lsn freshness gate already enforced in cluster.c. The real
|
||||
* accessor (task 2.2/1.3) makes the gate effective. */
|
||||
/* on_persist_entry (ha-term-safety task 2.1, R1.1–R1.4): durably append/overwrite
|
||||
* the log entry at 1-based `index` (== WAL LSN) stamping the LEADER-supplied
|
||||
* `term` into hdr.term, then return the persist rc.
|
||||
*
|
||||
* Persist-before-act: this is called BEFORE the library reports the append/accept
|
||||
* (raft_log_append / raft_on_append_entries_request). On a non-zero (failure) rc
|
||||
* the library does NOT advance its log and a standby does NOT ACK (the existing
|
||||
* persist-before-ACK + bug-850 contiguity guard sit beneath the WAL write and are
|
||||
* preserved — we do not bypass them). We never false-ACK on failure.
|
||||
*
|
||||
* `entry` is the adapter envelope (raft_entry_blob_t prefix + payload) packed by
|
||||
* the append/replication driver (tasks 3.1/3.2); the library holds no entry bytes
|
||||
* and treats it as opaque. We decode (type, payload, payload_len) from it for the
|
||||
* existing persist write path.
|
||||
*
|
||||
* Path selection by role:
|
||||
* - PRIMARY own-append: persist_wal_async — assigns the contiguous next LSN
|
||||
* (== index here, since the library's index == log_len+1 == current_lsn) and
|
||||
* defers fsync to the group-commit flusher (the steady-state append path).
|
||||
* - STANDBY replicated apply: persist_wal_write_replicated — writes at the GIVEN
|
||||
* lsn=index with the contiguity guard + inline persist-before-ACK fsync.
|
||||
*
|
||||
* NOTE (deferred to 2.3/3.2): the leader's per-entry term is carried on the repl
|
||||
* envelope by task 2.3/3.2; until then the standby path stamps the term we set
|
||||
* here via persist_set_term — which IS the supplied `term` argument — so a test
|
||||
* that drives this callback already records the leader term. The envelope wiring
|
||||
* makes the live replication path source the same value. */
|
||||
static int ha_cb_persist_entry_stub(void *ctx, uint64_t index, uint64_t term,
|
||||
const void *entry, size_t len)
|
||||
{
|
||||
(void)ctx; (void)index; (void)term; (void)entry; (void)len;
|
||||
return 0; /* TODO task 2.1 */
|
||||
ha_state_t *ha = (ha_state_t *)ctx;
|
||||
if (!ha || !ha->raft_c)
|
||||
return -EINVAL; /* test mode without a cluster: cannot persist */
|
||||
|
||||
persist_t *p = &ha->raft_c->persist;
|
||||
|
||||
/* Decode the adapter entry envelope: a fixed raft_entry_blob_t prefix
|
||||
* carrying the WAL type + payload_len, followed by the payload bytes. */
|
||||
uint32_t type = WAL_NOOP;
|
||||
const void *payload = NULL;
|
||||
uint32_t plen = 0;
|
||||
if (entry && len >= sizeof(raft_entry_blob_t)) {
|
||||
const raft_entry_blob_t *blob = (const raft_entry_blob_t *)entry;
|
||||
type = blob->type;
|
||||
plen = blob->payload_len;
|
||||
/* Reject a malformed envelope whose declared payload exceeds the buffer
|
||||
* — fail loud rather than read past the blob. */
|
||||
if ((size_t)plen + sizeof(*blob) > len)
|
||||
return -EINVAL;
|
||||
if (plen > 0)
|
||||
payload = (const uint8_t *)entry + sizeof(*blob);
|
||||
} else if (entry && len > 0) {
|
||||
/* Defensive: an un-enveloped blob is treated as malformed. */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Stamp the live election term so the WAL header records the LEADER's term
|
||||
* for this entry (R1.1/R1.2/R1.4). */
|
||||
persist_set_term(p, term);
|
||||
|
||||
/* Persist at LSN=index through the existing write path (persist-before-ACK
|
||||
* and the bug-850 contiguity guard are preserved beneath these). */
|
||||
if (ha_is_primary(ha)) {
|
||||
/* Own-append: persist_wal_async assigns the contiguous next LSN, which
|
||||
* equals `index` for the leader (library index == current_lsn). */
|
||||
return persist_wal_async(p, type, payload, plen);
|
||||
}
|
||||
|
||||
return persist_wal_write_replicated(p, index, type, payload, plen);
|
||||
}
|
||||
|
||||
static uint64_t ha_cb_get_entry_term_stub(void *ctx, uint64_t index)
|
||||
|
|
@ -1560,6 +1624,15 @@ uint64_t ha_trigger_manual_failover_for_test(ha_state_t *ha)
|
|||
ha_do_promote(ha);
|
||||
return atomic_load_explicit(&ha->term, memory_order_acquire);
|
||||
}
|
||||
|
||||
/* ha-term-safety task 2.1: drive the WIRED on_persist_entry callback so a unit
|
||||
* test exercises the real adapter body (term stamp + WAL write) without
|
||||
* raft_log_append (task 3.1). Exposed only under XCE_TESTING. */
|
||||
int ha_cb_persist_entry_for_test(ha_state_t *ha, uint64_t index, uint64_t term,
|
||||
const void *entry, size_t len)
|
||||
{
|
||||
return s_real_cbs.on_persist_entry(ha, index, term, entry, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ha_check_heartbeat_nocluster() / ha_watchdog_thread() / ha_start_watchdog() /
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue