Commit graph

269 commits

Author SHA1 Message Date
b9ddfa60cb feat(ha-convergence-recovery): commit-gated scheduler egress — submit_lsn tagging + watermark gate (task 1.2) 2026-06-14 02:33:36 +00:00
7ff81156c2 test(ha-convergence-recovery): RED egress-filter tests + submit_lsn field + dataex decls (task 1.1) 2026-06-14 01:40:22 +00:00
dbc631664d docs(ha-single-leader-role): sync steering + contributor docs to three-state model + own-term gate (task 6.1) 2026-06-13 14:58:55 +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
7f974e766b test(ha-single-leader-role): sweep all COMMITTING fixtures to direct-PRIMARY contract (task 5.1) 2026-06-13 13:59:42 +00:00
71ce41f486 feat(ha-single-leader-role): delete dispatch-gate COMMITTING branch + collapse role predicates to PRIMARY (task 4.2) 2026-06-13 12:59:58 +00:00
a5bf86112a feat(ha-single-leader-role): delete force-promotion watchdog + convergence/ack machinery (task 4.1) 2026-06-13 12:05:08 +00:00
b7b951b9e7 feat(ha-single-leader-role): legacy election path symmetry — direct CTRL_PRIMARY via shared sequence (task 3.1) 2026-06-13 10:16:14 +00:00
c089fb69f3 feat(ha-single-leader-role): direct CTRL_PRIMARY promotion + own-term gate via shared sequence (task 2.2) 2026-06-13 09:26:08 +00:00
b7a69afa0b test(ha-single-leader-role): RED lifecycle tests for direct promotion + own-term gate (task 2.1) 2026-06-13 08:31:42 +00:00
e6abd7c8af feat(ha-single-leader-role): WAL_NOOP entry type — enum + apply no-op + RETAIN classify (task 1.2) 2026-06-13 07:45:21 +00:00
6df1283f1a test(ha-single-leader-role): RED tests for WAL_NOOP entry type (task 1.1) 2026-06-13 07:30:43 +00:00
5522d6f274 feat(ha-commit-barrier-rollout): mark_job_* idempotency/ordering guards (task 6.2)
Guards at the top of mark_job_start/finish/update under the caller's
job lock, before BEFORE_EVENT: duplicate (FINISH on terminal) → INFO
no-op skipping the AFTER_EVENT hook (no WAL append, lsn frozen);
terminal→non-terminal backward (START/UPDATE) → WARN reject preserving
state. Shared job_state_is_backward predicate provably admits every
production forward merge (PDONE/PERR/suspend bit-OR) while rejecting
overwrites. mark_job_fail intentionally unguarded (its merges are
always terminal-forward; job_ctrl pre-gates terminal). 5/5 guard tests
green; full job-lifecycle suite green; census 0 FAIL.
2026-06-13 04:54:36 +00:00
2308389eb1 test(ha-commit-barrier-rollout): RED tests for mark_job_* guards (task 6.1)
3 RED carriers (duplicate FINISH on DONE appends a 2nd WAL entry +
overwrites state; START on terminal clobbers it; replay non-equivalent)
+ 2 preservation pins (forward postdone PDONE-merge, fail-after-finish
PERR-merge) that 6.2 must not break. Reviewer-confirmed: the
UPDATE-clears-terminal arm is production-unreachable (pinned, not
driven). 22 tests / 3 RED; census 508 distinct triples, only those 3
fail.
2026-06-13 04:20:05 +00:00
5a2fae1d37 feat(ha-commit-barrier-rollout): config barrier with production ACK ingestion (task 5.2 + r1)
configAck branch: per-push majority (acked_config_lsn >= push_tag,
1+count > n_peers/2 over r->ce->n_peers, n_peers<=1 immediate,
ce==NULL -EINVAL); legacy >0 counting and n_active==0 shortcut dead.
push_config_to_standbys returns the barrier result; upgrade failure ->
send_upgrade_error (rc forwarded), no success ACK, no restart,
committed_config_tag success-only.

Remediation r1 (two CRITICAL review findings):
- F1: wire production config-ACK ingestion — replconfigpushreplymsg
  decode branch in repl_ack_decode_and_dispatch -> repl_on_config_ack
  on the promoted-vote ep; + defensive cluster.c OPCode_reply case.
  Closes the unlanded half of ha-config-bootstrap 8.1; without this the
  barrier would brick every multi-member config upgrade. No schema change.
