#!/usr/bin/env bash # diag_bugs.sh — диагностика двух аномалий из bench_full run=9248232 # BUG-1: php/medium + php/large COLD-TIMEOUT (гипотеза: arrow functions PHP 7.4+) # BUG-2: ruby/medium cold=80451ms (гипотеза: poolmgr eviction — проверяем воспроизводимость) set -uo pipefail SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) ROOT_DIR=$(cd "${SCRIPT_DIR}/.." && pwd) source "${ROOT_DIR}/scripts/lib.sh" SUB="bench@bench.local" RUNID="diag$(date +%s | tail -c 6)" TMP=$(mktemp -d) TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S) RESULTS_DIR="${ROOT_DIR}/test-results/bench-func" LOGFILE="${RESULTS_DIR}/diag_bugs_${TIMESTAMP}.log" mkdir -p "$RESULTS_DIR" declare -a ALL_FNS=() PASS_CNT=0 FAIL_CNT=0 cleanup() { rm -rf "$TMP" 2>/dev/null || true for fn in "${ALL_FNS[@]:-}"; do curl -s -X DELETE "${BASE}/functions/${fn}" \ -H "X-Auth-Token: ${SUB}" >/dev/null 2>&1 || true done } trap cleanup EXIT log() { echo "$@" | tee -a "$LOGFILE"; } escape_code() { python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))'; } get_status() { python3 -c "import json,sys try: d=json.load(sys.stdin); print(d.get('status','')) except: print('')" 2>/dev/null } get_field() { local f="$1" python3 -c "import json,sys try: d=json.load(sys.stdin); print(d.get('$f','')) except: print('')" 2>/dev/null } log "════════════════════════════════════════════════════════════════" log " diag_bugs.sh run=${RUNID} $(date)" log " API: ${BASE} SUB: ${SUB}" log "════════════════════════════════════════════════════════════════" # ═══════════════════════════════════════════════════════════════════ # Вспомогательная: загрузить функцию, сделать cold+hot invoke, вернуть результат # bench_one LABEL LANG CODEFILE [EXPECTED_STR] # ═══════════════════════════════════════════════════════════════════ bench_one() { local label="$1" lang="$2" codefile="$3" expected="${4:-}" local fn="bench-${label}-${RUNID}" ALL_FNS+=("$fn") # Upload local code_esc t0 t1 R CODE BODY UPLOAD_MS code_esc=$(cat "$codefile" | escape_code) t0=$(date +%s%3N) R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" \ -d "{\"name\":\"${fn}\",\"language\":\"${lang}\",\"code\":${code_esc}}" 2>/dev/null) t1=$(date +%s%3N) CODE=$(echo "$R" | tail -1) BODY=$(echo "$R" | head -n -1) UPLOAD_MS=$((t1-t0)) if [ "$CODE" != "201" ]; then log " [${label}] UPLOAD FAIL HTTP=${CODE}: $(echo "$BODY" | head -c 200)" FAIL_CNT=$((FAIL_CNT+1)) return 1 fi log " [${label}] upload OK ${UPLOAD_MS}ms" # Cold start (retry 20×5s) local tc0 COLD_MS=0 cold_ok=0 cold_body="" tc0=$(date +%s%3N) for i in $(seq 1 20); do local resp s raw resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) s=$(echo "$resp" | get_status) raw=$(echo "$resp" | get_field "response_raw") if [ "$s" = "200" ]; then if [ -z "$expected" ] || echo "$raw" | grep -qF "$expected"; then local tc1; tc1=$(date +%s%3N) COLD_MS=$((tc1-tc0)) cold_ok=1 cold_body="$raw" break fi fi sleep 5 done if [ $cold_ok -eq 0 ]; then log " [${label}] COLD TIMEOUT" FAIL_CNT=$((FAIL_CNT+1)) return 1 fi # Hot ×3 local HOT_TOTAL=0 hot_ok=0 for i in 1 2 3; do local th0 th1 resp s th0=$(date +%s%3N) resp=$(curl -s --max-time 35 -X POST "${BASE}/functions/${fn}/invoke" \ -H "X-Auth-Token: ${SUB}" -H "Content-Type: application/json" -d '{}' 2>/dev/null) th1=$(date +%s%3N) s=$(echo "$resp" | get_status) [ "$s" = "200" ] && { HOT_TOTAL=$((HOT_TOTAL+th1-th0)); hot_ok=$((hot_ok+1)); } done local HOT_AVG=0; [ $hot_ok -gt 0 ] && HOT_AVG=$((HOT_TOTAL/hot_ok)) log " [${label}] OK cold=${COLD_MS}ms hot_avg=${HOT_AVG}ms (${hot_ok}/3)" [ -n "$cold_body" ] && log " [${label}] body=$(echo "$cold_body" | head -c 120)" PASS_CNT=$((PASS_CNT+1)) } # ═══════════════════════════════════════════════════════════════════ # BUG-1: PHP arrow functions # ═══════════════════════════════════════════════════════════════════ log "" log "┌─────────────────────────────────────────────────────────────────┐" log "│ BUG-1: PHP arrow functions (fn()=> требует PHP 7.4+) │" log "└─────────────────────────────────────────────────────────────────┘" # Тест 1а: минимальный PHP с arrow function — поддерживается? cat > "${TMP}/php_arrow_min.php" << 'EOF' $x * 2, $nums); return "arrow-ok:" . implode(",", $doubled); } EOF log "" log " [php-arrow-min] Тест: arrow function fn(\$x)=>\$x*2 (PHP 7.4+)" bench_one "php-arrow-min" "php" "${TMP}/php_arrow_min.php" "arrow-ok" # Тест 1б: PHP version info через phpinfo-like cat > "${TMP}/php_version.php" << 'EOF' "${TMP}/php_medium_noarrow.php" << 'EOF' $arr[$j+1]){$t=$arr[$j];$arr[$j]=$arr[$j+1];$arr[$j+1]=$t;}return $arr;} function matMul($a,$b){$ra=count($a);$ca=count($a[0]);$cb=count($b[0]);$c=array_fill(0,$ra,array_fill(0,$cb,0));for($i=0;$i<$ra;$i++)for($j=0;$j<$cb;$j++)for($k=0;$k<$ca;$k++)$c[$i][$j]+=$a[$i][$k]*$b[$k][$j];return $c;} function processWordsNoArrow($words){ $out=[]; foreach($words as $s){ if(strlen($s)>3) $out[]=strrev(strtoupper($s)); } return $out; } function statsArrNoArrow($arr){ $n=count($arr);$m=array_sum($arr)/$n; $v=0; foreach($arr as $x) $v+=($x-$m)*($x-$m); $v/=$n; return['n'=>$n,'mean'=>round($m,4),'std'=>round(sqrt($v),4)]; } function main($event,$context){ $t0=microtime(true); $words=["alpha","beta","gamma","delta","epsilon","zeta","eta","theta","iota","kappa","lambda","mu"]; $f=fib(35);$primes=sieve(500);$s=sqrtSum(1000); $nums=range(50,1);$sorted=bubbleSort($nums); $a=[[1,2,3],[4,5,6]];$b=[[7,8],[9,10],[11,12]];$c=matMul($a,$b); $pw=processWordsNoArrow($words);$st=statsArrNoArrow($primes); $h=substr(hash('sha256','bench-medium-php'),0,16); $elapsed=microtime(true)-$t0; return json_encode(['tag'=>'bench-php-medium-noarrow-ok','fib35'=>$f,'primes'=>count($primes), 'sqrt'=>round($s,1),'sorted0'=>$sorted[0],'mat_c00'=>$c[0][0], 'words'=>count($pw),'stats'=>$st,'hash'=>$h,'elapsed'=>round($elapsed,5)]); } EOF log "" log " [php-medium-noarrow] PHP medium логика без arrow functions (foreach вместо fn()=>)" bench_one "php-med-noarr" "php" "${TMP}/php_medium_noarrow.php" "bench-php-medium-noarrow-ok" # ═══════════════════════════════════════════════════════════════════ # BUG-2: ruby/medium cold=80451ms — воспроизводимость # ═══════════════════════════════════════════════════════════════════ log "" log "┌─────────────────────────────────────────────────────────────────┐" log "│ BUG-2: ruby/medium cold=80451ms — воспроизводимость (×3) │" log "└─────────────────────────────────────────────────────────────────┘" cat > "${TMP}/ruby_medium_diag.rb" << 'EOF' require 'json' require 'digest' WORDS=%w[alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega] def fib(n); a,b=0,1; n.times{a,b=b,a+b}; a; end def sieve(n) s=Array.new(n+1,true); s[0]=s[1]=false (2..Math.sqrt(n).to_i).each{|i|(i*i..n).step(i){|j|s[j]=false} if s[i]} (0..n).select{|i|s[i]} end def sqrt_sum(n); (1..n).sum{|i|Math.sqrt(i)}; end def bubble_sort(arr) arr=arr.dup; n=arr.length n.times{(n-1).times{|j|arr[j],arr[j+1]=arr[j+1],arr[j] if arr[j]>arr[j+1]}} arr end def mat_mul(a,b) ra,ca,cb=a.length,a[0].length,b[0].length Array.new(ra){|i|Array.new(cb){|j|(0...ca).sum{|k|a[i][k]*b[k][j]}}} end def process_words(w); w.select{|s|s.length>3}.map{|s|s.upcase.reverse}; end def get_stats(arr) n=arr.length; m=arr.sum.to_f/n; v=arr.sum{|x|(x-m)**2}/n {n:n, mean:m.round(4), std:Math.sqrt(v).round(4)} end def handler(ctx) t0=Time.now f=fib(35); primes=sieve(500); s=sqrt_sum(1000) nums=(1..50).to_a.reverse; sorted=bubble_sort(nums) a=[[1,2,3],[4,5,6]]; b=[[7,8],[9,10],[11,12]]; c=mat_mul(a,b) pw=process_words(WORDS); st=get_stats(primes) h=Digest::SHA256.hexdigest('bench-medium-ruby')[0,16] elapsed=Time.now-t0 { statusCode: 200, body: JSON.generate({tag:"bench-ruby-medium-ok", fib35:f, primes:primes.length, sqrt:s.round(1), sorted0:sorted[0], mat_c00:c[0][0], words:pw.length, stats:st, hash:h, elapsed:elapsed.round(5)}) } end EOF log "" log " ruby/medium × 3 независимых функции (разные имена → чистый cold start каждый раз)" for attempt in 1 2 3; do log "" log " --- Попытка ${attempt}/3 ---" bench_one "ruby-med-${attempt}" "ruby" "${TMP}/ruby_medium_diag.rb" "bench-ruby-medium-ok" done # ═══════════════════════════════════════════════════════════════════ # ИТОГ # ═══════════════════════════════════════════════════════════════════ log "" log "════════════════════════════════════════════════════════════════" log " ИТОГ: PASS=${PASS_CNT} FAIL=${FAIL_CNT} TOTAL=$((PASS_CNT+FAIL_CNT))" log " Лог: ${LOGFILE}" log " Готово: $(date)" log "════════════════════════════════════════════════════════════════"