code wiki / (root) / nx_seg_compact_cli.nx

nx_seg_compact_cli.nx source

↩ module page · 74 lines · 4534 B

1// nx_seg_compact_cli.nx -- run seg-store COMPACTION for one shard prefix. Folds all live segments into ONE 2// merged segment (latest entry per key; tombstones kept), archives the retired segment names (additive -- 3// never deletes), atomically swaps the live manifest. This is the PERF fix for the data-driven-search debt: 4// after fixing the 256-segment read cap, search loads ALL live segments per query -- compaction folds a 5// large uncompacted shard (e.g. 936 segs) into ~1 so per-query load stays cheap. Uses the FIXED (data-driven, 6// no 256/260 cap) ss_compact. Runs in nishihost CWD (the dp-<domain>-pub- store resolves there). 7// usage: nx_seg_compact_cli <store-prefix> <new-segid> 8// e.g. nx_seg_compact_cli knowledge/store/dp-nishifamily.com-pub- 9000000000000000001 9// license_tier: ORIGINAL 10import "nx_syscalls.nx" 11import "nx_seg_store.nx" 12import "nx_heavyio_lib.nx" // the estate-wide heavy-I/O concurrency bound (2026-09-02): ask BEFORE bulk I/O 13 14func scc_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 15func scc_putn(v: i64) -> i64 { 16 let t: *u8 = sys_mmap(32); var m: i64 = v; var k: i64 = 0 17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 18 if m == 0 { t[0] = 48 as u8; k = 1 } 19 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 20 let b: *u8 = sys_mmap(32); var i: i64 = 0 21 while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 22 sys_write(1, b, k); return 0 23} 24func scc_atoi(s: *u8) -> i64 { 25 var v: i64 = 0; var i: i64 = 0; var go: i64 = 1 26 while go == 1 { let c: i64 = s[i] as i64; if c < 48 { go = 0 } else { if c > 57 { go = 0 } else { v = v * 10 + (c - 48); i = i + 1 } } } 27 return v 28} 29 30func main(argc: i64, argv: *i64) -> i64 { 31 if argc < 3 { scc_puts("usage: nx_seg_compact_cli <store-prefix> <new-segid>\n" as *u8); return 2 } 32 let prefix: *u8 = argv[1] as *u8 33 let segid: i64 = scc_atoi(argv[2] as *u8) 34 // HEAVY-I/O BOUND (2026-09-02): ask the estate-wide concurrency bound BEFORE taking the plane lock, so a 35 // deferred run holds nothing. DEFER exits 4 (the estate's refused-admission code): the segguard beat re-fires. 36 // UNOBSERVABLE proceeds, ANNOUNCED -- a missing conf must never silence compaction; the clock's own storm 37 // check still guards the D-state line. 38 let hio: *i64 = sys_mmap(32) as *i64 39 let hv: i64 = hio_admit(hio) 40 hio_announce(1, hv, hio) 41 if hv == HIO_DEFER { scc_puts("COMPACT DEFERRED: heavy-I/O bound reached; the beat re-fires\n" as *u8); return 4 } 42 // serialize against plane writers via the advisory <prefix>plock -- the SAME lock nx_debt/ 43 // nx_store_put/nx_frontier_put/nx_store_compact take. PROVEN NEEDED 2026-07-20: the 600s 44 // nx_segguard beat ran this CLI LOCKLESS and its atomic manifest swap clobbered a concurrent 45 // writer's manifest append (one committed segment vanished from the live view; .docs stayed 46 // on disk). segguard's before-list-vs-archive repair structurally cannot see an append that 47 // lands AFTER its manifest read, so mutual exclusion here is the only correct fix. 48 // Fail-closed: no lock -> no compaction (a skipped sweep is safe; a raced swap is not). 49 let CLI_LOCK_EX: i64 = 2 50 let CLI_LOCK_MODE: i64 = 0x1a4 51 let lkp: *u8 = sys_mmap(512) 52 var lo: i64 = 0 53 lo = ss_cat(lkp, lo, prefix) 54 lo = ss_cat(lkp, lo, "plock" as *u8) 55 lkp[lo] = 0 as u8 56 let lfd: i64 = sys_openat_append(lkp, CLI_LOCK_MODE) 57 if lfd < 0 { scc_puts("REFUSED: cannot open plane lock\n" as *u8); return 3 } 58 if sys_flock(lfd, CLI_LOCK_EX) != 0 { scc_puts("REFUSED: cannot take plane lock\n" as *u8); return 3 } 59 // lock held for the whole read->merge->swap; released on process exit 60 // count live segments BEFORE 61 let sp: *i64 = sys_mmap(8) as *i64 62 let before: i64 = ss_manifest_dyn(prefix, sp) 63 scc_puts("COMPACT prefix=" as *u8); scc_puts(prefix) 64 scc_puts(" live-segs-before=" as *u8); scc_putn(before) 65 scc_puts(" new-segid=" as *u8); scc_putn(segid); scc_puts("\n" as *u8) 66 let r: i64 = ss_compact(prefix, segid) 67 // count live segments AFTER 68 let sp2: *i64 = sys_mmap(8) as *i64 69 let after: i64 = ss_manifest_dyn(prefix, sp2) 70 scc_puts("RESULT ss_compact=" as *u8); scc_putn(r) 71 scc_puts(" live-segs-after=" as *u8); scc_putn(after) 72 if r > 0 { scc_puts(" OK (retired segs archived to manifest-archive.txt, NOT deleted)\n" as *u8) } else { scc_puts(" FAIL (manifest unchanged -- fail-closed)\n" as *u8) } 73 return 0 74}