- F2: keep the barrier off the iomgr thread — export
  push_config_transport_only (non-blocking) for the 5 ha.c + join.c
  streaming sites; barrier path now vtask-only (do_upgrade_config).

Wire-path test drives real capnp ACK bytes through the production decode
branch. 30/30 rollout, 6/6 repl_majority, 46/46 config_repl; census 0
FAIL; pre-existing fixture leak only (less than baseline).
2026-06-13 03:46:29 +00:00
9cdeba5816 test(ha-commit-barrier-rollout): RED tests for config barrier (task 5.1)
6 RED carriers pinning the three configAck defects (previous-tag acks
count, n_active==0 success shortcut, no-self-count denominator) and the
push/upgrade failure path (logged-only result, unconditional success
ACK + restart). 2 sanctioned pins (single-member, success-unchanged).
Behavior-preserving signature change: push_config_to_standbys gains
acked_out (return still legacy push count; 7 call sites NULL'd,
body-diff verified). 29/6 in group; no collateral failures.
2026-06-12 16:31:42 +00:00
732e836868 test(ha-commit-barrier-rollout): fact-retention-across-quorum-loss proof (task 4.3)
Green integration proof: 4 facts driven during total quorum loss append
RETAIN with rc 0 in <1000ms (vs 5000ms timeout — wrongly-applied WAIT
tripwire), pending with retained-depth=4 logged, present exactly once
on disk (real WAL header scan); reconnect + one covering ACK commits
the suffix with current_lsn unchanged (no redelivery). Completes the
bug-396 fix evidence (umbrella Req 2.8). Census 495/495.
2026-06-12 15:29:47 +00:00
4fafeadbbc feat(ha-commit-barrier-rollout): dispatch-gate fact allowlist (task 4.2)
notify/update/setpreason/clrpreason admitted in CTRL_PRIMARY without
write quorum (Req 3.2 / umbrella 2.8 / bug-396): all four terminate in
receipt-style RETAIN fact paths — reviewer-traced no-block proof (no
admitted opcode reaches a barrier/WAIT path; request opcodes stay
rejected). 21/21 group; census 494/494 zero FAIL; RED→GREEN
independently reproduced via stash-rebuild.
2026-06-12 14:50:28 +00:00
4fa38c6f88 test(ha-commit-barrier-rollout): RED tests for dispatch-gate fact allowlist (task 4.1)
4 RED carriers (notify/update/setpreason/clrpreason must be admitted
without quorum — currently NOT_READY, the bug-396 hole) + 2 pins (six
request opcodes stay rejected; quorum-present admits all ten). Census
494 = 488 + 6; exactly 4 FAILs.
2026-06-12 14:05:27 +00:00
7103fa643d test(ha-commit-barrier-rollout): receipt-ACK verdict pins (task 3.3)
4 green pins: handle_jobdispatch replies receipt-only with zero WAL
appends through a real 2-alloc dispatch cycle (notify outcomes intact,
reply-precedes-processing); set/clr_preason and do_update_op rusage
stay receipt-style with RETAIN-pending fact appends ACK-committed.
Umbrella Reply-Ordering Contract cited per pin. DISCOVERY bug-451
(reviewer-verified): RUSAGE hook va_list type confusion — 4-byte OOB
read + garbage state in WAL_JOB_RUSAGE payloads; fix deferred to 7.1.
Census 488; zero failures.
2026-06-12 11:16:51 +00:00
facdb90917 feat(ha-commit-barrier-rollout): commit-first-then-reply in all four handlers (task 3.2)
Uniform pattern (ctrl fn -> persist_commit_barrier -> reply) in
do_jobcontrol_op/do_queuecontrol_op/do_hostcontrol_op; join barrier
between AFTER_EVENT and success reply (failure -> error reply, skip
on_worker_join). FIXES bug-447: key_find found-rc normalized inside
job_ctrl/queue_ctrl/worker_ctrl — queue/host control now actually
append WAL and replicate (was silently unpersisted); idempotent no-ops
now return 0. handle_jobsubmit untouched. 12/12 + 15/15; census zero
failures.
2026-06-12 10:28:25 +00:00
e1c3c173f4 test(ha-commit-barrier-rollout): RED commit-first-then-reply tests per opcode (task 3.1)
New test_commit_barrier_reply.c drives do_jobcontrol_op /
do_queuecontrol_op / do_hostcontrol_op / handle_worker_join directly
(pthread harness, ep->outq reply capture, wired engine n_peers=3).
12 cases: 8 RED (reply-before-commit + bug-447 pins), 4 validation
pins green. DISCOVERY bug-447 (reviewer-confirmed): key_find found-rc
leak means queue/host control never append WAL in production — 3.2's
rc normalization is the fix point. Census 484 = 472 + 12; zero
baseline regressions.
2026-06-12 10:00:38 +00:00
8f688eace1 feat(ha-commit-barrier-rollout): persist_commit_barrier + legacy walAck retirement (task 2.2)
Barrier: watermark = current_lsn - 1 under p->lock (released before
blocking), covered/nothing-appended/LSN-0 short-circuits, sync_timeout_ms
fallback, engine code unmodified, ce==NULL vacuous. repl.c walAck track
now engine-only (legacy acked_lsn>0 counting and walAck-reachable
n_active==0 success deleted); configAck track byte-equivalent for
task 5. 15/15 group; census 472/472.
2026-06-12 05:36:42 +00:00
611fbc8d7b test(ha-commit-barrier-rollout): RED tests for persist_commit_barrier (task 2.1)
7 cases: two covered short-circuits, timeout with committed_lsn
unmoved, covering-ACK release, sync_timeout_ms fallback (bounded both
directions), n_peers=1 standalone, NOT_READY passthrough via cap
saturation. Watermark resolved to current_lsn - 1 (design's literal
phrasing unsatisfiable per persist.h:71 invariant) with off-by-one
killing power both ways. 15 tests / 7 RED; suite baseline zero.
2026-06-12 05:09:11 +00:00
e8ecc87551 feat(ha-commit-barrier-rollout): wal_classify + classified hook dispatch (task 1.2)
Verified table implemented (STOP/RESUME/UPDATE/FAIL RETAIN per
supersession; WORKER detail switch; CHECKPOINT LOCAL); unknown type =
ERROR + XCE_TESTING abort + production RETAIN fallback. All three
hooks route through wal_hook_append_classified (zero bare async
appends); RETAIN registers with the engine lock-free; facts propagate
local append rc only. 8/8 group tests green; full suite 465/465.
2026-06-12 04:43:50 +00:00
fec9519960 test(ha-commit-barrier-rollout): RED tests for classification totality (task 1.1)
New test_commit_barrier_rollout.c: per-type rows matching the VERIFIED
design table (STOP/RESUME/UPDATE/FAIL RETAIN per supersession), 13-value
totality sweep, fork/SIGABRT unknown-type fail-fast, fact-hook retention
contract against real persist+engine. wal_commit_mode_t + wal_classify
declared in persist.h; inert sentinel stub. 8/8 RED; suite baseline
zero preserved. Finding: RETAIN is registration-free (commit.c:233-239)
— observable contract documented in Implementation Notes.
2026-06-12 04:13:22 +00:00
95a10cedf3 test(ha-commit-engine): full regression green + legacy-path audit (task 6.1)
Fixed the 3 bug-411 baseline failures honestly: stale timeout
expectation updated to the reject-on-timeout contract (engine-wired
fixture, killing power retained); two healthy-primary gate fixtures
populated with standby_endpoints (gate logic untouched). Census: 46
groups / 467 tests / 0 failures standalone under default ASAN.
Audit (reviewer-confirmed, recorded in tasks.md): no production walAck
path reaches the deprecated acked_lsn>0 counting or n_active==0
short-circuit except the documented LSN-0 first entry. Leak inventory
for 6 groups documented as follow-up.
2026-06-12 03:32:19 +00:00
f0d6074d4e feat(ha-commit-engine): engine instantiation + sync-append path switch (task 5.3)
cluster_t embeds commit_engine_t; init in cluster_init_phase2 after the
vtaskmgr guard (idempotence-guarded — phase2 can run twice), wiring
repl_set_commit_engine + ha.repl back-pointer before any standby can
register; fini after iomgr/vtask shutdown. persist_wal_sync_append
passes written_lsn; engine-wired path drops -ENODEV and the
committed_lsn store (engine owns advancement); LSN-0 first-entry
routes to legacy per the accepted interim. p2 -ENODEV pin updated to
the Req 6.3 contract. 34/34 + 6/6 green through honest delegation;
census exactly at the 3 validated baselines; production binary links.
2026-06-12 00:16:22 +00:00
6600fd5d1f feat(ha-commit-engine): repl-side wiring — ACK feed, membership hooks, adapter (task 5.2)
replicator_t gains ce pointer (set-once-before-traffic contract).
repl_on_ack feeds commit_on_ack after releasing r->lock (iomgr-safe);
four membership mutators feed commit_on_membership_change post-unlock,
gated on actual mutation. walAck branch delegates to commit_submit(WAIT)
when ce wired AND entry_lsn != 0 (LSN-0 interim guard until 5.3);
legacy counting kept for ce==NULL, marked DEPRECATED. commit.c linked
into production target (inert until 5.3 wiring). Tests 30-31 + all
repl_majority walAck cases green; 33/34 remain RED for 5.3; baseline
unchanged.
2026-06-11 23:32:59 +00:00
d1353360cc test(ha-commit-engine): RED integration tests for ACK feed and sync-append (task 5.1)
Tests 30-34: wired repl_on_ack feeds engine, membership mutators
trigger re-evaluation, per-entry sync-append contract (stale-ACK
rejection, committed_lsn unmoved on timeout, n_peers=1 replaces
-ENODEV). test_repl_majority walAck cases rewired to per-LSN
semantics (configAck byte-identical). Sanctioned scaffolding:
entry_lsn param on repl_wait_majority_ack (legacy body byte-identical,
callers pass 0), inert repl_set_commit_engine stub. 9 RED failures;
baseline signatures unchanged. LSN-0 edge documented for 5.2/5.3.
2026-06-11 23:09:05 +00:00
c9154bd5cc feat(ha-commit-engine): honest write-admission quorum accounting (task 4.2)
repl_active_standby_count: registry truth (ep != NULL && !in_catch_up)
under r->lock. ha_state_t gains repl back-pointer (wired at init by
task 5.3); ha_has_write_quorum uses registry truth when wired, exact
legacy fallback when NULL; formula unchanged. Test 29 (wired gate,
catch-up-only standby → NOT_READY, admits after clear) captured RED
pre-fix. 29/29 group tests; suite at the 3-failure baseline.
2026-06-11 22:28:44 +00:00
b2598823c0 test(ha-commit-engine): RED tests for write-admission quorum counting (task 4.1)
Tests 21-28: repl_active_standby_count contract (5 RED vs inert -1
stub) + 3 gate-level pins (n_peers<=1 always-true, zero-connected
reject, stateless re-accept) that 4.2 must preserve. Declaration in
repl.h; ha.c/ha.h untouched (4.2 boundary). 28 tests / 5 RED; suite
at the 3-failure baseline with unchanged signatures.
2026-06-11 16:15:21 +00:00
631a08939b test(ha-commit-engine): membership re-evaluation regression tests (tasks 3.1+3.2 joint)
Tests 17-20: catch-up ACK exclusion vs pending waiter, membership-change
wake of covered waiter, removal-below-majority fails at own deadline
(no forced early-fail), non-blocking bound. Landed GREEN: task 1.2's
commit_on_membership_change already implements the full 3.2 contract,
so RED was unachievable; reviewer confirmed mutation-killing power and
zero production changes. 20/20 group tests; suite at baseline.
2026-06-11 15:48:42 +00:00
3bc6fd14de feat(ha-commit-engine): WAIT mode, admission cap, writable_lsn gate (task 2.2)
commit_submit(WAIT): n_vcpus-relative admission cap with fast retriable
NOT_READY (admission-cap-only per corrected contract), CLOCK_REALTIME
timedwait loop on the per-LSN + gate predicate, post-timeout predicate
re-check, n_peers<=1 self-quorum through the single advancement site.
Inflight counter accurate under engine lock for the full blocking
interval with XCE_TESTING asserts. 16/16 group tests green unmodified;
full suite at the 3-failure baseline.
2026-06-11 15:24:30 +00:00
d91f446e9e test(ha-commit-engine): RED tests for WAIT mode, admission cap, own-term gate (task 2.1)
9 new cases (8-16): block-until-majority, deadline timeout, standalone
fast path, zero-ACK regression, n_vcpus-relative cap with floor, ACK
progress at saturation, gate as blocking predicate (NOT_READY is
admission-cap-only per corrected contract). Main-thread-only assertions
after joins; 16 tests / 9 RED failures; baseline unchanged.
2026-06-11 14:56:31 +00:00
eae4767d7d feat(ha-commit-engine): per-LSN majority commit engine core (task 1.2)
commit_on_ack: active-standby (non-catch-up, connected) watermark scan,
descending sort, candidate arr[n_peers/2-1], clamped to appended log,
monotonic single-site committed_lsn store, condvar broadcast, never
blocks beyond engine mutex. RETAIN returns immediately. Self-quorum for
n_peers<=1; zero-fill makes zero-standby commit impossible. 7/7 task-1.1
tests green unmodified; full suite at known 3-failure baseline.
2026-06-11 14:37:01 +00:00
d5a0f9c64d test(ha-commit-engine): RED tests for per-LSN majority evaluation (task 1.1)
commit.h umbrella-verbatim interface + inert stub commit.c (test target
only); 7 Unity tests covering per-LSN vs acked_lsn>0 counting, majority
median, monotonicity, append clamp, lagging standby, RETAIN immediacy.
All 7 fail RED as intended. Documented 3 pre-existing masked suite
failures (bug-411) and the per-group exit-code masking root cause.
2026-06-11 14:17:09 +00:00
06135954ff chore: baseline snapshot — prior-session work + ha-consistency-invariants spec family
Pre-existing uncommitted work: ha-repl-hardening payload data plane (7.x),
heartbeat liveness fix (Req 1.3), connection-reuse ASAN fixes, doc cleanup.
New: ha-consistency-invariants umbrella (requirements/design/research) and
its four implementation sub-specs (specs generated via batch, cross-spec
review applied), steering roadmap + product.md Raft+CP decision.
2026-06-11 13:21:50 +00:00
ab270fab99 test(ha-wal-replication): registration lifecycle, broadcast, ACK tests (tasks 5.1-5.3)
Add test_repl_registration_lifecycle covering the complete 3-step standby
lifecycle: register (nstandbys==1, ep pointer verified), duplicate
(idempotent, same slot), deregister (ep==NULL, nstandbys==0 — the
previously-untested slot-zeroing invariant). Tasks 5.1/5.2 were already
covered by test_broadcast_one_standby_sends_entry, test_wal_wire
round-trip, and test_wal_ack_reply_updates_acked_lsn_and_signals.
18 repl unit tests pass under ASAN.
2026-06-10 11:41:43 +00:00
e2fba08f5d test(ha-wal-replication): verify majority-ack advances committed_lsn (task 4.2)
Fix defective assertion in test_committed_lsn_advances_after_majority_ack
(before/after delta instead of >0 which fails for lsn=0 first write).
Add test_single_node_committed_lsn_unchanged documenting -ENODEV degraded
mode for no-standby path. Fix NACK test expected return from -ETIMEDOUT to
0: with 1 registered standby, quorum=1 is met by primary self-count alone.
308 tests pass, 0 fail under ASAN.
2026-06-10 11:18:25 +00:00
861b746c84 feat(ha-wal-replication): primary WalAckReply handler (task 4.1)
Handle incoming WalAckReply in repl_ack_decode_and_dispatch: extract
lsn+success, call repl_on_ack on success to update standby acked_lsn
and signal majority-ack waiter. NACK is WARN-logged with no state
change. Two unit tests verify positive path (acked_lsn updated, waiter
unblocked) and negative path (NACK leaves acked_lsn=0, wait times out).
2026-06-10 09:46:22 +00:00
566c91cd8b feat(ha-wal-replication): standby WAL ACK reply (tasks 3.1-3.3)
Standby sends WalAckReply via xce_reply after each successfully
applied WAL entry. ha_apply_wal_catchup_entry signature extended
with endpoint_t* and cluster_t* parameters. walackreplymsg union
arm added to RequestReply capnp schema and xce_reply_t. Error
entries skip ACK. Dispatch gate test confirms OPCode_walCatchupEntry
allowed in CTRL_STANDBY/ELECTING/INIT.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-10 09:14:25 +00:00
81dbc4d7b4 feat(ha-wal-replication): HA→replicator standby registration bridge (tasks 2.1-2.3)
Register standbys with replicator on connect (election promotion +
late-joiner), deregister on disconnect (vote err handler + standby err
handler). repl_add_standby now idempotent (duplicate ep returns
existing slot).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-06-10 08:35:02 +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
fa66d1ba9c finalize configuration replication 2026-06-09 16:40:13 +08:00
e1517bbf28 fix(ha-repl-hardening): heartbeat re-arm broken by delaymgr ms-task ordering
Root cause: task_cmp in delay.c compared nextrun (always 0 for ms-tasks)
instead of nextrun_ts, making all ms-tasks appear equal in the ordered
queue. The heartbeat task was re-enqueued correctly but stuck behind the
liveness poll task in the broken ordering.

