From 47d05a1d6ad97a4662386e9a5768666bead612ea Mon Sep 17 00:00:00 2001 From: houseme Date: Sat, 27 Jun 2026 22:23:09 +0800 Subject: [PATCH] feat(bench): harden cooled get ab harness (#3960) --- scripts/run_get_codec_streaming_smoke.sh | 275 +++++++++++++++++++-- scripts/run_object_batch_bench_enhanced.sh | 54 ++-- 2 files changed, 298 insertions(+), 31 deletions(-) diff --git a/scripts/run_get_codec_streaming_smoke.sh b/scripts/run_get_codec_streaming_smoke.sh index a552e9606..15d65476f 100755 --- a/scripts/run_get_codec_streaming_smoke.sh +++ b/scripts/run_get_codec_streaming_smoke.sh @@ -18,14 +18,16 @@ SECRET_KEY="rustfsadmin" BUCKET="rustfs-get-codec-smoke" REGION="us-east-1" SIZES="1MiB,4MiB,10MiB" -CONCURRENCY=32 -DURATION="15s" +CONCURRENCY=16 +DURATION="30s" ROUNDS=3 RETRY_PER_ROUND=1 -ROUND_COOLDOWN_SECS=0 +ROUND_COOLDOWN_SECS=20 MODE="both" CODEC_ENGINES="legacy" CODEC_MAX_INFLIGHT=1 +METADATA_EARLY_STOP="off" +SHARD_LOCALITY_PREFERENCE="off" OUTPUT_HANDOFF_ATTRIBUTION=false OUT_DIR="" RUSTFS_BIN="${PROJECT_ROOT}/target/release/rustfs" @@ -38,6 +40,8 @@ COMPAT_OBJECT_KEY="__rustfs_get_v2_pr24_compat/object.bin" COMPAT_OBJECT_SIZE=65536 DRY_RUN=false SKIP_BUILD=false +PROFILE_FAILURES=0 +declare -a ORIGINAL_ARGS=() SERVER_PID="" @@ -53,16 +57,19 @@ Purpose: Core options: --mode Which profile(s) to run (default: both) --codec-engine Codec engine(s) for codec profiles (default: legacy) + --metadata-early-stop Enable metadata early-stop observe/opt-in env (default: off) + --shard-locality-preference + Enable shard locality preference env (default: off) --codec-max-inflight RUSTFS_GET_CODEC_STREAMING_MAX_INFLIGHT (default: 1) --handoff-attribution Enable output handoff attribution metrics --address RustFS listen address (default: 127.0.0.1:19030) --bucket Benchmark bucket (default: rustfs-get-codec-smoke) --sizes Object sizes (default: 1MiB,4MiB,10MiB) - --concurrency warp concurrency (default: 32) - --duration warp duration per round (default: 15s) + --concurrency warp concurrency (default: 16) + --duration warp duration per round (default: 30s) --rounds rounds per size (default: 3) --retry-per-round failed-attempt retries per round (default: 1) - --round-cooldown-secs cooldown seconds after each completed round (default: 0) + --round-cooldown-secs cooldown seconds after each completed round (default: 20) --out-dir output directory (default: target/bench/get-codec-streaming-) Binary/options: @@ -81,6 +88,9 @@ Credentials: --region S3 region (default: us-east-1) Output: + /environment.txt + /manifest.env + /baseline_compare.csv /legacy/warp/median_summary.csv /codec-legacy/warp/median_summary.csv /codec-rustfs/warp/median_summary.csv @@ -99,6 +109,7 @@ Output: //compat/response_headers.json //compat/body_sha256.txt //rustfs.log + //warp/logs/*.log Example: scripts/run_get_codec_streaming_smoke.sh \ @@ -142,6 +153,8 @@ parse_args() { case "$1" in --mode) MODE="$2"; shift 2 ;; --codec-engine) CODEC_ENGINES="$2"; shift 2 ;; + --metadata-early-stop) METADATA_EARLY_STOP="$2"; shift 2 ;; + --shard-locality-preference) SHARD_LOCALITY_PREFERENCE="$2"; shift 2 ;; --codec-max-inflight) CODEC_MAX_INFLIGHT="$2"; shift 2 ;; --handoff-attribution) OUTPUT_HANDOFF_ATTRIBUTION=true; shift ;; --address) ADDRESS="$2"; shift 2 ;; @@ -179,6 +192,16 @@ validate_args() { *) die "--mode must be legacy, codec, or both" ;; esac + case "$METADATA_EARLY_STOP" in + on|off) ;; + *) die "--metadata-early-stop must be on or off" ;; + esac + + case "$SHARD_LOCALITY_PREFERENCE" in + on|off) ;; + *) die "--shard-locality-preference must be on or off" ;; + esac + local raw engine IFS=',' read -r -a engines <<< "$CODEC_ENGINES" [[ "${#engines[@]}" -gt 0 ]] || die "--codec-engine must not be empty" @@ -222,6 +245,111 @@ setup_output() { mkdir -p "$OUT_DIR" } +bool_from_on_off() { + case "$1" in + on) echo "true" ;; + off) echo "false" ;; + *) die "expected on/off value, got: $1" ;; + esac +} + +command_line_string() { + printf '%q ' "${BASH_SOURCE[0]}" "${ORIGINAL_ARGS[@]}" +} + +detect_cpu_summary() { + if command -v sysctl >/dev/null 2>&1; then + local brand logical + brand="$(sysctl -n machdep.cpu.brand_string 2>/dev/null || true)" + logical="$(sysctl -n hw.logicalcpu 2>/dev/null || true)" + if [[ -n "$brand" && -n "$logical" ]]; then + printf '%s (%s logical cores)\n' "$brand" "$logical" + return + fi + fi + + if command -v lscpu >/dev/null 2>&1; then + lscpu 2>/dev/null | awk -F: ' + /^Model name:/ { model=$2 } + /^CPU\(s\):/ { cpus=$2 } + END { + gsub(/^[ \t]+|[ \t]+$/, "", model) + gsub(/^[ \t]+|[ \t]+$/, "", cpus) + if (model != "" && cpus != "") { + printf "%s (%s logical cores)\n", model, cpus + } else if (model != "") { + printf "%s\n", model + } + } + ' + return + fi + + echo "unknown" +} + +write_root_environment() { + local branch git_head git_dirty_count rustc_version cargo_version command_line + branch="$(git -C "$PROJECT_ROOT" rev-parse --abbrev-ref HEAD)" + git_head="$(git -C "$PROJECT_ROOT" rev-parse HEAD)" + git_dirty_count="$(git -C "$PROJECT_ROOT" status --porcelain | awk 'END { print NR + 0 }')" + rustc_version="$(rustc --version 2>/dev/null || echo unavailable)" + cargo_version="$(cargo --version 2>/dev/null || echo unavailable)" + command_line="$(command_line_string)" + + cat >"${OUT_DIR}/environment.txt" </dev/null || uname -a) +cpu=$(detect_cpu_summary) +command_line=${command_line% } +EOF +} + +write_root_manifest() { + local profiles_csv="$1" + local branch git_head git_dirty_count command_line + branch="$(git -C "$PROJECT_ROOT" rev-parse --abbrev-ref HEAD)" + git_head="$(git -C "$PROJECT_ROOT" rev-parse HEAD)" + git_dirty_count="$(git -C "$PROJECT_ROOT" status --porcelain | awk 'END { print NR + 0 }')" + command_line="$(command_line_string)" + + cat >"${OUT_DIR}/manifest.env" < "$ROUND_CSV" + echo "size,tool,round,attempt,concurrency,status,exit_code,round_started_at_utc,round_finished_at_utc,throughput_human,throughput_bps,reqps,latency_human,latency_ms,log_file" > "$ROUND_CSV" echo "size,tool,concurrency,successful_rounds,failed_rounds,median_throughput_bps,median_reqps,median_latency_ms" > "$MEDIAN_CSV" } @@ -342,6 +343,9 @@ run_one_attempt() { local attempt="$3" local log_file="$OUT_DIR/logs/${TOOL}_${size}_r${round}_a${attempt}.log" local status="ok" + local exit_code=0 + local started_at_utc finished_at_utc + started_at_utc="$(date -u +%Y-%m-%dT%H:%M:%SZ)" if [[ "$TOOL" == "warp" ]]; then local warp_host @@ -369,7 +373,10 @@ run_one_attempt() { printf '\n' echo "dry run" > "$log_file" else - if ! "${cmd[@]}" 2>&1 | tee "$log_file" >&2; then + if "${cmd[@]}" 2>&1 | tee "$log_file" >&2; then + : + else + exit_code=$? status="failed" fi fi @@ -397,27 +404,40 @@ run_one_attempt() { printf '\n' echo "dry run" > "$log_file" else - if ! "${cmd[@]}" 2>&1 | tee "$log_file" >&2; then + if "${cmd[@]}" 2>&1 | tee "$log_file" >&2; then + : + else + exit_code=$? status="failed" fi fi fi + finished_at_utc="$(date -u +%Y-%m-%dT%H:%M:%SZ)" local metrics throughput_human reqps latency_human throughput_bps latency_ms - metrics="$(extract_metrics "$log_file")" - throughput_human="$(echo "$metrics" | cut -d',' -f1)" - reqps="$(echo "$metrics" | cut -d',' -f2)" - latency_human="$(echo "$metrics" | cut -d',' -f3)" - throughput_bps="$(to_bps "$throughput_human")" - latency_ms="$(to_ms "$latency_human")" + if [[ "$DRY_RUN" == "true" ]]; then + throughput_human="N/A" + reqps="N/A" + latency_human="N/A" + throughput_bps="N/A" + latency_ms="N/A" + else + metrics="$(extract_metrics "$log_file")" + throughput_human="$(echo "$metrics" | cut -d',' -f1)" + reqps="$(echo "$metrics" | cut -d',' -f2)" + latency_human="$(echo "$metrics" | cut -d',' -f3)" + throughput_bps="$(to_bps "$throughput_human")" + latency_ms="$(to_ms "$latency_human")" + fi if [[ "$DRY_RUN" != "true" && "$status" == "ok" ]]; then if [[ "$throughput_bps" == "N/A" && "$reqps" == "N/A" ]]; then status="failed" + exit_code=1 fi fi - echo "$size,$TOOL,$round,$attempt,$CONCURRENCY,$status,$throughput_human,$throughput_bps,$reqps,$latency_human,$latency_ms,$log_file" >> "$ROUND_CSV" + echo "$size,$TOOL,$round,$attempt,$CONCURRENCY,$status,$exit_code,$started_at_utc,$finished_at_utc,$throughput_human,$throughput_bps,$reqps,$latency_human,$latency_ms,$log_file" >> "$ROUND_CSV" echo "$status" } @@ -437,12 +457,13 @@ run_size() { fi if (( attempt < RETRY_PER_ROUND+1 )); then echo "Round failed, retry in ${RETRY_SLEEP_SECS}s..." - sleep "$RETRY_SLEEP_SECS" + cooldown_sleep "$RETRY_SLEEP_SECS" fi done if [[ "$success" == "no" ]]; then echo "WARN: size=$size round=$round failed after retries." + FAILED_FINAL_ROUNDS=$((FAILED_FINAL_ROUNDS + 1)) fi if (( COOLDOWN_SECS > 0 && round < ROUNDS )); then @@ -464,9 +485,9 @@ build_median_summary() { ok_rounds="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" {c++} END{print c+0}' "$ROUND_CSV")" fail_rounds="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6!="ok" {c++} END{print c+0}' "$ROUND_CSV")" - t_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $8!="N/A" {print $8}' "$ROUND_CSV")" - r_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $9!="N/A" {print $9}' "$ROUND_CSV")" - l_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $11!="N/A" {print $11}' "$ROUND_CSV")" + t_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $11!="N/A" {print $11}' "$ROUND_CSV")" + r_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $12!="N/A" {print $12}' "$ROUND_CSV")" + l_vals="$(awk -F',' -v s="$size" 'NR>1 && $1==s && $6=="ok" && $14!="N/A" {print $14}' "$ROUND_CSV")" local m_t m_r m_l m_t="$(median_from_numbers "$t_vals")" @@ -559,6 +580,11 @@ main() { echo "=== Baseline Compare ===" cat "$COMPARE_CSV" fi + + if [[ "$DRY_RUN" != "true" && "$FAILED_FINAL_ROUNDS" -gt 0 ]]; then + echo "ERROR: ${FAILED_FINAL_ROUNDS} benchmark rounds failed after retries." >&2 + exit 1 + fi } main "$@"