#!/usr/bin/env bash # searxng-global full S3c run: pull → up → idle 5min → T3×5 + engine matrix → T5 → summarize set -euo pipefail ROOT="$(cd "$(dirname "$0")" && pwd)" # shellcheck disable=SC1091 source "$ROOT/_lib.sh" NAME="bench-s3c-searxng-global" IMG="docker.io/searxng/searxng:2026.8.29-d226b78bc" RAW="$ROOT/raw" SAMP="$ROOT/samples" mkdir -p "$RAW" "$SAMP" exec > >(tee -a "$RAW/run.log") 2>&1 log "=== searxng-global S3c start cwd=$ROOT ===" ensure_image "$IMG" "$RAW/pull.how" PULL_HOW="$(cat "$RAW/pull.how" 2>/dev/null || echo unknown)" log "compose down leftover" docker rm -f "$NAME" >/dev/null 2>&1 || true log "docker compose up -d (project bench-s3c-searxng-global)" (cd "$ROOT" && docker compose -p bench-s3c-searxng-global up -d --force-recreate) log "wait /healthz" if ! wait_http "http://127.0.0.1:18882/healthz" 60; then log "healthz failed; try /" wait_http "http://127.0.0.1:18882/" 20 || true docker logs --tail 80 "$NAME" | tee "$RAW/container-start.log" || true fi ARCH="$(docker image inspect --format '{{.Architecture}}' "$IMG")" DIGEST="$(docker inspect --format '{{index .RepoDigests 0}}' "$NAME" 2>/dev/null || true)" IID="$(docker inspect --format '{{.Image}}' "$NAME")" log "image arch=$ARCH digest=$DIGEST id=$IID pull=$PULL_HOW" # proxy egress check from HOST (same pool the container uses) PROXY_CODE="$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 10 --max-time 20 \ -x http://127.0.0.1:17890 -A onesvm-bench/s3c \ https://www.google.com/generate_204 || true)" log "proxy generate_204 via 127.0.0.1:17890 http=$PROXY_CODE" # also check container can resolve host.docker.internal HOST_RES="$(docker exec "$NAME" getent hosts host.docker.internal 2>/dev/null || docker exec "$NAME" sh -c 'python3 -c "import socket;print(socket.gethostbyname(\"host.docker.internal\"))"' 2>/dev/null || echo unresolved)" log "container host.docker.internal => $HOST_RES" log "idle 300s interval=5 (no business traffic)" sample_stats "$RAW/idle.tsv" 5 300 "$NAME" log "idle done lines=$(($(wc -l < "$RAW/idle.tsv") - 1))" # session window: 10s before first request through 10s after last SENT="$RAW/session.done" rm -f "$SENT" sample_until_file "$RAW/session.tsv" 1 "$SENT" 10 "$NAME" & STAT_PID=$! sleep 10 : > "$RAW/t3.jsonl" : > "$RAW/t3-engines.jsonl" log "T3 x5 q='best bluetooth earbuds 2026 reddit'" for i in 1 2 3 4 5; do log "T3 trial $i" set +e line="$(bash "$ROOT/request.sh" t3 2>"$RAW/t3-excerpt-$i.json")" rc=$? set -e echo "$line" | tee -a "$RAW/t3.jsonl" if [[ "$i" -le 3 && -s "$RAW/t3-excerpt-$i.json" ]]; then cp "$RAW/t3-excerpt-$i.json" "$SAMP/t3-$i.json" fi log "T3 trial $i rc=$rc" sleep 2 done log "T3 per-engine matrix google/bing/duckduckgo/brave" for eng in google bing duckduckgo brave; do log "engine probe $eng" set +e line="$(bash "$ROOT/request.sh" engine "$eng" 2>"$RAW/t3-engine-$eng.excerpt")" rc=$? set -e echo "$line" | tee -a "$RAW/t3-engines.jsonl" cp "$RAW/t3-engine-$eng.excerpt" "$SAMP/t3-engine-$eng.json" 2>/dev/null || true log "engine $eng rc=$rc" sleep 1 done touch "$SENT" wait "$STAT_PID" || true log "T5 60 concurrent same query" SENT5="$RAW/t5.done" rm -f "$SENT5" sample_until_file "$RAW/t5-stats.tsv" 1 "$SENT5" 15 "$NAME" & STAT5_PID=$! T5_START=$(date +%s) rm -rf "$RAW/t5-parts" mkdir -p "$RAW/t5-parts" # 60 background curls via request.sh (each writes its own file) t5_pids=() for i in $(seq 1 60); do ( set +e bash "$ROOT/request.sh" t5 >"$RAW/t5-parts/$(printf '%02d' "$i").json" 2>/dev/null ) & t5_pids+=($!) done for p in "${t5_pids[@]}"; do wait "$p" || true done : > "$RAW/t5.jsonl" for f in "$RAW/t5-parts"/*.json; do [[ -s "$f" ]] && cat "$f" >> "$RAW/t5.jsonl" done T5_END=$(date +%s) T5_WALL=$((T5_END - T5_START)) log "T5 wall=${T5_WALL}s" touch "$SENT5" wait "$STAT5_PID" || true # first 2 t5 excerpts python3 - "$RAW/t5.jsonl" "$SAMP" <<'PY' import json,sys from pathlib import Path p=Path(sys.argv[1]); d=Path(sys.argv[2]) rows=[] for line in p.read_text(encoding="utf-8").splitlines(): if line.strip(): rows.append(json.loads(line)) for i,r in enumerate(rows[:2],1): (d/f"t5-{i}.json").write_text(json.dumps(r,ensure_ascii=False,indent=2)+"\n",encoding="utf-8") print(f"t5 n={len(rows)} ok={sum(1 for r in rows if r.get('assert_ok'))}") PY OOM="$(docker inspect --format '{{.State.OOMKilled}}' "$NAME")" STATUS="$(docker inspect --format '{{.State.Status}}' "$NAME")" log "container status=$STATUS oom=$OOM" docker logs --tail 40 "$NAME" | tee "$RAW/container-tail.log" || true python3 - "$RAW/image-meta.json" <