Changes:
- delay.c: fix task_cmp to compare nextrun_ts for ms-tasks (ns precision)
- ha.c: use runcount=-1 (infinite repeat) instead of one-shot+re-arm;
  simpler and avoids the broken re-arm path entirely
- ha.c: add static atomic idempotency guard to prevent duplicate heartbeat
  tasks on re-election
2026-06-06 13:28:00 +00:00
f236d320a2 refactor(ha-repl-hardening): standardize n_peers = total cluster size including self
- ha_state_t.n_peers now matches raft_config_t.n_peers convention:
  total cluster size (including self), consistently across both layers
- ha_start_election_async: ha->n_peers = n_peers (candidates_count)
- Convergence/counting loops: i < n_peers - 1 (skip self for standby arrays)
- Single-node: n_peers=1, threshold=0 (same behavior, clear semantics)
- ha_elect_ctx_t.n_peers unchanged (separate field for vote array sizing)
- Tests: 3-node n_peers=2->3, single-node 0->1, loop bounds updated

E2E test 6.7: 3-node cluster lifecycle verified
- Initial: ctrl-1 PRIMARY via majority convergence, no degraded mode
- Kill primary: RDHUP detected on both standbys, ctrl-2 elected at term=4
- No data loss: config tag preserved across failover
- Full ASAN build green
2026-06-05 09:57:23 +00:00
361397ac02 fix(ha-repl-hardening): ASAN bugs found in snapshot integration test (task 6.6)
- ha_connect_to_peer: set ep->data=NULL before endpoint_fini to prevent
  double-free of sctx (FREEUP in endpoint_fini + explicit free)
