code wiki / (root) / nx_hotlisten_gate.nx

nx_hotlisten_gate.nx source

↩ module page · 69 lines · 3722 B

1// nx_hotlisten_gate.nx -- R5 proof: SO_REUSEPORT is what makes a ZERO-DOWNTIME handoff possible. 2// license_tier: ORIGINAL 3// THE CLASS UNDER TEST: today every /api/deploy and /api/restart drops the in-flight request (FETCH-FAIL, 4// ~10x in one session) because the daemon is KILLED while answering, and we wrote DOCTRINE around that 5// ("503 = EXPECTED, do NOT retry-hammer") -- a doctrine patch over a missing mechanism. A second process 6// cannot take the port until the first is gone, so there is always a gap. SO_REUSEPORT removes the gap. 7// T1 is the NON-VACUITY CONTROL and it is the whole point: it proves the SECOND bind genuinely FAILS on the 8// standard listener. Without T1, T2 succeeding would prove nothing -- it could just mean the port was free. 9// A cure with no proven disease is not a proof. 10import "nx_http_server.nx" 11import "nx_gate.nx" 12 13const HL_PORT_A: i64 = 19731 // control: standard listener, second bind must FAIL 14const HL_PORT_B: i64 = 19732 // cure: hot listener, second bind must SUCCEED 15 16func main() -> i64 { 17 gw("=== nx_hotlisten_gate: SO_REUSEPORT enables the zero-downtime handoff (R5) ===\n" as *u8) 18 var pass: i64 = 0 19 var tot: i64 = 0 20 let v: *i64 = sys_mmap(16) as *i64 21 22 // T1 CONTROL / NON-VACUITY: standard listener -- the SECOND bind on the same port must be REFUSED. 23 let a1: *u8 = sys_mmap(32) 24 nx_http_server_addr_loopback(a1, HL_PORT_A) 25 let f1: i64 = nx_http_server_listen(a1, 8, v) 26 let a2: *u8 = sys_mmap(32) 27 nx_http_server_addr_loopback(a2, HL_PORT_A) 28 let f2: i64 = nx_http_server_listen(a2, 8, v) 29 tot = tot + 1 30 var t1: i64 = 0 31 if f1 >= 0 { if f2 < 0 { t1 = 1 } } 32 if t1 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 33 gw("T1 CONTROL: standard listener REFUSES a second bind (this is the outage gap: first=" as *u8) 34 gn(f1); gw(" second=" as *u8); gn(f2); gw(")\n" as *u8) 35 if f1 >= 0 { sys_close(f1) } 36 if f2 >= 0 { sys_close(f2) } 37 38 // T2 CURE: hot listener -- BOTH processes can hold the port, which IS the handoff window. 39 let b1: *u8 = sys_mmap(32) 40 nx_http_server_addr_loopback(b1, HL_PORT_B) 41 let g1: i64 = nx_http_server_listen_hot(b1, 8, v) 42 let b2: *u8 = sys_mmap(32) 43 nx_http_server_addr_loopback(b2, HL_PORT_B) 44 let g2: i64 = nx_http_server_listen_hot(b2, 8, v) 45 tot = tot + 1 46 var t2: i64 = 0 47 if g1 >= 0 { if g2 >= 0 { t2 = 1 } } 48 if t2 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 49 gw("T2 CURE: hot listener admits a SECOND live binder = the drain window (old=" as *u8) 50 gn(g1); gw(" new=" as *u8); gn(g2); gw(")\n" as *u8) 51 if g1 >= 0 { sys_close(g1) } 52 if g2 >= 0 { sys_close(g2) } 53 54 // T3: the hot listener is still a WORKING listener, not merely a socket that binds. Same port, after both 55 // closed, must accept a fresh listen -- proving we did not leave the port poisoned for the next start. 56 let c1: *u8 = sys_mmap(32) 57 nx_http_server_addr_loopback(c1, HL_PORT_B) 58 let h1: i64 = nx_http_server_listen_hot(c1, 8, v) 59 tot = tot + 1 60 var t3: i64 = 0 61 if h1 >= 0 { t3 = 1 } 62 if t3 == 1 { pass = pass + 1; gw(" [PASS] " as *u8) } else { gw(" [FAIL] " as *u8) } 63 gw("T3 the port is not poisoned: a fresh hot listen after handoff succeeds (fd=" as *u8); gn(h1); gw(")\n" as *u8) 64 if h1 >= 0 { sys_close(h1) } 65 66 gw("\n=== nx_hotlisten_gate " as *u8); gn(pass); gw("/" as *u8); gn(tot); gw(" ===\n" as *u8) 67 if pass == tot { gw("HOTLISTEN GREEN -- a new process CAN take the port while the old still serves; the 503 gap is closable\n" as *u8); sys_exit(0); return 0 } 68 gw("HOTLISTEN RED\n" as *u8); sys_exit(1); return 1 69}