code wiki / _hdl_build / nx_torrent_backoff_gate.nx

nx_torrent_backoff_gate.nx source

↩ module page · 176 lines · 9249 B

1// nx_torrent_backoff_gate.nx -- does the seeder stop burning a core when nothing it accepts can be served? 2// 3// ★THE DEFECT THIS PROVES (measured live 2026-07-30). `nx_torrent_seed` held ~71.6% of a core with the 4// signature of a hot accept loop. Two independent faults, one symptom: 5// (1) BOTH accept loops read `if afd >= 0 { ... }` with NO else branch. A persistently failing 6// sys_accept (EMFILE / ENFILE / ECONNABORTED) therefore re-looped with ZERO delay -- an 7// unconditional spin at whatever rate the CPU allows. 8// (2) The forked child called `sys_exit(0)` UNCONDITIONALLY, so the parent could not observe that 9// `ts_serve_peer_registry` was returning -2 every single time. /tmp/seed.log showed 10// `served blocks=-2` repeating without end while the parent forked again immediately, forever. 11// ★A LOOP THAT CANNOT SEE ITS OWN FAILURES CANNOT BACK OFF FROM THEM. The exit code was the missing wire. 12// 13// ★WHY A PURE FUNCTION IS THE UNIT UNDER TEST. "every peer handshake fails" is not a host state you can 14// summon on a shared box, so a policy welded into an accept loop beside its own syscalls is untestable in 15// practice -- which is exactly how it survived. Same discipline as `hc_keep_delay` and `ba_verdict`. 16// 17// ★THE TOOTH THAT MATTERS IS T6, the NEG-CONTROL: the pre-fix policy is kept alive as 18// `ts_backoff_ms_old` and asserted to return 0 on the very inputs where the fix backs off. If someone 19// reverts the fix, T2/T3/T4 go green against a spinning loop unless a tooth pins the OLD behaviour to the 20// OLD answer. A gate that cannot fail on the unfixed code proves nothing about the fixed code. 21// 22// usage: nx_torrent_backoff_gate (CWD = the store root) 23// exit 0 = GREEN · 1 = RED 24// license_tier: ORIGINAL expect_exit: 0 25import "nx_syscalls.nx" 26import "nx_torrent_seed.nx" 27import "nx_artifact_root.nx" 28 29const TB_SRC: *u8 = "runtime/nx_torrent_seed.nx" 30const TB_CAP: i64 = 2097152 31 32func tb_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 33func tb_p(s: *u8) -> i64 { sys_write(1, s, tb_len(s)); return 0 } 34func tb_pn(v: i64) -> i64 { 35 let t: *u8=sys_mmap(32); var m: i64=v; var k: i64=0 36 if m<0 { sys_write(1,"-" as *u8,1); m=0-m } 37 if m==0 { t[0]=48 as u8; k=1 } 38 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } 39 let o: *u8=sys_mmap(32); var i: i64=0 40 while i<k { o[i]=t[k-1-i]; i=i+1 } 41 sys_write(1,o,k); return 0 42} 43func tb_ck(name: *u8, c: i64) -> i64 { 44 if c==1 { tb_p(" PASS " as *u8) } else { tb_p(" FAIL " as *u8) } 45 tb_p(name); tb_p("\n" as *u8); return c 46} 47func tb_read(path: *u8, buf: *u8) -> i64 { 48 let fd: i64 = sys_openat_rd(path) 49 if fd < 0 { return 0 - 1 } 50 var tot: i64 = 0 51 var go: i64 = 1 52 while go == 1 { 53 let rem: i64 = TB_CAP - tot 54 if rem <= 0 { go = 0 } 55 if go == 1 { 56 let r: i64 = sys_read(fd, ((buf as i64)+tot) as *u8, rem) 57 if r <= 0 { go = 0 } else { tot = tot + r } 58 } 59 } 60 sys_close(fd) 61 return tot 62} 63func tb_find(buf: *u8, n: i64, pat: *u8) -> i64 { 64 let pl: i64 = tb_len(pat) 65 if pl <= 0 { return 0 } 66 var i: i64 = 0 67 while i + pl <= n { 68 var k: i64 = 0 69 var hit: i64 = 1 70 while k < pl { if buf[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 71 if hit == 1 { return 1 } 72 i = i + 1 73 } 74 return 0 75} 76 77func main() -> i64 { 78 var pass: i64 = 0 79 var total: i64 = 0 80 tb_p("=== nx_torrent_backoff_gate -- does the seeder back off when every serve is failing? ===\n" as *u8) 81 82 // T1: healthy is FULL SPEED. A backoff that taxes the working case would be traded away in a week. 83 var t1: i64 = 0 84 if ts_backoff_ms(0) == 0 { t1 = 1 } 85 pass = pass + tb_ck("T1 zero failures -> 0ms (a healthy seeder is never slowed)" as *u8, t1); total = total + 1 86 87 // T2: a burst of aborts is TOLERATED. Real peers drop mid-handshake; punishing that would be a 88 // regression dressed as a fix. 89 var t2: i64 = 0 90 if ts_backoff_ms(TS_BK_TOL - 1) == 0 { t2 = 1 } 91 pass = pass + tb_ck("T2 7 consecutive failures -> still 0ms (real peers abort; tolerate the burst)" as *u8, t2); total = total + 1 92 93 // T3: the fix -- sustained total failure engages the brake. 94 var t3: i64 = 0 95 if ts_backoff_ms(TS_BK_TOL) == TS_BK_D1 { t3 = 1 } 96 pass = pass + tb_ck("T3 8 consecutive failures -> 50ms (the spin is broken)" as *u8, t3); total = total + 1 97 98 // T4/T5: it ESCALATES, and it CAPS. Never unbounded -- a seeder that sleeps for an hour is dead. 99 var t4: i64 = 0 100 if ts_backoff_ms(TS_BK_N2) == TS_BK_D2 { t4 = 1 } 101 pass = pass + tb_ck("T4 64 failures -> 250ms (escalates with severity)" as *u8, t4); total = total + 1 102 var t5: i64 = 0 103 if ts_backoff_ms(TS_BK_N3) == TS_BK_D3 { if ts_backoff_ms(1000000) == TS_BK_D3 { t5 = 1 } } 104 pass = pass + tb_ck("T5 512 and 1,000,000 failures -> both 1000ms (CAPPED; still a live seeder)" as *u8, t5); total = total + 1 105 106 // T6 ★NEG-CONTROL / LIAR-KILLER: the OLD policy returns 0 on the SAME inputs where the new one backs 107 // off. This is the tooth that fails if the fix is reverted. 108 var t6: i64 = 0 109 if ts_backoff_ms_old(TS_BK_TOL) == 0 { if ts_backoff_ms_old(TS_BK_N3) == 0 { t6 = 1 } } 110 pass = pass + tb_ck("T6 NEG-CONTROL: pre-fix policy returns 0ms at 8 AND 512 failures (proves the spin was real)" as *u8, t6); total = total + 1 111 112 // T7 ★MONOTONIC: delay must never DECREASE as failures mount. A non-monotonic backoff is a bug that 113 // only shows up under the exact load it was written for. 114 var t7: i64 = 1 115 var prev: i64 = 0 116 var i: i64 = 0 117 while i < 1200 { 118 let cur: i64 = ts_backoff_ms(i) 119 if cur < prev { t7 = 0 } 120 prev = cur 121 i = i + 1 122 } 123 pass = pass + tb_ck("T7 delay is monotonic non-decreasing across 0..1199 failures" as *u8, t7); total = total + 1 124 125 // ---- SOURCE-BACKED TEETH ---- 126 // Each distinct failure gets its OWN tooth: unreadable source (T8a) is never reported as drift (T8) or 127 // as a broken scanner (T9). A neg-control that shares a failure guard with the test it controls cannot 128 // discriminate -- learned the hard way on nx_hostctl_keepbackoff_gate the same day. 129 let buf: *u8 = sys_mmap(TB_CAP) 130 let srcp: *u8 = sys_mmap(512) 131 let located: i64 = ar_resolve(TB_SRC, srcp) 132 var n: i64 = 0 133 if located == 1 { n = tb_read(srcp, buf) } 134 tb_p(" source: " as *u8); tb_p(srcp); tb_p(" bytes=" as *u8); tb_pn(n); tb_p("\n" as *u8) 135 var t8a: i64 = 0 136 if n > 0 { t8a = 1 } 137 pass = pass + tb_ck("T8a seeder source LOCATED and non-empty (an unreadable file is its own defect)" as *u8, t8a); total = total + 1 138 139 if t8a == 1 { 140 // T8: the accept loops must actually CONSULT the policy, and the child must carry its outcome in 141 // the exit code. A pure function nothing calls is decoration. 142 var ok: i64 = 1 143 if tb_find(buf, n, "ts_backoff_ms(consec)" as *u8) == 0 { ok = 0 } 144 if tb_find(buf, n, "if afd < 0 { consec = consec + 1 }" as *u8) == 0 { ok = 0 } 145 // ★`n <= 0`, NOT `n < 0` -- and this is the tooth that MEASUREMENT changed. nx_seedchurn_gate read 146 // the live log: useful=11 permille, zero_block=296, failed=691. Because the child exited 0 for any 147 // n >= 0, every zero-block serve RESET the consecutive-failure counter, so with ~30% zero-block 148 // serves interleaved among ~69% failures the counter could never reach 8 and THE BRAKE COULD NEVER 149 // FIRE. A completed handshake that moves no data is not a success. One real serve (blocks>0) still 150 // resets to full speed, so a healthy seeder is never slowed. 151 if tb_find(buf, n, "if n <= 0 { sys_exit(1) }" as *u8) == 0 { ok = 0 } 152 pass = pass + tb_ck("T8 both loops call ts_backoff_ms, branch on afd<0, and the child treats n<=0 (NOT n<0) as failure" as *u8, ok); total = total + 1 153 154 // ★T8b GUARDS THE REGRESSION DIRECTLY. `n < 0` is the exact pre-measurement form; if it ever comes 155 // back, the brake silently stops working again with every other tooth still green. Naming the dead 156 // form is cheaper than re-deriving why the fix stopped mattering. 157 var t8b: i64 = 0 158 if tb_find(buf, n, "if n < 0 { sys_exit(1) }" as *u8) == 0 { t8b = 1 } 159 pass = pass + tb_ck("T8b the superseded `n < 0` exit condition is GONE (it is what made the brake unfireable)" as *u8, t8b); total = total + 1 160 161 var t9: i64 = 0 162 if tb_find(buf, n, "zzz_not_in_torrent_seed_zzz" as *u8) == 0 { t9 = 1 } 163 pass = pass + tb_ck("T9 NEG-CONTROL: source scanner does not report a string that is absent" as *u8, t9); total = total + 1 164 } else { 165 tb_p(" INCONCLUSIVE T8 (adoption) and T9 (scanner sanity): not answerable without the source.\n" as *u8) 166 } 167 168 tb_p("---- nx_torrent_backoff_gate " as *u8); tb_pn(pass); tb_p(" / " as *u8); tb_pn(total); tb_p(" ----\n" as *u8) 169 if pass == total { 170 tb_p("VERDICT: verdict=GREEN (the seeder backs off under total failure, tolerates real aborts, caps, and self-heals)\n" as *u8) 171 sys_exit(0); return 0 172 } 173 tb_p("VERDICT: verdict=RED\n" as *u8) 174 sys_exit(1) 175 return 1 176}