- ha_arm_heartbeat_timeout_vtask: defer free to liveness_poll_fn
  (set cancelled=1 only) to prevent UAF race with delaymgr task
- ha_disarm_heartbeat_timeout_vtask: remove free(ctx), matching
  deferred-free contract from ha_arm fix
- Integration test: snapshot transfer trigger confirmed (standby
  lsn=0 < oldest_wal_lsn=1 → SEND_SNAPSHOT on primary)
2026-06-05 08:06:24 +00:00
e10cf9a6fc fix(ha-repl-hardening): n_peers count and single-node safe_lsn (task 6.5)
- Fix ha_start_election_async: ha->n_peers = n_other (other peers),
  not candidates_count (total including self). The standby_converged[]
  array is indexed by peer index, so the bound must be n_other.
- Fix ha_compute_safe_committed_lsn: return self_lsn for n_peers==0
  (prevents WAL truncation on single-node re-election).
- Integration test: 3-node cluster reaches CTRL_PRIMARY via majority
  convergence within ~20s; no degraded-mode WARN in logs.
2026-06-05 07:03:37 +00:00
345d51dd61 feat(ha-repl-hardening): audit safe commit point and snapshot transfer tests (tasks 6.3, 6.4)
6.3: Fix single-node bug — ha_compute_safe_committed_lsn now returns
self_lsn instead of 0 for n_peers=0 (prevents WAL truncation on
single-node re-election). Add self_lsn/self_tag to ha_elect_ctx_t,
populated unconditionally in ha_start_election_async. All 3 test
scenarios pass under ASAN.

6.4: Add test_snapshot_recv_128kb_two_chunks (128KB snapshot in
2x64KB chunks, verifies size and applied_lsn); 4 other test cases
already existed from tasks 3.5c/3.5d. All ASAN-clean.
2026-06-05 06:21:00 +00:00