code wiki / (root) / nx_coindex_bench_gate.nx

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}