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}