feat(raft-log-replication): header contract — log model, callbacks, entry points (tasks 1.1-1.3)
Extend src/lib/raft/raft.h for the TLA+ log half (raft.tla logVars/leaderVars): - raft_state_t: log_len, next_index[], match_index[] (no entry bytes stored — terms via on_get_entry_term, bytes in the adapter); raft_config_t.initial_log_len. - raft_callbacks_t: 4 REQUIRED log callbacks on_persist_entry, on_get_entry_term, on_send_append_entries, on_truncate_from. - Entry points + raft_append_result_t: raft_log_append (ClientRequest), raft_send_append_entries (AppendEntries emit), raft_on_append_entries_request (Handle + UpdateTerm-first), raft_on_append_entries_reply. Concretized on_send_append_entries's varargs "entries handle" placeholder to an explicit n_entries (0/1); design.md updated to match. Header compiles clean with and without XCE_TESTING (-Wall -Wextra -fsyntax-only).
This commit is contained in:
parent
20e59d0b42
commit
6b5b201a64
3 changed files with 111 additions and 6 deletions
|
|
@ -338,10 +338,13 @@ int on_persist_entry(void *ctx, uint64_t index, uint64_t term,
|
|||
uint64_t on_get_entry_term(void *ctx, uint64_t index);
|
||||
|
||||
/* REQUIRED: transmit one AppendEntries to peer_idx with prevLog{Index,Term},
|
||||
* leaderCommit, and an entries handle. */
|
||||
* leaderCommit, and n_entries (0 = heartbeat, 1 = one entry). The library holds
|
||||
* no entry bytes, so when n_entries==1 the adapter sources the single entry at
|
||||
* prev_log_index+1 from its own log. [Concretizes the original varargs "entries
|
||||
* handle" placeholder; impl 2026-06-21.] */
|
||||
int on_send_append_entries(void *ctx, int peer_idx,
|
||||
uint64_t prev_log_index, uint64_t prev_log_term,
|
||||
uint64_t leader_commit /*, entries handle */ ...);
|
||||
uint64_t leader_commit, int n_entries);
|
||||
|
||||
/* REQUIRED: delete local entries at index..end. */
|
||||
int on_truncate_from(void *ctx, uint64_t index);
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
## 1. Header contract: log model, callbacks, entry points
|
||||
|
||||
- [ ] 1.1 Extend `raft.h` with the log model state and config restore field
|
||||
- [x] 1.1 Extend `raft.h` with the log model state and config restore field
|
||||
- Add `uint64_t log_len;`, `uint64_t next_index[RAFT_MAX_PEERS];`, `uint64_t match_index[RAFT_MAX_PEERS];` to `raft_state_t`
|
||||
- Add `uint64_t initial_log_len;` to `raft_config_t` (restored WAL length at boot, default 0; library never reads storage)
|
||||
- Observable completion: `raft.h` compiles; `sizeof(raft_state_t)` grows by the new fixed fields; no entry-byte storage added
|
||||
- _Requirements: 1.1, 1.2, 3.1_
|
||||
- _Boundary: raft.h_
|
||||
|
||||
- [ ] 1.2 Add the four new REQUIRED callbacks to `raft_callbacks_t` (verbatim contract)
|
||||
- [x] 1.2 Add the four new REQUIRED callbacks to `raft_callbacks_t` (verbatim contract)
|
||||
- Declare `on_persist_entry`, `on_get_entry_term`, `on_send_append_entries`, `on_truncate_from` with the exact signatures from design.md / brief
|
||||
- Document each as REQUIRED and the `on_get_entry_term` → `0`-means-absent convention
|
||||
- Observable completion: the four function-pointer members exist in `raft_callbacks_t` with the verbatim signatures; header doc comments match the Authoritative shared contract
|
||||
|
|
@ -21,7 +21,8 @@
|
|||
- _Boundary: raft.h_
|
||||
- _Depends: 1.1_
|
||||
|
||||
- [ ] 1.3 Declare the three new entry points and `raft_append_result_t`
|
||||
- [x] 1.3 Declare the three new entry points and `raft_append_result_t`
|
||||
- NOTE (impl 2026-06-21): `on_send_append_entries`'s design "entries handle `...`" varargs placeholder was concretized to an explicit `int n_entries` (0=heartbeat, 1=one entry at `prev_log_index+1`; adapter sources the bytes since the library holds none). design.md §"Seam — raft_callbacks_t additions" updated to match. Downstream `raft-commit-safety` / `raft-sim-conformance` consume this concrete shape.
|
||||
- Declare `raft_append_result_t`; declare `raft_log_append`, `raft_send_append_entries`, `raft_on_append_entries_request`, `raft_on_append_entries_reply` with the design.md signatures
|
||||
- Observable completion: header declares all new entry points; a stub TU including `raft.h` compiles
|
||||
- _Requirements: 2.1, 3.3, 4.7, 5.1_
|
||||
|
|
|
|||
|
|
@ -44,6 +44,15 @@ typedef struct {
|
|||
int pre_vote_in_progress; /* 1 while a pre-vote round is active */
|
||||
int pre_votes_granted; /* grants collected this round (incl. self) */
|
||||
int pre_vote_replies[RAFT_MAX_PEERS]; /* per-peer replied flag; avoids double-count */
|
||||
/* ---- Log replication state (raft-log-replication, raft.tla logVars/leaderVars) ----
|
||||
* The library stores NO entry bytes and NO per-entry term array — entry terms
|
||||
* are obtained via on_get_entry_term; bytes live in the adapter/simulator. This
|
||||
* keeps raft_state_t fixed-size and the core pure (same discipline as not
|
||||
* storing (term, voted_for) bytes). Index is 1-based (raft.tla) and maps 1:1
|
||||
* to the WAL LSN. */
|
||||
uint64_t log_len; /* highest local log index (0 = empty) */
|
||||
uint64_t next_index[RAFT_MAX_PEERS]; /* leader: next index to send each peer */
|
||||
uint64_t match_index[RAFT_MAX_PEERS]; /* leader: highest index known replicated to each peer */
|
||||
/* set by raft_init — needed by the state machine to fire callbacks */
|
||||
const raft_callbacks_t *cbs;
|
||||
void *ctx;
|
||||
|
|
@ -69,7 +78,8 @@ typedef struct {
|
|||
* REQUIRED callbacks (must be non-NULL at raft_init):
|
||||
* on_send_vote_request, on_become_leader, on_become_follower,
|
||||
* on_persist, on_schedule_timeout, on_cancel_timeout,
|
||||
* on_start_election_after_delay
|
||||
* on_start_election_after_delay,
|
||||
* on_persist_entry, on_get_entry_term, on_send_append_entries, on_truncate_from
|
||||
*
|
||||
* OPTIONAL callbacks (may be NULL):
|
||||
* on_vote_eligible — if NULL, all Raft-eligible candidates are granted
|
||||
|
|
@ -109,6 +119,34 @@ typedef struct raft_callbacks_t {
|
|||
void (*on_cancel_timeout)(void *ctx);
|
||||
/* REQUIRED: Request delayed election start after RDHUP (priority-based jitter) */
|
||||
void (*on_start_election_after_delay)(void *ctx, uint32_t delay_ms);
|
||||
|
||||
/* ---- Log replication callbacks (raft-log-replication) ----
|
||||
* REQUIRED (all four non-NULL at raft_init). The library owns the protocol
|
||||
* decisions; these perform the I/O. The library stores no entry bytes, so it
|
||||
* reads entry terms only via on_get_entry_term and writes/truncates only via
|
||||
* these callbacks. */
|
||||
|
||||
/* REQUIRED: durably append/overwrite the log entry at 1-based `index` with
|
||||
* `term` (persist-before-act: called before an append/accept is reported). */
|
||||
int (*on_persist_entry)(void *ctx, uint64_t index, uint64_t term,
|
||||
const void *entry, size_t len);
|
||||
|
||||
/* REQUIRED: term of the local entry at 1-based `index` (0 if absent). */
|
||||
uint64_t (*on_get_entry_term)(void *ctx, uint64_t index);
|
||||
|
||||
/* REQUIRED: transmit one AppendEntries to peer_idx with prevLog{Index,Term}
|
||||
* and leader_commit. `n_entries` is 0 (heartbeat) or 1 (this spec, like
|
||||
* raft.tla); when 1 the adapter sources the single entry at prev_log_index+1
|
||||
* from its own log (the library holds no entry bytes). [Concretizes the
|
||||
* design's varargs "entries handle" placeholder into an explicit count.] */
|
||||
int (*on_send_append_entries)(void *ctx, int peer_idx,
|
||||
uint64_t prev_log_index,
|
||||
uint64_t prev_log_term,
|
||||
uint64_t leader_commit, int n_entries);
|
||||
|
||||
/* REQUIRED: delete local entries at `index`..end. */
|
||||
int (*on_truncate_from)(void *ctx, uint64_t index);
|
||||
|
||||
/* OPTIONAL: Trace callback for observability.
|
||||
* event: short string literal, e.g. "vote_granted", "became_leader"
|
||||
* detail: formatted human-readable context string
|
||||
|
|
@ -136,6 +174,7 @@ typedef struct {
|
|||
int my_priority; /* priority index for RDHUP jitter (0 = highest) */
|
||||
uint64_t initial_term; /* term restored from disk; 0 on first boot */
|
||||
int initial_voted_for; /* voted_for restored from disk; -1 if none */
|
||||
uint64_t initial_log_len; /* WAL length restored from disk; 0 on first boot (library never reads storage) */
|
||||
const raft_callbacks_t *cbs; /* all callbacks; required callbacks must be non-NULL */
|
||||
void *ctx; /* opaque caller context passed to every callback */
|
||||
} raft_config_t;
|
||||
|
|
@ -258,6 +297,68 @@ int raft_on_pre_vote_request(raft_state_t *s, int from_idx, uint64_t cand_term);
|
|||
*/
|
||||
int raft_on_pre_vote_reply(raft_state_t *s, int peer_idx, int granted);
|
||||
|
||||
/* ---------- Log replication (raft-log-replication) ----------
|
||||
*
|
||||
* The library owns the log protocol decisions (append, AppendEntries logOk +
|
||||
* conflict truncation, UpdateTerm-on-any-RPC) and drives I/O through the four
|
||||
* log callbacks. Index is 1-based (raft.tla) == WAL LSN. Commit-index advance
|
||||
* and (lastLogTerm,lastLogIndex) vote freshness are NOT here — they are
|
||||
* raft-commit-safety. */
|
||||
|
||||
/* Result of handling an AppendEntries request — the adapter replies with this. */
|
||||
typedef struct {
|
||||
uint64_t reply_term; /* current term to return to the leader */
|
||||
int success; /* 1 = accepted, 0 = rejected */
|
||||
uint64_t match_index; /* on success: prev_log_index + entries_len; else 0 */
|
||||
} raft_append_result_t;
|
||||
|
||||
/*
|
||||
* raft_log_append: leader appends one client entry (raft.tla ClientRequest, 246).
|
||||
* Stamps the entry with s->term, assigns index log_len+1, persists it via
|
||||
* on_persist_entry BEFORE returning success (persist-before-act). Does NOT
|
||||
* advance any commit index (owned by raft-commit-safety).
|
||||
* out_index receives the assigned 1-based index (== WAL LSN) on success.
|
||||
* Returns RAFT_OK; -EPERM if role != RAFT_LEADER; propagates a non-zero
|
||||
* on_persist_entry return on persist failure (no log mutation on failure).
|
||||
*/
|
||||
int raft_log_append(raft_state_t *s, const void *entry, size_t len,
|
||||
uint64_t *out_index);
|
||||
|
||||
/*
|
||||
* raft_send_append_entries: emit one AppendEntries to peer_idx (raft.tla
|
||||
* AppendEntries, 204). Computes prev_log_index = next_index[peer]-1 and
|
||||
* prev_log_term via on_get_entry_term, sends at most one entry (n_entries 0/1),
|
||||
* via on_send_append_entries. Leader-only (R3.5); leader_commit is opaque.
|
||||
* Returns RAFT_OK.
|
||||
*/
|
||||
int raft_send_append_entries(raft_state_t *s, int peer_idx,
|
||||
uint64_t leader_commit);
|
||||
|
||||
/*
|
||||
* raft_on_append_entries_request: handle an incoming AppendEntries / heartbeat
|
||||
* (raft.tla HandleAppendEntriesRequest 327 + UpdateTerm 406). Step order is
|
||||
* fixed: UpdateTerm first (G5, every RPC incl. heartbeat) → logOk (G2) →
|
||||
* reject / candidate-step-down / accept (already-have / conflict-truncate (G3) /
|
||||
* append). entries_len is 0 (heartbeat) or 1. Fills *out. leader_commit is
|
||||
* accepted but NOT acted on here (raft-commit-safety). Returns RAFT_OK.
|
||||
*/
|
||||
int raft_on_append_entries_request(raft_state_t *s, int leader_idx,
|
||||
uint64_t term, uint64_t prev_log_index,
|
||||
uint64_t prev_log_term, uint64_t leader_commit,
|
||||
const void *entry, size_t entry_len,
|
||||
uint64_t entry_term, int entries_len,
|
||||
raft_append_result_t *out);
|
||||
|
||||
/*
|
||||
* raft_on_append_entries_reply: process a follower's AppendEntries response
|
||||
* (raft.tla HandleAppendEntriesResponse 393). Applied only when
|
||||
* reply_term == s->term (stale-term drop, 415). success: next_index[peer] =
|
||||
* match_index+1, match_index[peer] = match_index. failure: next_index[peer] =
|
||||
* Max(next_index[peer]-1, 1). Does NOT advance commit. Returns RAFT_OK.
|
||||
*/
|
||||
int raft_on_append_entries_reply(raft_state_t *s, int peer_idx, int success,
|
||||
uint64_t match_index, uint64_t reply_term);
|
||||
|
||||
/* Fills snap with current state. Caller must hold lock. */
|
||||
void raft_snapshot(const raft_state_t *s, raft_snapshot_t *snap);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue