mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 15:46:53 +00:00
f566b382a0
Add a reusable offline rebuild path for pinned paired ABBA benchmark evidence so corrected summaries can be regenerated from raw warp logs after parser fixes. Record ACK-contract comparability explicitly in the rebuilt product comparison output, preventing relaxed RustFS ACK results from being treated as direct strict MinIO comparisons. Co-authored-by: heihutu <heihutu@gmail.com>
271 lines
12 KiB
Bash
Executable File
271 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
ENHANCED_BENCH="${PROJECT_ROOT}/scripts/run_object_batch_bench_enhanced.sh"
|
|
|
|
SOURCE_DIR=""
|
|
OUT_DIR=""
|
|
ARCHIVE_PATH=""
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage:
|
|
scripts/rebuild_pinned_paired_abba_artifact.sh --source-dir <paired-abba-dir> --out-dir <corrected-dir> [--archive <tar.gz>]
|
|
|
|
Rebuilds #1432 corrected CSVs from a pinned paired ABBA run's raw logs. The output records ACK contract comparability explicitly so RustFS relaxed ACK and MinIO strict ACK results are not treated as directly comparable.
|
|
USAGE
|
|
}
|
|
|
|
die() {
|
|
echo "ERROR: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--source-dir) SOURCE_DIR="$2"; shift 2 ;;
|
|
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
|
--archive) ARCHIVE_PATH="$2"; shift 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "unknown arg: $1" ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
read_env_value() {
|
|
local file="$1"
|
|
local key="$2"
|
|
awk -F= -v key="$key" '$1 == key {print substr($0, length(key) + 2); exit}' "$file" | sed -E "s/^'//; s/'$//"
|
|
}
|
|
|
|
resolve_log_path() {
|
|
local log_path="$1"
|
|
if [[ -f "$log_path" ]]; then
|
|
printf '%s\n' "$log_path"
|
|
return
|
|
fi
|
|
|
|
local relative_path="${log_path#"$SOURCE_DIR"/}"
|
|
if [[ -f "$SOURCE_DIR/$relative_path" ]]; then
|
|
printf '%s\n' "$SOURCE_DIR/$relative_path"
|
|
return
|
|
fi
|
|
|
|
printf '%s\n' "$log_path"
|
|
}
|
|
|
|
median_from_file() {
|
|
local file="$1"
|
|
if [[ ! -s "$file" ]]; then
|
|
echo "N/A"
|
|
return
|
|
fi
|
|
|
|
sort -n "$file" | awk '
|
|
{ values[++n] = $1 }
|
|
END {
|
|
if (n == 0) {
|
|
print "N/A"
|
|
} else if (n % 2 == 1) {
|
|
printf "%.6f\n", values[(n + 1) / 2]
|
|
} else {
|
|
printf "%.6f\n", (values[n / 2] + values[n / 2 + 1]) / 2
|
|
}
|
|
}'
|
|
}
|
|
|
|
pct_delta() {
|
|
local new_value="$1"
|
|
local old_value="$2"
|
|
if [[ "$new_value" == "N/A" || "$old_value" == "N/A" ]]; then
|
|
echo "N/A"
|
|
return
|
|
fi
|
|
awk -v n="$new_value" -v o="$old_value" 'BEGIN { if (o == 0) print "N/A"; else printf "%.2f\n", ((n - o) / o) * 100 }'
|
|
}
|
|
|
|
validate_inputs() {
|
|
[[ -n "$SOURCE_DIR" ]] || die "--source-dir is required"
|
|
[[ -n "$OUT_DIR" ]] || die "--out-dir is required"
|
|
[[ -d "$SOURCE_DIR" ]] || die "--source-dir does not exist: $SOURCE_DIR"
|
|
[[ -f "$SOURCE_DIR/paired_manifest.env" ]] || die "missing paired_manifest.env under $SOURCE_DIR"
|
|
[[ -f "$SOURCE_DIR/abba_schedule.csv" ]] || die "missing abba_schedule.csv under $SOURCE_DIR"
|
|
[[ -x "$ENHANCED_BENCH" ]] || die "missing executable enhanced bench helper: $ENHANCED_BENCH"
|
|
}
|
|
|
|
write_corrected_rows() {
|
|
local round_csv="$OUT_DIR/corrected_round_results.csv"
|
|
echo "leg,product,ack_contract,concurrency,size,tool,round,attempt,status,exit_code,throughput_human,throughput_bps,reqps,latency_human,latency_ms,req_p90_human,req_p90_ms,req_p99_human,req_p99_ms,log_file" > "$round_csv"
|
|
|
|
while IFS=',' read -r leg product _ _ _ _ ack_contract leg_out_dir; do
|
|
[[ "$leg" != "leg" ]] || continue
|
|
local relative_leg_dir="${leg_out_dir#"$SOURCE_DIR"/}"
|
|
local actual_leg_dir="$SOURCE_DIR/$relative_leg_dir"
|
|
[[ -d "$actual_leg_dir" ]] || actual_leg_dir="$leg_out_dir"
|
|
[[ -d "$actual_leg_dir" ]] || die "missing leg directory for $leg: $leg_out_dir"
|
|
|
|
local cell_dir
|
|
while IFS= read -r cell_dir; do
|
|
local source_round_csv="$cell_dir/round_results.csv"
|
|
[[ -f "$source_round_csv" ]] || continue
|
|
tail -n +2 "$source_round_csv" | while IFS=',' read -r size tool round attempt concurrency status exit_code _ _ _ _ _ _ _ log_file _ _ _ _; do
|
|
[[ -n "$size" ]] || continue
|
|
local resolved_log metrics throughput_human throughput_bps reqps latency_human latency_ms req_p90_human req_p90_ms req_p99_human req_p99_ms
|
|
resolved_log="$(resolve_log_path "$log_file")"
|
|
if [[ "$status" == "ok" && -f "$resolved_log" ]]; then
|
|
metrics="$("$ENHANCED_BENCH" --extract-metrics-from-log "$resolved_log" | tail -n 1)"
|
|
throughput_human="$(echo "$metrics" | cut -d',' -f1)"
|
|
throughput_bps="$(echo "$metrics" | cut -d',' -f2)"
|
|
reqps="$(echo "$metrics" | cut -d',' -f3)"
|
|
latency_human="$(echo "$metrics" | cut -d',' -f4)"
|
|
latency_ms="$(echo "$metrics" | cut -d',' -f5)"
|
|
req_p90_human="$(echo "$metrics" | cut -d',' -f6)"
|
|
req_p90_ms="$(echo "$metrics" | cut -d',' -f7)"
|
|
req_p99_human="$(echo "$metrics" | cut -d',' -f8)"
|
|
req_p99_ms="$(echo "$metrics" | cut -d',' -f9)"
|
|
else
|
|
throughput_human="N/A"
|
|
throughput_bps="N/A"
|
|
reqps="N/A"
|
|
latency_human="N/A"
|
|
latency_ms="N/A"
|
|
req_p90_human="N/A"
|
|
req_p90_ms="N/A"
|
|
req_p99_human="N/A"
|
|
req_p99_ms="N/A"
|
|
fi
|
|
echo "$leg,$product,$ack_contract,$concurrency,$size,$tool,$round,$attempt,$status,$exit_code,$throughput_human,$throughput_bps,$reqps,$latency_human,$latency_ms,$req_p90_human,$req_p90_ms,$req_p99_human,$req_p99_ms,$resolved_log" >> "$round_csv"
|
|
done
|
|
done < <(find "$actual_leg_dir" -maxdepth 1 -type d -name 'c*' | sort)
|
|
done < "$SOURCE_DIR/abba_schedule.csv"
|
|
}
|
|
|
|
write_summary_rows() {
|
|
local round_csv="$OUT_DIR/corrected_round_results.csv"
|
|
local summary_csv="$OUT_DIR/corrected_ack_contract_summary.csv"
|
|
local tmp_dir="$OUT_DIR/.summary-tmp"
|
|
mkdir -p "$tmp_dir"
|
|
|
|
echo "product,ack_contract,concurrency,size,ok_rounds,failed_rounds,median_throughput_bps,median_reqps,median_latency_ms,median_req_p90_ms,median_req_p99_ms" > "$summary_csv"
|
|
|
|
tail -n +2 "$round_csv" | awk -F',' '{print $2","$3","$4","$5}' | sort -u | while IFS=',' read -r product ack_contract concurrency size; do
|
|
local safe_key ok_rounds failed_rounds throughput_values reqps_values latency_values p90_values p99_values
|
|
safe_key="$(printf '%s_%s_%s_%s' "$product" "$ack_contract" "$concurrency" "$size" | tr -c '[:alnum:]_.-' '_')"
|
|
throughput_values="$tmp_dir/${safe_key}.throughput"
|
|
reqps_values="$tmp_dir/${safe_key}.reqps"
|
|
latency_values="$tmp_dir/${safe_key}.latency"
|
|
p90_values="$tmp_dir/${safe_key}.p90"
|
|
p99_values="$tmp_dir/${safe_key}.p99"
|
|
: > "$throughput_values"
|
|
: > "$reqps_values"
|
|
: > "$latency_values"
|
|
: > "$p90_values"
|
|
: > "$p99_values"
|
|
|
|
ok_rounds="$(awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" {count++} END{print count+0}' "$round_csv")"
|
|
failed_rounds="$(awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9!="ok" {count++} END{print count+0}' "$round_csv")"
|
|
awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" && $12!="N/A" {print $12}' "$round_csv" > "$throughput_values"
|
|
awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" && $13!="N/A" {print $13}' "$round_csv" > "$reqps_values"
|
|
awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" && $15!="N/A" {print $15}' "$round_csv" > "$latency_values"
|
|
awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" && $17!="N/A" {print $17}' "$round_csv" > "$p90_values"
|
|
awk -F',' -v p="$product" -v a="$ack_contract" -v c="$concurrency" -v s="$size" 'NR>1 && $2==p && $3==a && $4==c && $5==s && $9=="ok" && $19!="N/A" {print $19}' "$round_csv" > "$p99_values"
|
|
|
|
echo "$product,$ack_contract,$concurrency,$size,$ok_rounds,$failed_rounds,$(median_from_file "$throughput_values"),$(median_from_file "$reqps_values"),$(median_from_file "$latency_values"),$(median_from_file "$p90_values"),$(median_from_file "$p99_values")" >> "$summary_csv"
|
|
done
|
|
}
|
|
|
|
write_comparison_rows() {
|
|
local summary_csv="$OUT_DIR/corrected_ack_contract_summary.csv"
|
|
local comparison_csv="$OUT_DIR/corrected_product_comparison.csv"
|
|
echo "ack_comparable,rustfs_ack_contract,minio_ack_contract,concurrency,size,rustfs_median_throughput_bps,minio_median_throughput_bps,delta_throughput_pct,rustfs_median_reqps,minio_median_reqps,delta_reqps_pct" > "$comparison_csv"
|
|
|
|
awk -F',' 'NR>1 {print $3","$4}' "$summary_csv" | sort -u | while IFS=',' read -r concurrency size; do
|
|
local rustfs_rows minio_rows
|
|
rustfs_rows="$(awk -F',' -v c="$concurrency" -v s="$size" 'NR>1 && $1=="rustfs" && $3==c && $4==s {print}' "$summary_csv")"
|
|
minio_rows="$(awk -F',' -v c="$concurrency" -v s="$size" 'NR>1 && $1=="minio" && $3==c && $4==s {print}' "$summary_csv")"
|
|
[[ -n "$rustfs_rows" && -n "$minio_rows" ]] || continue
|
|
while IFS=',' read -r _ rustfs_ack _ _ _ _ rustfs_thr rustfs_reqps _ _ _; do
|
|
while IFS=',' read -r _ minio_ack _ _ _ _ minio_thr minio_reqps _ _ _; do
|
|
local comparable delta_thr delta_reqps
|
|
if [[ "$rustfs_ack" == "$minio_ack" ]]; then
|
|
comparable="true"
|
|
else
|
|
comparable="false"
|
|
fi
|
|
delta_thr="$(pct_delta "$rustfs_thr" "$minio_thr")"
|
|
delta_reqps="$(pct_delta "$rustfs_reqps" "$minio_reqps")"
|
|
echo "$comparable,$rustfs_ack,$minio_ack,$concurrency,$size,$rustfs_thr,$minio_thr,$delta_thr,$rustfs_reqps,$minio_reqps,$delta_reqps" >> "$comparison_csv"
|
|
done <<< "$minio_rows"
|
|
done <<< "$rustfs_rows"
|
|
done
|
|
}
|
|
|
|
write_manifest_and_checksums() {
|
|
cp "$SOURCE_DIR/paired_manifest.env" "$OUT_DIR/source_paired_manifest.env"
|
|
cp "$SOURCE_DIR/abba_schedule.csv" "$OUT_DIR/source_abba_schedule.csv"
|
|
|
|
local manifest="$OUT_DIR/corrected_artifact_manifest.env"
|
|
{
|
|
printf 'created_utc=%q\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
printf 'benchmark_issue=%q\n' "rustfs/backlog#1432"
|
|
printf 'source_dir=%q\n' "$SOURCE_DIR"
|
|
printf 'parser_revision=%q\n' "$(git -C "$PROJECT_ROOT" rev-parse HEAD)"
|
|
printf 'artifact_format=%q\n' "corrected-pinned-paired-abba-v1"
|
|
printf 'ack_comparability_rule=%q\n' "Only rows with ack_comparable=true are direct RustFS/MinIO ACK-contract comparisons."
|
|
printf 'source_rustfs_ack_contract=%q\n' "$(read_env_value "$SOURCE_DIR/paired_manifest.env" rustfs_ack_contract)"
|
|
printf 'source_minio_ack_contract=%q\n' "$(read_env_value "$SOURCE_DIR/paired_manifest.env" minio_ack_contract)"
|
|
} > "$manifest"
|
|
|
|
(
|
|
cd "$OUT_DIR"
|
|
find . -type f ! -name sha256sums.txt | LC_ALL=C sort | while IFS= read -r file; do
|
|
shasum -a 256 "${file#./}"
|
|
done > sha256sums.txt
|
|
)
|
|
}
|
|
|
|
copy_source_evidence() {
|
|
local source_evidence_dir="$OUT_DIR/source_evidence"
|
|
mkdir -p "$source_evidence_dir"
|
|
|
|
find "$SOURCE_DIR" \( -path '*/logs/*' -o -name round_results.csv \) -type f | while IFS= read -r source_file; do
|
|
local relative_path target_file
|
|
relative_path="${source_file#"$SOURCE_DIR"/}"
|
|
target_file="$source_evidence_dir/$relative_path"
|
|
mkdir -p "$(dirname "$target_file")"
|
|
cp "$source_file" "$target_file"
|
|
done
|
|
}
|
|
|
|
create_archive() {
|
|
[[ -n "$ARCHIVE_PATH" ]] || return
|
|
local archive_dir archive_name
|
|
archive_dir="$(dirname "$ARCHIVE_PATH")"
|
|
archive_name="$(basename "$ARCHIVE_PATH")"
|
|
mkdir -p "$archive_dir"
|
|
tar -C "$OUT_DIR" -czf "$archive_dir/$archive_name" .
|
|
shasum -a 256 "$archive_dir/$archive_name" > "$archive_dir/$archive_name.sha256"
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
validate_inputs
|
|
mkdir -p "$OUT_DIR"
|
|
write_corrected_rows
|
|
write_summary_rows
|
|
write_comparison_rows
|
|
rm -rf "$OUT_DIR/.summary-tmp"
|
|
copy_source_evidence
|
|
write_manifest_and_checksums
|
|
create_archive
|
|
echo "corrected artifact root: $OUT_DIR"
|
|
if [[ -n "$ARCHIVE_PATH" ]]; then
|
|
echo "corrected artifact archive: $ARCHIVE_PATH"
|
|
fi
|
|
}
|
|
|
|
main "$@"
|