code wiki / _hdl_build / nx_ha_chaos_gate.nx

nx_ha_chaos_gate.nx source

↩ module page · 57 lines · 4316 B

1import "nx_gate_gn.nx" 2// nx_ha_chaos_gate.nx -- availability CONFORMANCE capstone (the "chaos" gap): compose failover + replication + 3// quorum and inject faults, asserting the cluster survives with negative controls: 4// T1 steady: data replicated byte-exact, quorum leader=0, active=PRIMARY 5// T2 primary dies: replicated data SURVIVES on the standby, quorum re-leads (2/3), failover promotes standby 6// T3 minority partition (1/3): NO leader elected (split-brain prevented) + failover DEGRADED (honest) 7// T4 recover (2/3 back): quorum re-elects a leader 8// Sovereign: nx_syscalls + nx_ha_failover + nx_ha_replication + nx_ha_quorum. expect_exit: 0 9import "nx_syscalls.nx" 10import "nx_ha_failover.nx" 11import "nx_ha_replication.nx" 12import "nx_ha_quorum.nx" 13import "nx_gate_verdict.nx" 14 15func gp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 16func g_trunc(path: *u8) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd>=0 { sys_close(fd) } return 0 } 17 18func main(argc: i64, argv: *i64) -> i64 { 19 gp("=== nx_ha_chaos_gate (availability conformance: fault injection + survival) ===\n" as *u8) 20 let P: *u8 = "/tmp/chaos_p.log" as *u8 21 let S: *u8 = "/tmp/chaos_s.log" as *u8 22 g_trunc(P); g_trunc(S) 23 let h: *i64 = sys_mmap(32) as *i64; h[0]=1; h[1]=1; h[2]=1 // 3-node cluster, all up 24 let fo: *i64 = sys_mmap(16) as *i64; fo[0]=HA_PRIMARY // failover pair (primary/standby) 25 var pass: i64 = 0; var fail: i64 = 0 26 27 // T1 steady: write + replicate, quorum leader 0, active primary 28 rep_append(P, "commit-1\n" as *u8, 9) 29 rep_ship(P, S, 0) 30 if rep_in_sync(P, S) == 1 { if qm_leader(h, 3) == 0 { if fo_step(fo, HA_UP, HA_UP) == HA_PRIMARY { pass=pass+1; gp(" T1 steady: replicated, leader 0, active PRIMARY PASS\n" as *u8) } else { fail=fail+1; gp(" T1 FAIL fo\n" as *u8) } } else { fail=fail+1; gp(" T1 FAIL leader\n" as *u8) } } else { fail=fail+1; gp(" T1 FAIL sync\n" as *u8) } 31 32 // T2 CHAOS: primary (node 0) dies. Data already on the standby survives; quorum re-leads; failover promotes. 33 h[0] = 0 // kill primary 34 let survived: i64 = rep_in_sync(P, S) // the standby still holds the committed data 35 let newleader: i64 = qm_leader(h, 3) // 2/3 up -> node 1 leads 36 let newactive: i64 = fo_step(fo, HA_DOWN, HA_UP) // primary down, standby up -> failover 37 if survived == 1 { if newleader == 1 { if newactive == HA_STANDBY { pass=pass+1; gp(" T2 primary dies: DATA SURVIVES on standby + quorum re-leads(1) + failover->STANDBY PASS\n" as *u8) } else { fail=fail+1; gp(" T2 FAIL no failover\n" as *u8) } } else { fail=fail+1; gp(" T2 FAIL leader=" as *u8); gn(newleader); gp("\n" as *u8) } } else { fail=fail+1; gp(" T2 FAIL data lost\n" as *u8) } 38 39 // T3 CHAOS: partition down to a 1/3 minority -> NO leader (split-brain prevented), failover DEGRADED 40 h[1] = 0 // now only node 2 up (1/3) 41 if qm_leader(h, 3) == (0-1) { if fo_step(fo, HA_DOWN, HA_DOWN) == HA_DEGRADED { pass=pass+1; gp(" T3 minority 1/3: NO leader (no split-brain) + DEGRADED PASS\n" as *u8) } else { fail=fail+1; gp(" T3 FAIL not degraded\n" as *u8) } } else { fail=fail+1; gp(" T3 FAIL minority led\n" as *u8) } 42 43 // T4 recover: 2/3 back -> quorum re-elects 44 h[0] = 1 // node 0 back (nodes 0,2 up = 2/3) 45 if qm_leader(h, 3) == 0 { pass=pass+1; gp(" T4 recover 2/3 -> quorum re-elects leader 0 PASS\n" as *u8) } else { fail=fail+1; gp(" T4 FAIL\n" as *u8) } 46 47 gp("RESULT pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail) 48 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 49 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 50 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 51 let ctr__dry: *i64 = gv_ctr() 52 ctr__dry[0] = pass 53 ctr__dry[1] = pass + fail 54 let rc__dry: i64 = gv_verdict("HA-CHAOS-GATE" as *u8, ctr__dry, "cluster survives fault injection -- SPOF killed by construction)" as *u8) 55 sys_exit(rc__dry) 56 return rc__dry 57}