Commit graph

20 commits

Author SHA1 Message Date
e5b9b83955 feat(raft-log-replication): raft_log_append leader client-entry append (tasks 3.1, 3.2)
Leader-only (-EPERM for follower/candidate, no mutation/callback); index=
log_len+1, term=currentTerm (raft.tla ClientRequest:246, Req 2.1-2.4);
persist-before-act: on_persist_entry called once BEFORE log_len advances,
non-zero return propagated with no phantom entry (Req 2.5); no commit-index
advance, no transport I/O. Spy-based tests for all four branches. ASAN green.
2026-06-21 15:50:33 +00:00
fc87a662cd fix(raft): reject n_peers > RAFT_MAX_PEERS in raft_init
next_index[]/match_index[]/pre_vote_replies[] are fixed-size RAFT_MAX_PEERS
arrays indexed by peer; without an upper-bound check a caller passing
n_peers > 8 would OOB-write in the leader-progress and pre-vote loops.
Add the guard (returns -EINVAL, no mutation) + canary-checked test.

Latent since task 1.1; surfaced by task 2.2 review (first loop writes into
the new arrays). No current caller exceeds the bound (sim caps at 8, HA <= 4).
2026-06-21 15:42:37 +00:00
6ea89569fa feat(raft-log-replication): init leader progress on becoming leader (task 2.2)
next_index[j]=log_len+1, match_index[j]=0 for every peer at both win paths
(single-node RAFT_ELECTED_IMMEDIATELY in raft_start_election; multi-node
RAFT_BECAME_LEADER in raft_on_vote_reply) via shared raft_init_leader_progress
helper (raft.tla BecomeLeader, Req 3.1). Suite green under ASAN.
2026-06-21 15:40:26 +00:00
d70f2507c3 feat(raft-log-replication): require 4 log callbacks in raft_init + sim log backing (tasks 2.1, 8.1)
2.1: raft_init returns -EINVAL when any of on_persist_entry/on_get_entry_term/
on_send_append_entries/on_truncate_from is NULL (Req 7.1-7.4), before any write
to *s; restores s->log_len = cfg->initial_log_len (Req 1.1).

8.1: raft_sim gains a per-node in-memory log (term+bytes per 1-based index) and
an AE buffer queue; wires all four callbacks (persist/get-term/truncate/send).
Pulled forward to land with 2.1 so the stricter init does not leave the
sim/proto suite red between tasks. AE delivery deferred to 8.2.

Suite green under ASAN (Ok:1 Fail:0).
2026-06-21 15:30:09 +00:00
9fac681510 feat(ha-prevote): implement pure PreVote reply tally (task 2.3)
raft_on_pre_vote_reply: per-peer-deduped tally returning WON at quorum
((n_peers/2)+1), LOST when all non-self peers reply without quorum, else
PENDING; bounds-guards peer_idx. Never fires become-leader/follower, never
mutates term/voted_for/role, never persists. 8 RED-first tests (3- and 5-node,
dedup, out-of-range, purity). Raft PreVote pure core complete.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 04:45:10 +00:00
6db912b2a9 feat(ha-prevote): implement pure candidate-side PreVote start (task 2.2)
raft_begin_pre_vote: compute prospective term (term+1) locally without
persisting, arm the pre_vote_* tally with a self-grant, NULL-safely probe each
non-self peer, return RAFT_PRE_VOTE_WON for single-node else RAFT_OK. Stays
FOLLOWER, schedules no timeout, mutates no persistent state. 6 RED-first tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 04:33:53 +00:00
722fee5323 feat(ha-prevote): implement pure voter-side PreVote decision (task 2.1)
raft_on_pre_vote_request: deny on stale term, else reuse on_vote_eligible
(primary/heartbeat-stickiness/catch-up gates). Never mutates term/voted_for/
role and never persists or adopts a higher term. 6 RED-first raft unit tests
assert the purity invariant; libxce suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 04:24:23 +00:00
93dfc02717 feat(ha-single-leader-role): remove CTRL_PRIMARY_COMMITTING enum + remaining decls — compiler-enforced (task 5.2) 2026-06-13 14:41:42 +00:00
9821ec4f3c fix(ha): election-safety hardening — vote gates, restored-vote term, freshness source
Election protocol fixes validated against raft.tla invariants:

- ha_init_raft(): one-time raft state init at startup (re-init would lose
  durable election progress); n_peers = full cluster size including self
- handle_request_vote(): higher-term step-down moved BEFORE the role gate
  (split-brain fix); standby vote-deny now requires a live primary control
  channel (primary_ctrl_fd >= 0) AND a fresh heartbeat
- ha_heartbeat_is_fresh(): derive freshness from ha->last_activity (the
  field production actually writes); the orphaned last_heartbeat timespec
  had no production writer, leaving the vote-deny gate inert (bug-337);
  field removed from ha_state_t
- handle_request_vote(): restore same-candidate idempotent re-grant —
  deny only a DIFFERENT candidate in the same term, matching raft.c and
  Raft §5.2 votedFor ∈ {Nil, candidateId} (bug-338)
- raft_init(): restore voted_for_term alongside a persisted vote; leaving
  it 0 allowed a double vote in the same term across a restart (bug-339)
- myapp_run(): leader_known check treats a vote recorded under this node's
  own name as a self-vote — production persists the real candidate name,
  the "self" placeholder only appears in test mode
- raft_on_vote_request(): leader steps down on higher-term vote request
  instead of denying it

Tests: test_proto_H6_restored_vote_blocks_second_vote added; H1 restore
assertion strengthened; test_ha_vote_gate.c driven through last_activity +
primary_ctrl_fd (the real production fields). Full /xce-build all green
(lib + controller + scheduler + Go suites, 0 failures, ASAN clean).

deploy-ha.sh: start.sh now forwards extra args to the controller binary.
2026-06-10 01:25:39 +00:00
e31673dd88 chore: checkpoint all prior work for clean state
Captures all uncommitted work from ha-config-bootstrap,
ha-heartbeat-liveness, and associated spec/tooling updates:

- src: ha.c, ha.h, cluster.c, config_repl.c, myapp.c, raft.c —
  heartbeat liveness, RDHUP integration, committing phase fixes
- tests: test_ha_role.c, test_config_repl.c, test_ha_rdhup_integration.c,
  test_p1_integration.c, test_p3_hardening.c, scheduler test
- specs: election-iomgr-refactor, ha-heartbeat-liveness,
  ha-peer-connection-reuse, raft-library (new); ha-config-bootstrap
  and election-iomgr-refactor task/research updates
- tooling: .wolf/ state, ha/data cluster fixtures, raft/raft.tla,
  src/build symlink, xce-build skill update
2026-06-04 13:04:16 +00:00
becf6ee765 feat(raft-library): reject even n_peers in raft_init; add test_raft_init_even_peers_returns_einval 2026-05-31 08:48:30 +00:00
5c677bc52d feat(raft-library): task 5.3 — Groups D+E 7 test cases; fix Req 6.3 on_cancel_timeout in decided path 2026-05-31 06:58:02 +00:00
85a7e325a8 feat(raft-library): task 3.5 — election timeout, leader-lost, check-and-start; 6 tests 2026-05-31 06:02:29 +00:00
f81054930f feat(raft-library): task 3.4 — raft_on_vote_reply quorum tracking; 6 tests + review fixes 2026-05-31 05:56:34 +00:00
e0bcb35554 feat(raft-library): task 3.3 — raft_on_vote_request 6-step decision; 10 tests 2026-05-31 05:44:34 +00:00
4955fa3805 feat(raft-library): task 3.2 — raft_start_election with single-node shortcircuit; 4 tests 2026-05-31 05:30:00 +00:00
1aa7db4583 feat(raft-library): task 3.1 — raft_init validation, state init, raft_snapshot; 12 tests 2026-05-31 05:18:42 +00:00
d109155a21 feat(raft-library): task 2.2 — raft_callbacks_t, raft_config_t, public API signatures 2026-05-31 05:03:04 +00:00
66c9810823 feat(raft-library): task 2.1 — raft_role_t, raft_state_t, raft_snapshot_t in raft.h 2026-05-31 04:56:49 +00:00
7f7de013b9 feat(raft-library): task 1.1 — build scaffold (raft/ files + meson wiring) 2026-05-31 04:51:42 +00:00