code wiki / _hdl_build / nx_apm_sweep.nx

nx_apm_sweep.nx source

↩ module page · 220 lines · 12227 B

1// nx_apm_sweep.nx -- SOVEREIGN concurrency-aware APM probe sweep (the "New Relic" core). 2// 3// WHY: the existing monitor (nx_site_monitor/nx_health_probe) is BINARY up/down and SINGLE-request, 4// and the HTTPS fetch used to read-until-close (measuring the ~19s keep-alive idle timeout, not real 5// latency). So a daemon that serves fine alone but STALLS under concurrent load ("switching tabs and 6// tabs don't load") showed all-green. This organ fixes that: it fires N SIMULTANEOUS cold probes per 7// endpoint (= N browser tabs) using the Content-Length-honest nx_fetch_staged, and reports the real 8// latency distribution + stalls + timeouts + error rate, with a durable per-endpoint time-series row. 9// 10// usage: nx_apm_sweep [concurrency] (default 8) 11// out: per-endpoint report on stdout + appended rows in knowledge/status/apm_metrics.tsv 12// license_tier: ORIGINAL 13import "nx_fetch_unit.nx" // nx_fetch_staged, FU_CAP 14import "nx_syscalls.nx" 15const APS_MAGIC_1024: i64 = 1024 16const APS_MAGIC_65536: i64 = 65536 17 18const APS_STALL_MS: i64 = 2000 19const APS_TIMEOUT_MS: i64 = 18000 20const APS_METRICS: *u8 = "knowledge/status/apm_metrics.tsv" 21 22func aps_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 23func aps_w(s: *u8) -> i64 { return sys_write(1, s, aps_slen(s)) } 24// append decimal v into buf at off, return new off 25func aps_n(buf: *u8, off: i64, v: i64) -> i64 { 26 var o: i64 = off; var m: i64 = v 27 if m < 0 { buf[o] = 45 as u8; o = o + 1; m = 0 - m } 28 let t: *u8 = sys_mmap(24); var k: i64 = 0 29 if m == 0 { t[0] = 48 as u8; k = 1 } 30 while m > 0 { t[k] = (48 + (m - (m/10)*10)) as u8; m = m / 10; k = k + 1 } 31 var i: i64 = 0; while i < k { buf[o + i] = t[k - 1 - i]; i = i + 1 } 32 return o + k 33} 34// append C-string s into buf at off, return new off 35func aps_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 } 36func aps_wn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); let e: i64 = aps_n(b, 0, v); return sys_write(1, b, e) } 37// pad a number's printed width to w with leading spaces (right-align) 38func aps_wpad(v: i64, w: i64) -> i64 { 39 let b: *u8 = sys_mmap(24); let e: i64 = aps_n(b, 0, v) 40 var pad: i64 = w - e; while pad > 0 { sys_write(1, " " as *u8, 1); pad = pad - 1 } 41 return sys_write(1, b, e) 42} 43 44// parse HTTP status from "HTTP/1.x NNN ..."; -1 if not an HTTP response. 45func aps_status(buf: *u8, n: i64) -> i64 { 46 if n < 13 { return 0 - 1 } 47 if buf[0] != (72 as u8) { return 0 - 1 } // 'H' 48 var i: i64 = 0; var sp: i64 = 0 - 1 49 while i < n { if buf[i] == (32 as u8) { sp = i; i = n } else { i = i + 1 } } 50 if sp < 0 { return 0 - 1 } 51 if sp + 4 > n { return 0 - 1 } 52 let c0: i64 = buf[sp+1] as i64; let c1: i64 = buf[sp+2] as i64; let c2: i64 = buf[sp+3] as i64 53 if c0 < 48 { return 0 - 1 } if c0 > 57 { return 0 - 1 } 54 if c1 < 48 { return 0 - 1 } if c1 > 57 { return 0 - 1 } 55 if c2 < 48 { return 0 - 1 } if c2 > 57 { return 0 - 1 } 56 return (c0-48)*100 + (c1-48)*10 + (c2-48) 57} 58 59// one cold probe; out_status[0]=HTTP code (or -1), returns elapsed ms. 60func aps_probe(url: *u8, out_status: *i64) -> i64 { 61 let buf: *u8 = sys_mmap(FU_CAP) 62 let t0: i64 = sys_now_ms() 63 let n: i64 = nx_fetch_staged(url, buf, FU_CAP) 64 let t1: i64 = sys_now_ms() 65 // nx_fetch_staged returns the BODY byte-count (headers already stripped), >0 for any HTTP 66 // response (incl. 401), <=0 for a connect/TLS failure. So bytes>0 == "server responded". 67 out_status[0] = n 68 return t1 - t0 69} 70 71// build "/tmp/nx_apm_<base>_<k>\0" 72func aps_mkpath(path: *u8, base: i64, k: i64) -> i64 { 73 var o: i64 = aps_s(path, 0, "/tmp/nx_apm_" as *u8) 74 o = aps_n(path, o, base); path[o] = 95 as u8; o = o + 1; o = aps_n(path, o, k) 75 path[o] = 0 as u8; return o 76} 77 78// parse two leading ints (ms, status) from a result file's bytes. 79func aps_2ints(data: *u8, n: i64, out: *i64) -> i64 { 80 out[0] = 0 - 1; out[1] = 0 - 1 81 var i: i64 = 0; var idx: i64 = 0 82 while idx < 2 { 83 var go: i64 = 1 84 while go == 1 { if i >= n { go = 0; idx = 2 } else { let c: i64 = data[i] as i64; if c == 45 { go = 0 } else { if c >= 48 { if c <= 57 { go = 0 } else { i = i + 1 } } else { i = i + 1 } } } } 85 if idx < 2 { 86 var neg: i64 = 0 87 if i < n { if data[i] as i64 == 45 { neg = 1; i = i + 1 } } 88 var v: i64 = 0; var any: i64 = 0; var g2: i64 = 1 89 while g2 == 1 { if i >= n { g2 = 0 } else { let d: i64 = data[i] as i64; if d >= 48 { if d <= 57 { v = v*10 + (d-48); any = 1; i = i + 1 } else { g2 = 0 } } else { g2 = 0 } } } 90 if neg == 1 { v = 0 - v } 91 if any == 1 { out[idx] = v; idx = idx + 1 } else { idx = 2 } 92 } 93 } 94 return 0 95} 96 97// insertion sort ascending 98func aps_sort(a: *i64, n: i64) -> i64 { 99 var i: i64 = 1 100 while i < n { 101 let kv: i64 = a[i]; var j: i64 = i - 1; var go: i64 = 1 102 while go == 1 { if j >= 0 { if a[j] > kv { a[j+1] = a[j]; j = j - 1 } else { go = 0 } } else { go = 0 } } 103 a[j+1] = kv; i = i + 1 104 } 105 return 0 106} 107 108// fire `conc` concurrent cold probes of url; aggregate; print a report row; append a time-series row. 109func aps_burst(url: *u8, label: *u8, conc: i64, base: i64, epoch: i64) -> i64 { 110 var k: i64 = 0 111 while k < conc { 112 let pid: i64 = sys_fork() 113 if pid == 0 { 114 let st: *i64 = sys_mmap(16) as *i64 115 let ms: i64 = aps_probe(url, st) 116 let path: *u8 = sys_mmap(64); aps_mkpath(path, base, k) 117 let ob: *u8 = sys_mmap(48); var o: i64 = aps_n(ob, 0, ms); ob[o] = 32 as u8; o = o + 1; o = aps_n(ob, o, st[0]); ob[o] = 10 as u8; o = o + 1 118 let fd: i64 = sys_openat_wr(path, 0x1a4) 119 if fd >= 0 { sys_write(fd, ob, o); sys_close(fd) } 120 sys_exit(0) 121 } 122 k = k + 1 123 } 124 let stp: *i64 = sys_mmap(16) as *i64; var got: i64 = 0 125 while got < conc { let r: i64 = sys_wait4(0 - 1, stp, 0); if r <= 0 { got = conc } else { got = got + 1 } } 126 127 let ms: *i64 = sys_mmap(8 * (conc + 2)) as *i64 128 var nres: i64 = 0; var oks: i64 = 0; var stalls: i64 = 0; var timeouts: i64 = 0; var errs: i64 = 0 129 var kk: i64 = 0 130 while kk < conc { 131 let path: *u8 = sys_mmap(64); aps_mkpath(path, base, kk) 132 let lp: *i64 = sys_mmap(16) as *i64; lp[0] = 0 133 let data: *u8 = sys_read_file(path, lp) 134 if (data as i64) != 0 { if lp[0] > 0 { 135 let v: *i64 = sys_mmap(24) as *i64; aps_2ints(data, lp[0], v) 136 let mv: i64 = v[0]; let sv: i64 = v[1] // mv = ms, sv = body bytes (>0 = responded) 137 if mv >= 0 { ms[nres] = mv; nres = nres + 1 } 138 if sv > 0 { oks = oks + 1 } else { errs = errs + 1 } 139 if sv <= 0 { timeouts = timeouts + 1 } else { if mv >= APS_TIMEOUT_MS { timeouts = timeouts + 1 } } 140 if mv >= APS_STALL_MS { stalls = stalls + 1 } 141 } } 142 kk = kk + 1 143 } 144 aps_sort(ms, nres) 145 var mn: i64 = 0; var p50: i64 = 0; var p95: i64 = 0; var mx: i64 = 0 146 if nres > 0 { 147 mn = ms[0]; mx = ms[nres-1]; p50 = ms[nres/2] 148 var pi: i64 = (nres*95)/100; if pi >= nres { pi = nres - 1 } p95 = ms[pi] 149 } 150 151 // verdict 152 var verd: *u8 = "GREEN" as *u8 153 if stalls > 0 { verd = "AMBER" as *u8 } 154 if timeouts > 0 { verd = "RED " as *u8 } 155 if errs > 0 { verd = "RED " as *u8 } 156 if oks < conc { if timeouts == 0 { verd = "AMBER" as *u8 } } 157 158 // report row 159 aps_w("[" as *u8); aps_w(verd); aps_w("] " as *u8) 160 let lw: i64 = aps_slen(label); sys_write(1, label, lw); var pad: i64 = 22 - lw; while pad > 0 { sys_write(1, " " as *u8, 1); pad = pad - 1 } 161 aps_w("ok=" as *u8); aps_wn(oks); aps_w("/" as *u8); aps_wn(conc) 162 aps_w(" p50=" as *u8); aps_wpad(p50, 5); aps_w(" p95=" as *u8); aps_wpad(p95, 6); aps_w(" max=" as *u8); aps_wpad(mx, 6); aps_w("ms" as *u8) 163 aps_w(" stall=" as *u8); aps_wn(stalls); aps_w(" to=" as *u8); aps_wn(timeouts); aps_w(" err=" as *u8); aps_wn(errs) 164 aps_w("\n" as *u8) 165 166 // time-series append: epoch label url conc oks nres min p50 p95 max stalls timeouts errs 167 // SOVEREIGN seg-store record (latest-per-endpoint, key:value; NO tsv) -- per-key file + apmsweep-ids index 168 sys_mkdir("knowledge/store" as *u8, 0x1ff) 169 let sp: *u8 = sys_mmap(512); var spo: i64 = aps_s(sp, 0, "knowledge/store/apmsweep-" as *u8); spo = aps_s(sp, spo, label); sp[spo]=0 as u8 170 let rb: *u8 = sys_mmap(APS_MAGIC_1024); var ro: i64 = 0 171 ro=aps_s(rb,ro,"label=" as *u8); ro=aps_s(rb,ro,label); rb[ro]=10 as u8; ro=ro+1 172 ro=aps_s(rb,ro,"epoch=" as *u8); ro=aps_n(rb,ro,epoch); rb[ro]=10 as u8; ro=ro+1 173 ro=aps_s(rb,ro,"url=" as *u8); ro=aps_s(rb,ro,url); rb[ro]=10 as u8; ro=ro+1 174 ro=aps_s(rb,ro,"conc=" as *u8); ro=aps_n(rb,ro,conc); rb[ro]=10 as u8; ro=ro+1 175 ro=aps_s(rb,ro,"oks=" as *u8); ro=aps_n(rb,ro,oks); rb[ro]=10 as u8; ro=ro+1 176 ro=aps_s(rb,ro,"ttfb_p50=" as *u8); ro=aps_n(rb,ro,p50); rb[ro]=10 as u8; ro=ro+1 177 ro=aps_s(rb,ro,"ttfb_p95=" as *u8); ro=aps_n(rb,ro,p95); rb[ro]=10 as u8; ro=ro+1 178 ro=aps_s(rb,ro,"ttfb_max=" as *u8); ro=aps_n(rb,ro,mx); rb[ro]=10 as u8; ro=ro+1 179 ro=aps_s(rb,ro,"stalls=" as *u8); ro=aps_n(rb,ro,stalls); rb[ro]=10 as u8; ro=ro+1 180 ro=aps_s(rb,ro,"timeouts=" as *u8); ro=aps_n(rb,ro,timeouts); rb[ro]=10 as u8; ro=ro+1 181 ro=aps_s(rb,ro,"errs=" as *u8); ro=aps_n(rb,ro,errs); rb[ro]=10 as u8; ro=ro+1 182 let mfd: i64 = sys_openat_wr(sp, 0x1a4); if mfd >= 0 { sys_write(mfd, rb, ro); sys_close(mfd) } 183 aps_seg_index(label) 184 return 0 185} 186 187// ---- SOVEREIGN seg-store (NO tsv): latest-per-endpoint record + apmsweep-ids index ---- 188func aps_line_eq(buf: *u8, start: i64, end: i64, label: *u8) -> i64 { var i: i64=start; var j: i64=0; while i<end { if buf[i]!=label[j] {return 0} i=i+1; j=j+1 } if label[j]!=(0 as u8) {return 0} return 1 } 189func aps_seg_index(label: *u8) -> i64 { 190 let buf: *u8=sys_mmap(APS_MAGIC_65536); var n: i64=0; let fd: i64=sys_openat_rd("knowledge/store/apmsweep-ids" as *u8); if fd>=0 { n=sys_read(fd,buf,APS_MAGIC_65536); sys_close(fd) } 191 var present: i64=0; var ls: i64=0; var i: i64=0 192 while i<=n { var nl: i64=0; if i>=n{nl=1} else { if buf[i]==(10 as u8){nl=1} } if nl==1 { if i>ls { if aps_line_eq(buf,ls,i,label)==1 {present=1} } ls=i+1 } i=i+1 } 193 if present==0 { let afd: i64=sys_openat_append("knowledge/store/apmsweep-ids" as *u8, 0x1a4); if afd>=0 { let lb: *u8=sys_mmap(128); var lo: i64=aps_s(lb,0,label); lb[lo]=10 as u8; lo=lo+1; sys_write(afd,lb,lo); sys_close(afd) } } 194 return 0 195} 196 197func main(argc: i64, argv: *i64) -> i64 { 198 var conc: i64 = 8 199 if argc >= 2 { 200 let a: *u8 = argv[1] as *u8; var v: i64 = 0; var i: i64 = 0 201 while a[i] != (0 as u8) { let c: i64 = a[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 } 202 if v > 0 { conc = v } 203 } 204 let base: i64 = sys_now_ms() 205 let epoch: i64 = sys_now_realtime_sec() 206 207 aps_w("=== NISHI APM SWEEP (concurrency=" as *u8); aps_wn(conc); aps_w(", browser-faithful, cold-connection) ===\n" as *u8) 208 aps_w(" stall=>2000ms timeout=>18000ms verdict GREEN/AMBER(stall)/RED(timeout|err|5xx)\n\n" as *u8) 209 210 aps_burst("https://nishifamily.com/" as *u8, "landing /" as *u8, conc, base, epoch) 211 aps_burst("https://nishifamily.com/status.html" as *u8, "status /status.html" as *u8, conc, base, epoch) 212 aps_burst("https://nishifamily.com/wiki" as *u8, "wiki /wiki" as *u8, conc, base, epoch) 213 aps_burst("https://nishifamily.com/gallery/login" as *u8, "gallery /gallery/login" as *u8, conc, base, epoch) 214 aps_burst("https://nishifamily.com/gallery/api/list" as *u8, "gallery /gallery/api" as *u8, conc, base, epoch) 215 aps_burst("https://nishifamily.com/library" as *u8, "library /library" as *u8, conc, base, epoch) 216 aps_burst("https://nishifamily.com/media" as *u8, "media /media" as *u8, conc, base, epoch) 217 218 aps_w("\nappended " as *u8); aps_w(APS_METRICS); aps_w(" (epoch=" as *u8); aps_wn(epoch); aps_w(")\n" as *u8) 219 return 0 220}