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.
This commit is contained in:
parent
6600fd5d1f
commit
f0d6074d4e
6 changed files with 142 additions and 50 deletions
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "commit.h"
|
||||
#include "config.h"
|
||||
#include "persist.h"
|
||||
#include "repl.h"
|
||||
|
|
@ -219,44 +220,50 @@ static void test_repl_api_completeness(void)
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Test 4b: Req 4.4 / single-node path — committed_lsn NOT advanced by
|
||||
* persist_wal_sync_append when no standbys are registered.
|
||||
/* Test 4b: single-node path — production contract (ha-commit-engine Req 6.3).
|
||||
*
|
||||
* persist_wal_sync_append uses has_standbys flag. With no standbys it skips
|
||||
* repl_wait_majority_ack entirely and sets sync_err = -ENODEV (degraded mode).
|
||||
* The committed_lsn therefore remains unchanged. This is intentional: a
|
||||
* single-node controller operates in degraded mode — WAL writes are locally
|
||||
* durable but not majority-committed. Req 4.4 convergence only applies once
|
||||
* standbys are registered and ACK.
|
||||
* UPDATED by ha-commit-engine task 5.3: this test previously pinned the
|
||||
* `has_standbys → -ENODEV` degraded special case, which 5.3 deliberately
|
||||
* replaced. In production the commit engine is wired at cluster init, so a
|
||||
* single-member primary (n_peers=1) self-commits: persist_wal_sync_append
|
||||
* returns 0, sync status is 0 (NOT -ENODEV), and committed_lsn advances to
|
||||
* the entry. The engine — not persist — owns the advancement.
|
||||
*
|
||||
* This test documents the actual behaviour so regressions are caught if the
|
||||
* single-node path is accidentally changed to advance committed_lsn.
|
||||
* A priming async append makes the sync entry land at LSN 1: the first-ever
|
||||
* WAL entry is LSN 0, which the adapter routes to the legacy branch (the
|
||||
* accepted once-per-cluster-lifetime LSN-0 interim behaviour).
|
||||
*/
|
||||
static void test_single_node_committed_lsn_unchanged(void)
|
||||
static void test_single_node_committed_lsn_advances(void)
|
||||
{
|
||||
commit_engine_t ce;
|
||||
|
||||
persist_t* p = make_p2_persist("clsn_1n");
|
||||
TEST_ASSERT_NOT_NULL(p);
|
||||
|
||||
uint64_t committed_before =
|
||||
atomic_load_explicit(&p->committed_lsn, memory_order_acquire);
|
||||
/* Wire the engine exactly as cluster_init_phase2 does for n_peers=1 */
|
||||
TEST_ASSERT_EQUAL_INT(0, commit_engine_init(&ce, p, 1, 8));
|
||||
repl_set_commit_engine(&p->repl, &ce);
|
||||
/* Priming append: sync entry below lands at LSN 1 (engine-wired path) */
|
||||
TEST_ASSERT_EQUAL_INT(0, persist_wal_async(p, WAL_JOB_UPDATE, "p", 1));
|
||||
|
||||
int sync_rc = 0;
|
||||
int sync_rc = -1;
|
||||
int rc = persist_wal_sync_append(p, WAL_JOB_SUBMIT, "job", 3, &sync_rc);
|
||||
|
||||
/* WAL write must succeed locally */
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
/* Degraded mode: no standbys → -ENODEV */
|
||||
TEST_ASSERT_EQUAL_INT(-ENODEV, sync_rc);
|
||||
/* committed_lsn must remain at its pre-call value (not advanced) */
|
||||
uint64_t committed_after =
|
||||
atomic_load_explicit(&p->committed_lsn, memory_order_acquire);
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(committed_before, committed_after,
|
||||
"single-node: committed_lsn must stay unchanged (degraded, no standbys)");
|
||||
/* Single-member self-quorum: confirmed locally, no -ENODEV degraded mode */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, sync_rc,
|
||||
"single-node wired engine must self-commit (0), not -ENODEV");
|
||||
/* committed_lsn advances to the entry (engine-owned advancement) */
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
|
||||
1, atomic_load_explicit(&p->committed_lsn, memory_order_acquire),
|
||||
"single-node: committed_lsn must advance to the self-committed entry");
|
||||
/* WAL LSN did advance (local write succeeded) */
|
||||
TEST_ASSERT_TRUE(p->current_lsn > 0);
|
||||
|
||||
commit_engine_fini(&ce);
|
||||
teardown_p2_persist(p);
|
||||
printf("test_single_node_committed_lsn_unchanged: PASS\n");
|
||||
printf("test_single_node_committed_lsn_advances: PASS\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -407,7 +414,7 @@ void test_p2_integration(void)
|
|||
RUN_TEST(test_trim_lsn_with_active_standby_at_lsn_5);
|
||||
RUN_TEST(test_checkpoint_does_not_update_replicated_crc);
|
||||
RUN_TEST(test_sync_replication_degraded_mode);
|
||||
RUN_TEST(test_single_node_committed_lsn_unchanged);
|
||||
RUN_TEST(test_single_node_committed_lsn_advances);
|
||||
RUN_TEST(test_catchup_full_lifecycle_integration);
|
||||
RUN_TEST(test_needs_snapshot_boundary);
|
||||
RUN_TEST(test_repl_api_completeness);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue