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.
This commit is contained in:
Rongsong Shen 2026-06-11 14:17:09 +00:00
parent 06135954ff
commit d5a0f9c64d
10 changed files with 757 additions and 283 deletions

View file

@ -0,0 +1,61 @@
#pragma once
/*
* commit.h — Commit engine: single authority for per-entry commit evaluation,
* committed_lsn advancement, and waiter completion.
*
* Service interface is umbrella-verbatim from
* .kiro/specs/ha-consistency-invariants/design.md (sliced by ha-commit-engine).
*
* All functions thread-safe; commit_on_ack callable from iomgr (non-blocking).
*/
#include <pthread.h>
#include <stdint.h>
#include "persist.h" /* persist_t (owns committed_lsn atomic + replicator_t registry) */
/* Headroom kept free of blocking commit waits; cap = max(1, n_vcpus - reserved) */
#define COMMIT_WAIT_RESERVED 2
typedef enum { COMMIT_MODE_WAIT, COMMIT_MODE_RETAIN } commit_mode_t;
/*
* Commit engine state — in-memory only (commit state volatile, Raft-conformant).
* Per-standby view delegated to standby_conn_t.acked_lsn / in_catch_up via
* p->repl (no duplication).
*/
typedef struct commit_engine {
pthread_mutex_t lock;
pthread_cond_t cond; /* broadcast on committed_lsn advance / membership change */
persist_t* p; /* back-reference: committed_lsn store + repl registry */
int n_peers;
int n_vcpus;
int inflight_waits; /* <= max(1, n_vcpus - COMMIT_WAIT_RESERVED) */
uint64_t writable_lsn; /* own-term gate; 0 = no gate */
uint64_t scratch[MAX_STANDBYS]; /* sorted matchIndex scratch array */
} commit_engine_t;
/* Lifecycle. init validates n_peers >= 0, n_vcpus >= 1, non-NULL persist_t;
* returns -EINVAL otherwise. Call after n_peers and n_vcpus are final. */
int commit_engine_init(commit_engine_t* ce, persist_t* p, int n_peers, int n_vcpus);
void commit_engine_fini(commit_engine_t* ce);
/* Register entry at append time. RETAIN returns 0 immediately.
* WAIT blocks up to timeout_ms: 0 = committed; -ETIMEDOUT = not committed
* (entry remains in log; outcome resolves per umbrella 2.5); -XCE_ERR_NOT_READY =
* admission cap exhausted (only cause). An unmet own-term gate does NOT fail
* fast: the gate is part of the completion predicate, so WAIT blocks within
* timeout_ms until the gate opens, surfacing -ETIMEDOUT if it never does. */
int commit_submit(commit_engine_t* ce, uint64_t lsn, commit_mode_t mode,
uint32_t timeout_ms);
/* iomgr path: record standby ACK, evaluate majority, advance committed_lsn
* contiguously, wake satisfied waiters. Never blocks. */
void commit_on_ack(commit_engine_t* ce, int standby_idx, uint64_t acked_lsn);
/* Promotion: set own-term gate to the no-op entry's LSN.
* (API ships here; no caller in this spec — unset = no gate.) */
void commit_set_writable_lsn(commit_engine_t* ce, uint64_t noop_lsn);
/* Quorum-membership change (standby add/remove/catch-up transition):
* re-evaluate pending entries; standbys in catch-up are excluded. */
void commit_on_membership_change(commit_engine_t* ce);