code wiki / _hdl_build / nx_polite_crawl_test.nx

nx_polite_crawl_test.nx source

↩ module page · 141 lines · 7705 B

1// nx_polite_crawl_test.nx -- ACCEPTANCE GATE for the polite crawl governor. Exact-integer KATs + 2// a SIMULATED crawl proving the synergy: spreading across many hosts is BOTH broad AND polite, and 3// a host that pushes back (429) gets backed off, not hammered. 4import "nx_polite_crawl.nx" 5import "nx_syscalls.nx" 6 7func ct_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 8func ct_putn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1} sys_write(1,bb,k); return 0 } 9 10func main() -> i64 { 11 var pass: i64 = 0 12 var total: i64 = 0 13 14 // T1: backoff doubles and caps -- base 1000, cap 30000: consec 0..6 -> 1000,2000,4000,8000,16000,30000,30000 15 total = total + 1 16 var t1: i64 = 1 17 if pc_backoff_ms(0, 1000, 30000) != 1000 { t1 = 0 } 18 if pc_backoff_ms(1, 1000, 30000) != 2000 { t1 = 0 } 19 if pc_backoff_ms(3, 1000, 30000) != 8000 { t1 = 0 } 20 if pc_backoff_ms(5, 1000, 30000) != 30000 { t1 = 0 } // 32000 capped to 30000 21 if pc_backoff_ms(9, 1000, 30000) != 30000 { t1 = 0 } 22 if t1 == 1 { pass = pass + 1 } else { ct_puts("T1 FAIL backoff " as *u8); ct_putn(pc_backoff_ms(5,1000,30000)); ct_puts("\n" as *u8) } 23 24 // T2: may_fetch respects min interval AND robots crawl-delay (max of the two) 25 total = total + 1 26 var t2: i64 = 1 27 // last=1000, interval=500, crawl=0 : at now=1400 NO (1000+500=1500), at 1500 YES 28 if pc_may_fetch(1000, 0, 1400, 500, 0) != 0 { t2 = 0 } 29 if pc_may_fetch(1000, 0, 1500, 500, 0) != 1 { t2 = 0 } 30 // crawl-delay 2000 overrides interval 500: at now=1500 NO (need 1000+2000=3000) 31 if pc_may_fetch(1000, 0, 1500, 500, 2000) != 0 { t2 = 0 } 32 if pc_may_fetch(1000, 0, 3000, 500, 2000) != 1 { t2 = 0 } 33 if t2 == 1 { pass = pass + 1 } else { ct_puts("T2 FAIL mayfetch\n" as *u8) } 34 35 // T3: may_fetch blocked while backoff active 36 total = total + 1 37 var t3: i64 = 1 38 if pc_may_fetch(1000, 5000, 4000, 500, 0) != 0 { t3 = 0 } // backoff until 5000, now 4000 -> no 39 if pc_may_fetch(1000, 5000, 5000, 500, 0) != 1 { t3 = 0 } // now 5000 -> ok 40 if t3 == 1 { pass = pass + 1 } else { ct_puts("T3 FAIL backoff-gate\n" as *u8) } 41 42 // T4: on_response -- 429 sets exponential backoff + increments consec; 200 resets 43 total = total + 1 44 let bu: *i64 = sys_mmap(64) as *i64 45 let cs: *i64 = sys_mmap(64) as *i64 46 bu[0] = 0; cs[0] = 0 47 pc_on_response(bu, cs, 0, PC_RETRY, 10000, 1000, 30000) // 1st 429: consec1 -> backoff 2000 -> until 12000 48 var t4: i64 = 1 49 if cs[0] != 1 { t4 = 0 } 50 if bu[0] != 12000 { t4 = 0 } 51 pc_on_response(bu, cs, 0, PC_RETRY, 12000, 1000, 30000) // 2nd 429: consec2 -> backoff 4000 -> until 16000 52 if cs[0] != 2 { t4 = 0 } 53 if bu[0] != 16000 { t4 = 0 } 54 pc_on_response(bu, cs, 0, PC_OK, 16000, 1000, 30000) // 200: reset 55 if cs[0] != 0 { t4 = 0 } 56 if bu[0] != 0 { t4 = 0 } 57 if t4 == 1 { pass = pass + 1 } else { ct_puts("T4 FAIL onresp bu=" as *u8); ct_putn(bu[0]); ct_puts(" cs=" as *u8); ct_putn(cs[0]); ct_puts("\n" as *u8) } 58 59 // T5: 403 hard-block -> effectively-forever backoff (stop hitting it) 60 total = total + 1 61 let bu2: *i64 = sys_mmap(32) as *i64; let cs2: *i64 = sys_mmap(32) as *i64; bu2[0]=0; cs2[0]=0 62 pc_on_response(bu2, cs2, 0, PC_BLOCKED, 1000, 1000, 30000) 63 if pc_may_fetch(1000, bu2[0], 999999, 500, 0) == 0 { pass = pass + 1 } else { ct_puts("T5 FAIL block\n" as *u8) } 64 65 // T6: scheduler picks the LONGEST-IDLE eligible host (spreads load) 66 total = total + 1 67 let last: *i64 = sys_mmap(64) as *i64 68 let bk: *i64 = sys_mmap(64) as *i64 69 let cd: *i64 = sys_mmap(64) as *i64 70 // 4 hosts; last-times 5000,1000,3000,2000 ; all backoff 0, no crawl delay ; interval 500, now 10000 71 last[0]=5000; last[1]=1000; last[2]=3000; last[3]=2000 72 bk[0]=0; bk[1]=0; bk[2]=0; bk[3]=0; cd[0]=0; cd[1]=0; cd[2]=0; cd[3]=0 73 // host 1 idle longest (last=1000) -> picked 74 if pc_next_host(last, bk, cd, 4, 10000, 500) == 1 { pass = pass + 1 } else { ct_puts("T6 FAIL sched=" as *u8); ct_putn(pc_next_host(last,bk,cd,4,10000,500)); ct_puts("\n" as *u8) } 75 76 // T7: scheduler returns -1 when NO host is eligible (all just fetched) -> caller waits, never busy-hits 77 total = total + 1 78 last[0]=9900; last[1]=9900; last[2]=9900; last[3]=9900 // all fetched at 9900, interval 500, now 10000 79 if pc_next_host(last, bk, cd, 4, 10000, 500) == (0 - 1) { pass = pass + 1 } else { ct_puts("T7 FAIL noneligible\n" as *u8) } 80 81 // T8: breadth metric -- 100 distinct hosts over 120 requests = 833 permil (broad+polite) 82 total = total + 1 83 var t8: i64 = 1 84 if pc_breadth_permil(100, 120) != 833 { t8 = 0 } 85 if pc_breadth_permil(2, 200) != 10 { t8 = 0 } // 2 hosts 200 reqs = hammering = low breadth 86 if t8 == 1 { pass = pass + 1 } else { ct_puts("T8 FAIL breadth\n" as *u8) } 87 88 // T9: THE RESEARCH GATE -- broad (100 hosts), polite (<=2 each), high breadth -> PASS; 89 // small-sampled OR hammering -> FAIL 90 total = total + 1 91 let rph: *i64 = sys_mmap(1024) as *i64 92 var i: i64 = 0 93 while i < 100 { rph[i] = 1; i = i + 1 } // 100 hosts, 1 request each = ideal 94 var t9: i64 = 1 95 // broad+polite: 100 hosts, 100 reqs, floor 2, min 50 hosts, min breadth 500 permil -> PASS 96 if pc_research_gate(100, 100, rph, 100, 50, 2, 500) != 1 { t9 = 0 } 97 // make host 0 hammered (50 reqs) -> not polite -> FAIL 98 rph[0] = 50 99 if pc_research_gate(100, 149, rph, 100, 50, 2, 500) != 0 { t9 = 0 } 100 // small-sampled: only 5 hosts -> not broad -> FAIL 101 rph[0] = 1 102 if pc_research_gate(5, 5, rph, 5, 50, 2, 500) != 0 { t9 = 0 } 103 if t9 == 1 { pass = pass + 1 } else { ct_puts("T9 FAIL gate\n" as *u8) } 104 105 // T10: SIMULATED CRAWL -- 60 hosts, round-robin, each fetched exactly twice, paced; prove 106 // it stays polite (max 2/host) AND broad (60 hosts) over the run. 107 total = total + 1 108 let L: *i64 = sys_mmap(8192) as *i64 109 let B: *i64 = sys_mmap(8192) as *i64 110 let C: *i64 = sys_mmap(8192) as *i64 111 let R: *i64 = sys_mmap(8192) as *i64 112 let NH: i64 = 60 113 i = 0 114 while i < NH { L[i] = 0 - 100000; B[i] = 0; C[i] = 0; R[i] = 0; i = i + 1 } 115 var now: i64 = 0 116 var issued: i64 = 0 117 var guard: i64 = 0 118 while issued < 120 { 119 if guard > 100000 { issued = 120 } // safety: never spin 120 let h: i64 = pc_next_host(L, B, C, NH, now, 1000) // 1s min interval per host 121 if h < 0 { now = now + 250 } // nobody ready -> advance the clock (wait) 122 else { 123 pc_record(L, h, now) 124 R[h] = R[h] + 1 125 pc_on_response(B, C, h, PC_OK, now, 1000, 30000) 126 issued = issued + 1 127 now = now + 50 // 50ms between any two requests globally 128 } 129 guard = guard + 1 130 } 131 var t10: i64 = 1 132 if pc_is_polite(R, NH, 2) != 1 { t10 = 0 } // no host hit > 2x 133 if pc_is_broad(NH, 50) != 1 { t10 = 0 } // 60 >= 50 hosts = broad 134 if issued != 120 { t10 = 0 } 135 if t10 == 1 { pass = pass + 1 } else { ct_puts("T10 FAIL crawl polite=" as *u8); ct_putn(pc_is_polite(R,NH,2)); ct_puts(" issued=" as *u8); ct_putn(issued); ct_puts("\n" as *u8) } 136 137 ct_puts("POLITE-CRAWL " as *u8); ct_putn(pass); ct_puts("/" as *u8); ct_putn(total); ct_puts("\n" as *u8) 138 if pass == total { ct_puts("POLITE-CRAWL ALL-PASS\n" as *u8); sys_exit(0) } 139 sys_exit(1) 140 return 1 141}