nx_coindex_bench_gate.nx source
↩ module page · 162 lines · 7199 B
1// nx_coindex_bench_gate.nx -- MEASURED concurrent-writer benchmark for the conflict-free co-index
2// (R-ORCH-2). Exists because the coordinator CAUGHT ITSELF crediting nx_coindex with the WMS's R11
3// measurement it never individually earned (the exact self-grading the triage v2 forbids). This
4// gate makes it earn a number: 8 FORKED writer processes x 12 appends each, through BOTH paths --
5// GOOD = ci_append (flock + O_APPEND single-write): must land 96/96, ZERO lost, timed.
6// BAD = the naive read-modify-write a hand-edited shared file degrades to (read whole -> append in
7// memory -> truncate-write whole, no lock, deliberate window): must MEASURABLY lose entries
8// -- the RUN comparator proving the failure mode is real physics, not hypothesis.
9// git = the named external yardstick, STATED by-design (index.lock: concurrent committers
10// serialize-or-FAIL and must retry; the naive path loses SILENTLY; coindex = all land, no retry).
11// GREEN iff good_lost==0 AND bad_lost>0 (the neg-control must fire). Journals the measured line.
12// license_tier: ORIGINAL expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_coindex_core.nx"
15
16func b_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
17func b_putn(v: i64) -> i64 { nxi_out(v); return 0 }
18
19const NW: i64 = 8 // workers
20const NR: i64 = 12 // appends per worker
21
22// GOOD child: NR coindex appends, slug "w<i>", entry carries seq -- last-write-wins target = seq NR-1
23func b_good_child(w: i64) -> i64 {
24 let slug: *u8 = sys_mmap(32)
25 var o: i64 = 0
26 o = ccz_cat_str(slug, o, "w" as *u8)
27 o = ccz_cat_num(slug, o, w)
28 let entry: *u8 = sys_mmap(128)
29 var j: i64 = 0
30 while j < NR {
31 var e: i64 = 0
32 e = ccz_cat_str(entry, e, "worker " as *u8)
33 e = ccz_cat_num(entry, e, w)
34 e = ccz_cat_str(entry, e, " seq " as *u8)
35 e = ccz_cat_num(entry, e, j)
36 ci_append("/tmp/cib_good.journal" as *u8, "/tmp/cib_good.journal.lock" as *u8, slug, entry)
37 sys_sleep_ms(1)
38 j = j + 1
39 }
40 return 0
41}
42
43// BAD child: naive RMW on a shared file, NO lock, 2ms window between read and truncate-write --
44// exactly what concurrent hand-editing of one index file is, mechanically.
45func b_bad_child(w: i64) -> i64 {
46 let buf: *u8 = sys_mmap(262144)
47 var j: i64 = 0
48 while j < NR {
49 var n: i64 = ccz_read("/tmp/cib_bad.txt" as *u8, buf, 262143)
50 if n < 0 { n = 0 }
51 var o: i64 = n
52 o = ccz_cat_str(buf, o, "b" as *u8)
53 o = ccz_cat_num(buf, o, w)
54 o = ccz_cat_str(buf, o, "s" as *u8)
55 o = ccz_cat_num(buf, o, j)
56 o = ccz_cat_str(buf, o, "\n" as *u8)
57 sys_sleep_ms(2) // the race window
58 let fd: i64 = sys_openat_wr("/tmp/cib_bad.txt" as *u8, 420) // truncate + rewrite whole
59 if fd >= 0 { sys_write(fd, buf, o); sys_close(fd) }
60 j = j + 1
61 }
62 return 0
63}
64
65// fork NW workers running `which` (0=good,1=bad), reap all.
66func b_run_fleet(which: i64) -> i64 {
67 var w: i64 = 0
68 while w < NW {
69 let pid: i64 = sys_fork()
70 if pid == 0 {
71 if which == 0 { b_good_child(w) } else { b_bad_child(w) }
72 sys_exit(0)
73 }
74 w = w + 1
75 }
76 let st: *i64 = sys_mmap(16) as *i64
77 var reaped: i64 = 0
78 while reaped < NW { if sys_wait4(0 - 1, st, 0) > 0 { reaped = reaped + 1 } }
79 return 0
80}
81
82func main(argc: i64, argv: *i64) -> i64 {
83 let pass: *i64 = sys_mmap(16) as *i64
84 pass[0] = 0
85 // fresh fixtures
86 var z: i64 = sys_openat_wr("/tmp/cib_good.journal" as *u8, 420)
87 if z >= 0 { sys_close(z) }
88 z = sys_openat_wr("/tmp/cib_bad.txt" as *u8, 420)
89 if z >= 0 { sys_close(z) }
90
91 // GOOD: coindex path, timed
92 let t0: i64 = sys_now_ms()
93 b_run_fleet(0)
94 let t1: i64 = sys_now_ms()
95 let good_total: i64 = ci_count("/tmp/cib_good.journal" as *u8)
96 let good_lost: i64 = NW * NR - good_total
97 b_puts("T good-zero-lost total=" as *u8); b_putn(good_total); b_puts("/96 lost=" as *u8); b_putn(good_lost)
98 if good_lost == 0 { b_puts(" PASS\n" as *u8); pass[0] = pass[0] + 1 } else { b_puts(" FAIL\n" as *u8) }
99
100 // GOOD derived: 8 slugs, each at its LAST seq (11)
101 let out: *u8 = sys_mmap(65536)
102 ci_emit("/tmp/cib_good.journal" as *u8, out, 65536)
103 var lines: i64 = 0
104 var i: i64 = 0
105 let on: i64 = ccz_slen(out)
106 while i < on { if out[i] == (10 as u8) { lines = lines + 1 } i = i + 1 }
107 b_puts("T good-derive-8-slugs got=" as *u8); b_putn(lines)
108 if lines == NW { b_puts(" PASS\n" as *u8); pass[0] = pass[0] + 1 } else { b_puts(" FAIL\n" as *u8) }
109 // every derived line must carry the FINAL seq (last-write-wins under real concurrency)
110 let want: *u8 = " seq 11" as *u8
111 var seqok: i64 = 1
112 var ls: i64 = 0
113 while ls < on {
114 var le: i64 = ls
115 var go: i64 = 1
116 while go == 1 { go = 0; if le < on { if out[le] != (10 as u8) { le = le + 1; go = 1 } } }
117 if le > ls {
118 var found: i64 = 0
119 var k: i64 = ls
120 let wl: i64 = ccz_slen(want)
121 while k + wl <= le { var m: i64 = 0; var ok: i64 = 1; while m < wl { if out[k+m] != want[m] { ok = 0; m = wl } m = m + 1 } if ok == 1 { found = 1; k = le } else { k = k + 1 } }
122 if found == 0 { seqok = 0 }
123 }
124 ls = le + 1
125 }
126 b_puts("T good-last-write-wins-all got=" as *u8); b_putn(seqok)
127 if seqok == 1 { b_puts(" PASS\n" as *u8); pass[0] = pass[0] + 1 } else { b_puts(" FAIL\n" as *u8) }
128
129 // BAD: naive RMW comparator -- must LOSE (the run neg-control)
130 b_run_fleet(1)
131 let bad_buf: *u8 = sys_mmap(262144)
132 let bn: i64 = ccz_read("/tmp/cib_bad.txt" as *u8, bad_buf, 262143)
133 var bad_total: i64 = 0
134 i = 0
135 while i < bn { if bad_buf[i] == (10 as u8) { bad_total = bad_total + 1 } i = i + 1 }
136 let bad_lost: i64 = NW * NR - bad_total
137 b_puts("T negctl-naive-RMW-loses total=" as *u8); b_putn(bad_total); b_puts("/96 lost=" as *u8); b_putn(bad_lost)
138 if bad_lost > 0 { b_puts(" PASS\n" as *u8); pass[0] = pass[0] + 1 } else { b_puts(" FAIL\n" as *u8) }
139
140 // journal the MEASURED line
141 let line: *u8 = sys_mmap(1024)
142 var o: i64 = 0
143 o = ccz_cat_str(line, o, "COINDEX-BENCH workers=8 rounds=12 good=" as *u8)
144 o = ccz_cat_num(line, o, good_total)
145 o = ccz_cat_str(line, o, "/96 good_lost=" as *u8)
146 o = ccz_cat_num(line, o, good_lost)
147 o = ccz_cat_str(line, o, " naive=" as *u8)
148 o = ccz_cat_num(line, o, bad_total)
149 o = ccz_cat_str(line, o, "/96 naive_lost=" as *u8)
150 o = ccz_cat_num(line, o, bad_lost)
151 o = ccz_cat_str(line, o, " good_ms=" as *u8)
152 o = ccz_cat_num(line, o, t1 - t0)
153 o = ccz_cat_str(line, o, " git=serialize-or-fail(index.lock, stated-by-design)\n" as *u8)
154 sys_write(1, line, o)
155 let lfd: i64 = sys_openat_append("knowledge/status/coindex_bench.log" as *u8, 420)
156 if lfd >= 0 { sys_write(lfd, line, o); sys_close(lfd) }
157
158 b_puts("CIB-GATE pass=" as *u8); b_putn(pass[0]); b_puts("/4 verdict=" as *u8)
159 if pass[0] == 4 { b_puts("GREEN\n" as *u8); return 0 }
160 b_puts("RED\n" as *u8)
161 return 1
162}