nx_segstore_uncap_gate.nx source
↩ module page · 128 lines · 6548 B
1// nx_segstore_uncap_gate.nx -- ISOLATED proof that the seg_store legacy-cap class is EATEN at the organ:
2// (1) ss_next_segid = canonical UNCAPPED writer segid (max+1) -- no clobber past the legacy cap;
3// (2) ss_manifest_dyn sees the WHOLE store while legacy ss_manifest stays byte-identical (capped) --
4// the discriminating pair proving old callers keep exact behavior (API contract stability);
5// (3) the ss_scan_seglist version WINDOW slides (oldest out, latest kept): a high-churn key (every
6// registry's __idx__ gains one version per put) now reads back its LATEST version past the window --
7// pre-fix ss_get returned the stale FIRST-window version (this is the "writes claim-OK but don't
8// surface" symptom class);
9// (4) ss_compact with a FRESH segid folds the >cap store to ONE segment with nothing lost.
10// Fresh /tmp store (never a shared registry). Runner must clean /tmp/ssuncap first. Exit 0 only on all-PASS.
11// license_tier: ORIGINAL expect_exit: 0
12import "nx_syscalls.nx"
13import "nx_seg_store.nx"
14
15const GATE_N: i64 = 300 // commits to drive -- chosen ABOVE the legacy cap so the tail exercises it
16const GATE_LEGACY_CAP: i64 = 256 // the legacy ss_manifest cap (must STILL cap -- byte-identical old API)
17const GATE_SEG_SLOTS: i64 = 260 // legacy caller buffer sizing convention (slots)
18const GATE_CHECKS: i64 = 9 // number of PASS conditions below
19const KEY_CH: i64 = 107 // 'k' record-key prefix
20const VAL_CH: i64 = 118 // 'v' record-value prefix
21const HOTV_CH: i64 = 104 // 'h' hot-key value prefix
22const ASCII_0: i64 = 48
23const DIR_MODE: i64 = 0x1ed // 0755
24
25func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
26func g_putn(v: i64) -> i64 {
27 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
28 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
29 let d: *u8 = sys_mmap(24); var k: i64 = 0
30 while m > 0 { d[k] = (ASCII_0 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 let o: *u8 = sys_mmap(24); var i: i64 = 0
32 while i < k { o[i] = d[k-1-i]; i = i + 1 } sys_write(1, o, k)
33 return 0
34}
35// build <c><i> (e.g. "k42") into buf, NUL-terminated; return length
36func g_id(buf: *u8, c: i64, i: i64) -> i64 {
37 buf[0] = c as u8
38 var o: i64 = 1; var m: i64 = i
39 if m == 0 { buf[o] = ASCII_0 as u8; o = o + 1; buf[o] = 0 as u8; return o }
40 let t: *u8 = sys_mmap(24); var k: i64 = 0
41 while m > 0 { t[k] = (ASCII_0 + (m % 10)) as u8; m = m / 10; k = k + 1 }
42 while k > 0 { buf[o] = t[k-1]; o = o + 1; k = k - 1 }
43 buf[o] = 0 as u8
44 return o
45}
46// 1 if ss_get(prefix,key) returns FOUND with bytes exactly equal to want[0..wlen)
47func g_get_eq(prefix: *u8, key: *u8, want: *u8, wlen: i64) -> i64 {
48 let po: *i64 = sys_mmap(16) as *i64
49 let lo: *i64 = sys_mmap(16) as *i64
50 if ss_get(prefix, key, po, lo) != 1 { return 0 }
51 if lo[0] != wlen { return 0 }
52 let src: *u8 = po[0] as *u8
53 var i: i64 = 0
54 while i < wlen { if src[i] != want[i] { return 0 } i = i + 1 }
55 return 1
56}
57func g_check(name: *u8, ok: i64, pass: *i64) -> i64 {
58 g_puts("T " as *u8); g_puts(name); g_puts(" -> " as *u8)
59 if ok == 1 { g_puts("PASS\n" as *u8); pass[0] = pass[0] + 1 } else { g_puts("FAIL\n" as *u8) }
60 return 0
61}
62
63func main(argc: i64, argv: *i64) -> i64 {
64 let pass: *i64 = sys_mmap(16) as *i64
65 pass[0] = 0
66 sys_mkdir("/tmp/ssuncap" as *u8, DIR_MODE)
67 let prefix: *u8 = "/tmp/ssuncap/su-" as *u8
68 let idbuf: *u8 = sys_mmap(64)
69 let valbuf: *u8 = sys_mmap(64)
70 let hotbuf: *u8 = sys_mmap(64)
71
72 // drive GATE_N commits; EVERY commit also advances the hot key (versions = GATE_N > window)
73 var w: i64 = 0
74 while w < GATE_N {
75 g_id(idbuf, KEY_CH, w)
76 let vl: i64 = g_id(valbuf, VAL_CH, w)
77 let hl: i64 = g_id(hotbuf, HOTV_CH, w)
78 let wr: *i64 = ss_begin()
79 ss_add(wr, 1, idbuf, valbuf, vl)
80 ss_add(wr, 1, "__hot__" as *u8, hotbuf, hl)
81 ss_commit(prefix, wr, ss_next_segid(prefix))
82 w = w + 1
83 }
84
85 // T1 canonical writer segid is UNCAPPED
86 g_check("next-segid-uncapped" as *u8, (ss_next_segid(prefix) == GATE_N) as i64, pass)
87
88 // T2 legacy ss_manifest STILL caps exactly (byte-identical old API preserved) -- the discriminator
89 let legacy: *i64 = sys_mmap(8 * GATE_SEG_SLOTS) as *i64
90 g_check("legacy-manifest-still-capped" as *u8, (ss_manifest(prefix, legacy) == GATE_LEGACY_CAP) as i64, pass)
91
92 // T3 dyn manifest sees the WHOLE store
93 let dp: *i64 = sys_mmap(16) as *i64
94 g_check("dyn-manifest-sees-all" as *u8, (ss_manifest_dyn(prefix, dp) == GATE_N) as i64, pass)
95
96 // T4 record PAST the legacy cap reads back exactly
97 g_id(idbuf, KEY_CH, GATE_N - 1)
98 let vl4: i64 = g_id(valbuf, VAL_CH, GATE_N - 1)
99 g_check("tail-record-readback" as *u8, g_get_eq(prefix, idbuf, valbuf, vl4), pass)
100
101 // T5 HOT KEY: latest version wins past the window (pre-fix: stale first-window version) -- THE fix
102 let hl5: i64 = g_id(hotbuf, HOTV_CH, GATE_N - 1)
103 g_check("hot-key-latest-not-stale" as *u8, g_get_eq(prefix, "__hot__" as *u8, hotbuf, hl5), pass)
104
105 // T6 earliest record still intact (slide corrupted nothing)
106 g_id(idbuf, KEY_CH, 0)
107 let vl6: i64 = g_id(valbuf, VAL_CH, 0)
108 g_check("first-record-intact" as *u8, g_get_eq(prefix, idbuf, valbuf, vl6), pass)
109
110 // T7 compact with a FRESH segid folds the whole >cap store...
111 let crc: i64 = ss_compact(prefix, ss_next_segid(prefix))
112 g_check("compact-fresh-segid-ok" as *u8, (crc > 0) as i64, pass)
113 // T8 ...to exactly ONE live segment...
114 g_check("compact-folds-to-one" as *u8, (ss_manifest_dyn(prefix, dp) == 1) as i64, pass)
115 // T9 ...with latest state fully preserved (hot key + tail record).
116 // regenerate the expected value: valbuf was reused for T6 (v0) -- comparing against it was a gate bug.
117 var ok9: i64 = 1
118 if g_get_eq(prefix, "__hot__" as *u8, hotbuf, hl5) != 1 { ok9 = 0; g_puts(" (hot-key lost)\n" as *u8) }
119 g_id(idbuf, KEY_CH, GATE_N - 1)
120 let vl9: i64 = g_id(valbuf, VAL_CH, GATE_N - 1)
121 if g_get_eq(prefix, idbuf, valbuf, vl9) != 1 { ok9 = 0; g_puts(" (tail record lost)\n" as *u8) }
122 g_check("compact-preserves-latest" as *u8, ok9, pass)
123
124 g_puts("SEGSTORE-UNCAP-GATE pass=" as *u8); g_putn(pass[0]); g_puts("/" as *u8); g_putn(GATE_CHECKS); g_puts(" verdict=" as *u8)
125 if pass[0] == GATE_CHECKS { g_puts("GREEN\n" as *u8); return 0 }
126 g_puts("RED\n" as *u8)
127 return 1
128}