Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
52 lines
2.9 KiB
Bash
Executable File
52 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Library: run ONE export in a fresh JVM with a fixed heap and print one result line.
|
|
# measure LABEL URL_PATH XMX [SAVE_TO_FILE]
|
|
# Every call starts its own JVM, so the numbers of one approach never contaminate another.
|
|
# Needs: curl, awk, a built jar (mvn package), Linux (/proc).
|
|
set -u
|
|
JAR="${JAR:-target/excel-export-1.0.0.jar}"
|
|
PORT="${PORT:-$((30000 + RANDOM % 20000))}"
|
|
WORK="${WORK:-/tmp/excel-export-measure}"
|
|
|
|
measure() {
|
|
local label="$1" path="$2" xmx="$3" save="${4:-/dev/null}"
|
|
rm -rf "$WORK"; mkdir -p "$WORK/tmp"
|
|
java -Xms"$xmx" -Xmx"$xmx" -XX:+UseG1GC -Xlog:gc:file="$WORK/gc.log" -XX:OnOutOfMemoryError="touch $WORK/oom" \
|
|
-Djava.io.tmpdir="$WORK/tmp" -jar "$JAR" --server.port="$PORT" > "$WORK/app.log" 2>&1 &
|
|
local pid=$!
|
|
for _ in $(seq 1 400); do [ "$(curl -s "http://localhost:$PORT/probe/ping")" = ok ] && break; sleep 0.5; done
|
|
|
|
# sample the size of the temp directory while the download runs
|
|
( peak=0; while kill -0 "$pid" 2>/dev/null && [ ! -f "$WORK/done" ]; do
|
|
s=$(du -sk "$WORK/tmp" 2>/dev/null | cut -f1); [ "${s:-0}" -gt "$peak" ] && peak=$s; echo "$peak" > "$WORK/tmp.peak"; sleep 0.2
|
|
done ) &
|
|
local sampler=$!
|
|
|
|
local res
|
|
local body="$WORK/body.txt"; [ "$save" = /dev/null ] && save="$body"
|
|
res=$(curl -s --max-time "${MAX_TIME:-300}" -o "$save" -w "%{http_code} %{size_download} %{time_total}" "http://localhost:$PORT$path"; echo " curl_exit=$?")
|
|
touch "$WORK/done"; wait "$sampler" 2>/dev/null || true
|
|
|
|
local http bytes errbody="" secs cexit probe peakUsed rss
|
|
read -r http bytes secs _ <<< "$res"
|
|
cexit=$(echo "$res" | sed 's/.*curl_exit=//')
|
|
probe=$(curl -s --max-time 5 "http://localhost:$PORT/probe" || true)
|
|
peakUsed=$(echo "$probe" | sed -n 's/.*"peakUsedHeapMb":\([0-9]*\).*/\1/p')
|
|
rss=$(awk '/VmHWM/ {printf "%d", $2/1024}' "/proc/$pid/status" 2>/dev/null)
|
|
local tmpafter; tmpafter=$(find "$WORK/tmp" -type f 2>/dev/null | wc -l)
|
|
local oom=no; [ -f "$WORK/oom" ] && oom=YES
|
|
|
|
[ "$http" != 200 ] && [ -f "$body" ] && errbody=" | body: $(head -c 120 "$body" | tr -d '\n')"
|
|
kill "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true
|
|
|
|
# largest heap occupancy right after a collection, number of pauses, longest pause
|
|
local gc
|
|
gc=$(grep -oE '[0-9]+M->[0-9]+M\([0-9]+M\) [0-9.]+ms' "$WORK/gc.log" | tr '>()M-' ' ' | sed 's/ms//' \
|
|
| awk '{ if ($2>a) a=$2; n++; if ($4>p) p=$4 } END { printf "%d %d %.0f", a, n, p }')
|
|
local maxafter pauses maxpause; read -r maxafter pauses maxpause <<< "$gc"
|
|
local tmppeak; tmppeak=$(( $(cat "$WORK/tmp.peak" 2>/dev/null || echo 0) / 1024 ))
|
|
|
|
printf '%-7s %-34s Xmx=%-5s http=%-3s curl=%-2s bytes=%-10s %6.1fs | peak used heap %5s MB | max after GC %5s MB | %4s GC pauses, longest %4s ms | peak RSS %5s MB | temp dir peak %4s MB, files left %s | OOM=%s%s\n' \
|
|
"$label" "$path" "$xmx" "$http" "$cexit" "$bytes" "$secs" "${peakUsed:--}" "$maxafter" "$pauses" "$maxpause" "${rss:--}" "$tmppeak" "$tmpafter" "$oom" "$errbody"
|
|
}
|