code wiki / _hdl_build / nx_pub_recover.nx
nx_pub_recover.nx
buildroot/runtime/_hdl_build/nx_pub_recover.nx
about
nx_pub_recover.nx -- THE PUBLISHER'S DAEMON-RECOVERY rung (ops authority, not ad-hoc workstreams).
Operator law: "the nishi publisher should be managing restarts and all this stuff." So recovery is a
PUBLISHER responsibility, decided from the MONITOR'S signal -- never an ad-hoc workstream kill.
The supervisor (nx_hostctl) already RESPAWNS dead/hung daemons + crash-loop-guards. The GAP it can't see:
a WEDGE -- a daemon that still answers but SLOWLY (the :8791 reader at 20s/request passes the binary
up/down probe). nx_apm_sweep measures real latency -> apm_metrics.tsv. This organ reads that signal,
maps a slow endpoint to its daemon, and emits an IDEMPOTENT + SERIALIZED + LEDGERED recovery REQUEST
(the control-plane consumer proc_kill_by_name's it -> the existing keeper respawns a fresh one).
By construction: never-brick #26 (a recovery REQUEST is reversible metadata; the action is only a
kill+supervised-respawn, never a destructive write); idempotent #10 (a daemon already PENDING is not
re-requested); serialized (fl_acquire, one decision at a time); fail-safe #14 (unmapped/healthy -> no-op).
pr_recover(metricspath, queuepath, wedge_ms) -> number of recovery requests emitted
license_tier: ORIGINAL
dependencies 3 imports · 3 importers
imports: nx_syscalls.nxnx_arbiter.nxnx_framed_append.nx
imported by: nx_hostop.nxnx_pub_recover_exec.nxnx_pub_recover_gate.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 20 | const PR_MAGIC_100000: i64 = 100000 |
| 22 | const PR_WEDGE_MS_DEFAULT: i64 = 5000 |
| 23 | const PR_METRICS: *u8 = "knowledge/status/apm_metrics.tsv" |
| 24 | const PR_QUEUE: *u8 = "knowledge/publish/recovery_queue.tsv" |
| 25 | const PR_LOCK: *u8 = "publish_recover" |
| 26 | const PR_REC_CAP: i64 = 1024 |
functions
| 28 | func pr_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 29 | func pr_uw(fd: i64, s: *u8) -> i64 { sys_write(fd, s, pr_slen(s)); return 0 } |
| 30 | func pr_n(buf: *u8, off: i64, v: i64) -> i64 |
| 39 | func pr_s(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 } return o } |
| 40 | func pr_wn(fd: i64, v: i64) -> i64 { let b: *u8 = sys_mmap(24); let e: i64 = pr_n(b, 0, v); sys_write(fd, b, e); return 0 } |
| 42 | func pr_has(hay: *u8, n: i64, needle: *u8) -> i64 |
| 49 | func pr_field(line: *u8, linelen: i64, idx: i64, out: *u8) -> i64 |
| 59 | func pr_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 } return v } |
| 61 | func pr_streq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 } called by 1: pr_already_pending |
| 64 | func pr_daemon_for(url: *u8, urllen: i64, out: *u8) -> i64 |
| 73 | func pr_already_pending(queuepath: *u8, daemon: *u8) -> i64 |
| 94 | func pr_recover(metricspath: *u8, queuepath: *u8, wedge_ms: i64) -> i64 called by 3: hostop_recovermainmain calls 13: sys_mmapsys_read_filepr_fieldpr_atoipr_slenpr_daemon_for+7 |
| 132 | func main(argc: i64, argv: *i64) -> i64 |