code wiki / _hdl_build / nx_web_shard_compact.nx
nx_web_shard_compact.nx
buildroot/runtime/_hdl_build/nx_web_shard_compact.nx
about
nx_web_shard_compact.nx -- SCALE compaction for big multi-segment shards (the web shard's rung).
ss_compact/ss_compact_cap have EXACT semantics (last entry per key wins, tombstones kept, retired
segments archived then an atomic manifest swap) but their key dedup is a LINEAR table scan =
O(entries^2): fine at 4k keys, unusable at the web shard's ~1.4M entries (bulk docs + pr: priors).
This organ is the same merge with an open-addressing HASH dedup = O(entries), plus a post-swap
verification pass. Semantics kept byte-equal to ss_compact: chronological walk, last wins, ALL kinds
carried (incl. kind-2 tombstones), archive-before-swap, every failure path returns BEFORE the swap.
BYTE-BUDGETED SUFFIX FOLD (2026-07-29): an unbounded whole-shard merge materializes the merged
segment in anonymous RAM (the writer buffer, plus aux blobs and key tables that all scale with
merged bytes) -- the OOM killer reaped it twice at bytes-in=1883885124 on 2026-07-25, mid-merge,
before the swap (never-brick held; shard untouched). The merge therefore now folds the LONGEST
NEWEST contiguous manifest suffix whose .docs bytes fit a budget derived from the box itself
(MemAvailable/WSC_BUDGET_SHARE; argv[2] in MB overrides). Replacing a contiguous suffix with its
fold preserves last-wins semantics EXACTLY: the run is merged in chronological order, kind-2
tombstones are carried, and older (kept) segments remain shadowed by the merged tail precisely as
they were by the unmerged tail. Repeated runs converge the shard LSM-style with bounded memory;
a streaming (file-backed) writer that lifts the budget entirely is the named next rung.
usage: nx_web_shard_compact <domain> [budget-MB]
license_tier: ORIGINAL
dependencies 1 imports · 0 importers
imports: nx_docportal_search_seg.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 22 | const WSC_MAGIC_1125899906842597: i64 = 1125899906842597 |
| 23 | const WSC_MAGIC_1048576: i64 = 1048576 |
| 24 | const WSC_MAGIC_65536: i64 = 65536 |
| 26 | const WSC_MAXSEGS: i64 = 8192 // manifest lines we can fold in one run |
| 27 | const WSC_MAXENT: i64 = 134217728 // hard sanity ceiling on entries (2^27; ~an order past any near plan) |
| 28 | const WSC_HMUL: i64 = 7046029254386353131 // odd 63-bit multiplicative-mix constant (same family as pagerank_build) |
| 29 | const WSC_BUDGET_SHARE: i64 = 32 // /32 (was /16) 2026-07-30: the /16 reasoning below is RIGHT about the 9-10x amplification but it reserves a share of MemAvailable AT START on a box with OTHER GROWING CONSUMERS (torrent stack, jellyfin, syno services) -- the share is stale the moment it is computed, and it assumes this job is the only claimant. MEASURED TODAY: with ~25GB available at start the /16 budget projected a peak of ~15GB and this organ was caught at 21GB RSS with 536MB free, minutes from OOM, taking mgmt + the tools daemon down with it. /32 projects ~7-8GB, which also keeps it clear of the 16GiB RLIMIT_AS ceiling now imposed by hc_spawn_searchpipe_job -- TWO CONTROLS THAT CONTRADICT EACH OTHER ARE ONE CONTROL AND ONE OUTAGE, so the soft budget must project a peak BELOW the hard cap. Original note, still accurate on the amplification: fold budget = MemAvailable/16. MEASURED 2026-07-29 (death #3, OOM in ss_build_terms): a 1.88GB run took MemAvailable from 19.4GB to 1.2GB before the kill => PEAK TOUCH ~9-10x run bytes (occ arrays + pairs + writer + key tables + input page cache), not the ~6x the seq1048 memo estimated. /16 caps projected peak at ~60% of MemAvailable so the box keeps serving |
| 30 | const WSC_BUDGET_FALLBACK: i64 = 268435456 // 256MB: used ONLY when /proc/meminfo is unreadable -- conservative enough to be safe on any box that boots this stack, keeps the pinned no-arg hostctl sub from wedging into a permanent refuse |
| 31 | const WSC_BUDGET_FLOOR: i64 = 67108864 // 64MB: below this the box is too tight to merge anything safely -> refuse loudly rather than thrash |
| 32 | const WSC_LOCK_EX: i64 = 2 // flock LOCK_EX on <prefix>plock -- the plane-lock discipline segguard + nx_seg_compact_cli already honor; a lockless manifest swap under a concurrent fold LOSES rows (the proven seq349 race) |
| 33 | const WSC_MEMINFO_BUF: i64 = 8192 // scratch size for the bounded /proc/meminfo read (file is ~1.5KB) |
| 34 | const WSC_MEMINFO_CAP: i64 = 7900 // read cap under the scratch -- headroom below WSC_MEMINFO_BUF |
| 35 | const WSC_KB: i64 = 1024 // /proc/meminfo reports MemAvailable in kB |
functions
| 37 | func wc9_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } called by 1: main |
| 38 | func wc9_num(v: i64) -> i64 called by 1: main |
| 49 | func wsc_slot(k: *u8, kl: i64, hshift: i64, hmask: i64) -> i64 called by 1: main |
| 61 | func wsc_memavail() -> i64 |
| 100 | func main(argc: i64, argv: *i64) -> i64 |