mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
431 lines
14 KiB
Bash
Executable File
431 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# rustfs/backlog#708 tuning matrix runner for large-object PUT.
|
|
# This controller:
|
|
# - defines high-priority tuning profiles for 16MiB / 32MiB PUT
|
|
# - writes an env snapshot per profile
|
|
# - optionally calls an apply hook after switching profile env
|
|
# - reuses scripts/run_put_large_stage_breakdown_with_capture.sh for each profile
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
RUNNER_SCRIPT="${SCRIPT_DIR}/run_put_large_stage_breakdown_with_capture.sh"
|
|
|
|
GROUP="all" # all|baseline|inflight|io-buffer|runtime|duplex
|
|
ENDPOINT=""
|
|
ACCESS_KEY=""
|
|
SECRET_KEY=""
|
|
REGION="us-east-1"
|
|
BUCKET_PREFIX="rustfs-put-large"
|
|
CONCURRENCIES="16,32,64,96,128"
|
|
SIZES="16MiB,32MiB"
|
|
DURATION="120s"
|
|
ROUNDS=3
|
|
OUT_ROOT=""
|
|
WARP_BIN="warp"
|
|
EXTRA_ARGS=""
|
|
INSECURE=false
|
|
DRY_RUN=false
|
|
COOLDOWN_SECS=30
|
|
|
|
TOPOLOGY_NODES=""
|
|
TOPOLOGY_DISKS_PER_NODE=""
|
|
TOPOLOGY_TOTAL_DISKS=""
|
|
TOPOLOGY_CPU_PER_NODE=""
|
|
TOPOLOGY_MEM_PER_NODE=""
|
|
TOPOLOGY_NETWORK=""
|
|
TOPOLOGY_ENDPOINT_MODE=""
|
|
TOPOLOGY_ERASURE_SET_DRIVE_COUNT=""
|
|
CLIENT_HOST=""
|
|
|
|
CAPTURE_METRICS_ENDPOINTS=""
|
|
CAPTURE_PROM_METRICS_URLS=""
|
|
CAPTURE_INTERVAL_SECS=15
|
|
CAPTURE_RUSTFS_PID=""
|
|
CAPTURE_SKIP_HOST_TELEMETRY=false
|
|
SKIP_CAPTURE=false
|
|
|
|
APPLY_CMD=""
|
|
APPLY_CMD_ARR=()
|
|
APPLY_WAIT_SECS=20
|
|
|
|
BASE_OBJECT_IO_BUFFER_SIZE=262144
|
|
BASE_OBJECT_DUPLEX_BUFFER_SIZE=8388608
|
|
BASE_ERASURE_ENCODE_MAX_INFLIGHT_BYTES=25165824
|
|
BASE_RUNTIME_WORKER_THREADS=12
|
|
BASE_RUNTIME_MAX_BLOCKING_THREADS=512
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage:
|
|
scripts/run_put_large_tuning_matrix.sh --endpoint <url> \
|
|
--access-key <ak> --secret-key <sk> [options]
|
|
|
|
Required:
|
|
--endpoint <url>
|
|
--access-key <ak>
|
|
--secret-key <sk>
|
|
|
|
Core options:
|
|
--group <name> all|baseline|inflight|io-buffer|runtime|duplex
|
|
--bucket-prefix <prefix> Default: rustfs-put-large
|
|
--region <name> Default: us-east-1
|
|
--concurrencies <csv> Default: 16,32,64,96,128
|
|
--sizes <csv> Default: 16MiB,32MiB
|
|
--duration <dur> Default: 120s
|
|
--rounds <n> Default: 3
|
|
--out-root <dir> Default: target/bench/put-large-tuning-<timestamp>
|
|
--warp-bin <path> Default: warp
|
|
--extra-args "<args>"
|
|
--cooldown-secs <n> Sleep between profiles (default: 30)
|
|
--insecure
|
|
--dry-run
|
|
|
|
Topology metadata:
|
|
--nodes <n>
|
|
--disks-per-node <n>
|
|
--total-disks <n>
|
|
--cpu-per-node <text>
|
|
--mem-per-node <text>
|
|
--network <text>
|
|
--endpoint-mode <direct|lb>
|
|
--erasure-set-drive-count <n>
|
|
--client-host <text>
|
|
|
|
Capture options:
|
|
--capture-metrics-endpoints <csv>
|
|
--capture-prom-metrics-urls <csv>
|
|
--capture-interval-secs <n> Default: 15
|
|
--capture-rustfs-pid <pid>
|
|
--capture-skip-host-telemetry
|
|
--skip-capture Reuse benchmark runner without starting capture
|
|
|
|
Optional apply hook:
|
|
--apply-cmd "<cmd>" Optional plain command + args, no shell operators.
|
|
Useful for restarting/reloading RustFS after env changes.
|
|
--apply-wait-secs <n> Wait after apply command (default: 20)
|
|
|
|
Behavior:
|
|
- Each profile writes: <out-root>/<profile>/env_snapshot.env
|
|
- Each profile then calls scripts/run_put_large_stage_breakdown_with_capture.sh
|
|
- Non-baseline profiles automatically compare against <out-root>/baseline
|
|
USAGE
|
|
}
|
|
|
|
require_cmd() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "ERROR: command not found: $1" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
arg_value() {
|
|
local flag="$1"
|
|
local value="${2:-}"
|
|
if [[ -z "$value" || "$value" == --* ]]; then
|
|
echo "ERROR: missing value for $flag" >&2
|
|
exit 1
|
|
fi
|
|
printf '%s\n' "$value"
|
|
}
|
|
|
|
parse_apply_cmd() {
|
|
local raw="$1"
|
|
if [[ "$raw" == *';'* || "$raw" == *'&&'* || "$raw" == *'||'* || "$raw" == *'|'* || "$raw" == *'<'* || "$raw" == *'>'* || "$raw" == *'`'* || "$raw" == *'$'* ]]; then
|
|
echo "ERROR: --apply-cmd does not allow shell operators or expansions; pass a plain command and args only" >&2
|
|
exit 1
|
|
fi
|
|
IFS=$' \t\n' read -r -a APPLY_CMD_ARR <<< "$raw"
|
|
if [[ "${#APPLY_CMD_ARR[@]}" -eq 0 ]]; then
|
|
echo "ERROR: --apply-cmd must not be empty" >&2
|
|
exit 1
|
|
fi
|
|
require_cmd "${APPLY_CMD_ARR[0]}"
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--endpoint) ENDPOINT="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--access-key) ACCESS_KEY="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--secret-key) SECRET_KEY="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--group) GROUP="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--bucket-prefix) BUCKET_PREFIX="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--region) REGION="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--concurrencies) CONCURRENCIES="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--sizes) SIZES="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--duration) DURATION="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--rounds) ROUNDS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--out-root) OUT_ROOT="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--warp-bin) WARP_BIN="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--extra-args) EXTRA_ARGS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--cooldown-secs) COOLDOWN_SECS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--nodes) TOPOLOGY_NODES="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--disks-per-node) TOPOLOGY_DISKS_PER_NODE="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--total-disks) TOPOLOGY_TOTAL_DISKS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--cpu-per-node) TOPOLOGY_CPU_PER_NODE="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--mem-per-node) TOPOLOGY_MEM_PER_NODE="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--network) TOPOLOGY_NETWORK="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--endpoint-mode) TOPOLOGY_ENDPOINT_MODE="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--erasure-set-drive-count) TOPOLOGY_ERASURE_SET_DRIVE_COUNT="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--client-host) CLIENT_HOST="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--capture-metrics-endpoints) CAPTURE_METRICS_ENDPOINTS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--capture-prom-metrics-urls) CAPTURE_PROM_METRICS_URLS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--capture-interval-secs) CAPTURE_INTERVAL_SECS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--capture-rustfs-pid) CAPTURE_RUSTFS_PID="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--capture-skip-host-telemetry) CAPTURE_SKIP_HOST_TELEMETRY=true; shift ;;
|
|
--skip-capture) SKIP_CAPTURE=true; shift ;;
|
|
--apply-cmd) APPLY_CMD="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--apply-wait-secs) APPLY_WAIT_SECS="$(arg_value "$1" "${2:-}")"; shift 2 ;;
|
|
--insecure) INSECURE=true; shift ;;
|
|
--dry-run) DRY_RUN=true; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*)
|
|
echo "ERROR: unknown arg: $1" >&2
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
is_positive_int() {
|
|
[[ "$1" =~ ^[1-9][0-9]*$ ]]
|
|
}
|
|
|
|
validate_args() {
|
|
if [[ -z "$ENDPOINT" || -z "$ACCESS_KEY" || -z "$SECRET_KEY" ]]; then
|
|
echo "ERROR: --endpoint, --access-key, and --secret-key are required" >&2
|
|
exit 1
|
|
fi
|
|
case "$GROUP" in
|
|
all|baseline|inflight|io-buffer|runtime|duplex) ;;
|
|
*) echo "ERROR: --group must be all|baseline|inflight|io-buffer|runtime|duplex" >&2; exit 1 ;;
|
|
esac
|
|
if ! is_positive_int "$ROUNDS" || ! is_positive_int "$CAPTURE_INTERVAL_SECS" || ! is_positive_int "$APPLY_WAIT_SECS" || ! [[ "$COOLDOWN_SECS" =~ ^[0-9]+$ ]]; then
|
|
echo "ERROR: --rounds, --capture-interval-secs, and --apply-wait-secs must be positive integers; --cooldown-secs must be a nonnegative integer" >&2
|
|
exit 1
|
|
fi
|
|
if [[ -n "$APPLY_CMD" ]]; then
|
|
parse_apply_cmd "$APPLY_CMD"
|
|
fi
|
|
}
|
|
|
|
setup_out_root() {
|
|
if [[ -z "$OUT_ROOT" ]]; then
|
|
OUT_ROOT="target/bench/put-large-tuning-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
fi
|
|
mkdir -p "$OUT_ROOT"
|
|
}
|
|
|
|
apply_profile() {
|
|
local profile="$1"
|
|
export RUSTFS_OBJECT_IO_BUFFER_SIZE="$BASE_OBJECT_IO_BUFFER_SIZE"
|
|
export RUSTFS_OBJECT_DUPLEX_BUFFER_SIZE="$BASE_OBJECT_DUPLEX_BUFFER_SIZE"
|
|
export RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES="$BASE_ERASURE_ENCODE_MAX_INFLIGHT_BYTES"
|
|
export RUSTFS_RUNTIME_WORKER_THREADS="$BASE_RUNTIME_WORKER_THREADS"
|
|
export RUSTFS_RUNTIME_MAX_BLOCKING_THREADS="$BASE_RUNTIME_MAX_BLOCKING_THREADS"
|
|
|
|
case "$profile" in
|
|
baseline) ;;
|
|
inflight-32m) export RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES=33554432 ;;
|
|
inflight-48m) export RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES=50331648 ;;
|
|
inflight-64m) export RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES=67108864 ;;
|
|
io-buffer-512k) export RUSTFS_OBJECT_IO_BUFFER_SIZE=524288 ;;
|
|
io-buffer-1m) export RUSTFS_OBJECT_IO_BUFFER_SIZE=1048576 ;;
|
|
runtime-w16-b768)
|
|
export RUSTFS_RUNTIME_WORKER_THREADS=16
|
|
export RUSTFS_RUNTIME_MAX_BLOCKING_THREADS=768
|
|
;;
|
|
runtime-w20-b1024)
|
|
export RUSTFS_RUNTIME_WORKER_THREADS=20
|
|
export RUSTFS_RUNTIME_MAX_BLOCKING_THREADS=1024
|
|
;;
|
|
duplex-16m) export RUSTFS_OBJECT_DUPLEX_BUFFER_SIZE=16777216 ;;
|
|
*)
|
|
echo "ERROR: unsupported profile $profile" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
write_env_snapshot() {
|
|
local out_file="$1"
|
|
cat > "$out_file" <<EOF
|
|
RUSTFS_OBJECT_IO_BUFFER_SIZE=${RUSTFS_OBJECT_IO_BUFFER_SIZE}
|
|
RUSTFS_OBJECT_DUPLEX_BUFFER_SIZE=${RUSTFS_OBJECT_DUPLEX_BUFFER_SIZE}
|
|
RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES=${RUSTFS_ERASURE_ENCODE_MAX_INFLIGHT_BYTES}
|
|
RUSTFS_RUNTIME_WORKER_THREADS=${RUSTFS_RUNTIME_WORKER_THREADS}
|
|
RUSTFS_RUNTIME_MAX_BLOCKING_THREADS=${RUSTFS_RUNTIME_MAX_BLOCKING_THREADS}
|
|
EOF
|
|
}
|
|
|
|
run_apply_hook_if_needed() {
|
|
local profile="$1"
|
|
local env_file="$2"
|
|
if [[ "${#APPLY_CMD_ARR[@]}" -eq 0 ]]; then
|
|
return
|
|
fi
|
|
echo "[${profile}] running apply command..."
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
printf '[DRY-RUN] RUSTFS_TUNING_ENV_FILE=%q ' "$env_file"
|
|
printf '%q ' "${APPLY_CMD_ARR[@]}"
|
|
printf '\n'
|
|
echo "[DRY-RUN] sleep $APPLY_WAIT_SECS"
|
|
else
|
|
RUSTFS_TUNING_ENV_FILE="$env_file" "${APPLY_CMD_ARR[@]}"
|
|
echo "[${profile}] waiting ${APPLY_WAIT_SECS}s for service readiness..."
|
|
sleep "$APPLY_WAIT_SECS"
|
|
fi
|
|
}
|
|
|
|
profiles_for_group() {
|
|
case "$1" in
|
|
baseline) echo "baseline" ;;
|
|
inflight) echo "baseline inflight-32m inflight-48m inflight-64m" ;;
|
|
io-buffer) echo "baseline io-buffer-512k io-buffer-1m" ;;
|
|
runtime) echo "baseline runtime-w16-b768 runtime-w20-b1024" ;;
|
|
duplex) echo "baseline duplex-16m" ;;
|
|
all) echo "baseline inflight-32m inflight-48m inflight-64m io-buffer-512k io-buffer-1m runtime-w16-b768 runtime-w20-b1024 duplex-16m" ;;
|
|
esac
|
|
}
|
|
|
|
run_profile() {
|
|
local profile="$1"
|
|
local out_dir env_file baseline_root
|
|
out_dir="${OUT_ROOT}/${profile}"
|
|
mkdir -p "$out_dir"
|
|
|
|
apply_profile "$profile"
|
|
env_file="${out_dir}/env_snapshot.env"
|
|
write_env_snapshot "$env_file"
|
|
run_apply_hook_if_needed "$profile" "$env_file"
|
|
|
|
baseline_root=""
|
|
if [[ "$profile" != "baseline" ]]; then
|
|
baseline_root="${OUT_ROOT}/baseline"
|
|
fi
|
|
|
|
local -a cmd=(
|
|
bash "$RUNNER_SCRIPT"
|
|
--endpoint "$ENDPOINT"
|
|
--access-key "$ACCESS_KEY"
|
|
--secret-key "$SECRET_KEY"
|
|
--bucket-prefix "${BUCKET_PREFIX}-${profile}"
|
|
--region "$REGION"
|
|
--concurrencies "$CONCURRENCIES"
|
|
--sizes "$SIZES"
|
|
--duration "$DURATION"
|
|
--rounds "$ROUNDS"
|
|
--out-dir "$out_dir"
|
|
--warp-bin "$WARP_BIN"
|
|
)
|
|
|
|
if [[ -n "$baseline_root" ]]; then
|
|
cmd+=(--baseline-root "$baseline_root")
|
|
fi
|
|
if [[ -n "$EXTRA_ARGS" ]]; then
|
|
cmd+=(--extra-args "$EXTRA_ARGS")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_NODES" ]]; then
|
|
cmd+=(--nodes "$TOPOLOGY_NODES")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_DISKS_PER_NODE" ]]; then
|
|
cmd+=(--disks-per-node "$TOPOLOGY_DISKS_PER_NODE")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_TOTAL_DISKS" ]]; then
|
|
cmd+=(--total-disks "$TOPOLOGY_TOTAL_DISKS")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_CPU_PER_NODE" ]]; then
|
|
cmd+=(--cpu-per-node "$TOPOLOGY_CPU_PER_NODE")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_MEM_PER_NODE" ]]; then
|
|
cmd+=(--mem-per-node "$TOPOLOGY_MEM_PER_NODE")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_NETWORK" ]]; then
|
|
cmd+=(--network "$TOPOLOGY_NETWORK")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_ENDPOINT_MODE" ]]; then
|
|
cmd+=(--endpoint-mode "$TOPOLOGY_ENDPOINT_MODE")
|
|
fi
|
|
if [[ -n "$TOPOLOGY_ERASURE_SET_DRIVE_COUNT" ]]; then
|
|
cmd+=(--erasure-set-drive-count "$TOPOLOGY_ERASURE_SET_DRIVE_COUNT")
|
|
fi
|
|
if [[ -n "$CLIENT_HOST" ]]; then
|
|
cmd+=(--client-host "$CLIENT_HOST")
|
|
fi
|
|
if [[ -n "$CAPTURE_METRICS_ENDPOINTS" ]]; then
|
|
cmd+=(--capture-metrics-endpoints "$CAPTURE_METRICS_ENDPOINTS")
|
|
fi
|
|
if [[ -n "$CAPTURE_PROM_METRICS_URLS" ]]; then
|
|
cmd+=(--capture-prom-metrics-urls "$CAPTURE_PROM_METRICS_URLS")
|
|
fi
|
|
if [[ -n "$CAPTURE_RUSTFS_PID" ]]; then
|
|
cmd+=(--capture-rustfs-pid "$CAPTURE_RUSTFS_PID")
|
|
fi
|
|
if [[ "$CAPTURE_SKIP_HOST_TELEMETRY" == "true" ]]; then
|
|
cmd+=(--capture-skip-host-telemetry)
|
|
fi
|
|
if [[ "$SKIP_CAPTURE" == "true" ]]; then
|
|
cmd+=(--skip-capture)
|
|
fi
|
|
if [[ "$INSECURE" == "true" ]]; then
|
|
cmd+=(--insecure)
|
|
fi
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
cmd+=(--dry-run)
|
|
fi
|
|
|
|
echo
|
|
echo "===== Running profile ${profile} ====="
|
|
echo "Output: $out_dir"
|
|
echo "Env snapshot: $env_file"
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
printf '[DRY-RUN] '
|
|
printf '%q ' "${cmd[@]}"
|
|
printf '\n'
|
|
else
|
|
"${cmd[@]}"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
validate_args
|
|
require_cmd bash
|
|
require_cmd awk
|
|
if [[ ! -x "$RUNNER_SCRIPT" && ! -f "$RUNNER_SCRIPT" ]]; then
|
|
echo "ERROR: missing dependency script: $RUNNER_SCRIPT" >&2
|
|
exit 1
|
|
fi
|
|
setup_out_root
|
|
|
|
echo "Tuning output root: $OUT_ROOT"
|
|
echo "Group: $GROUP"
|
|
echo "Concurrencies: $CONCURRENCIES"
|
|
echo "Sizes: $SIZES"
|
|
echo "Cooldown between profiles: ${COOLDOWN_SECS}s"
|
|
|
|
local profiles profile index total_profiles
|
|
profiles=($(profiles_for_group "$GROUP"))
|
|
total_profiles="${#profiles[@]}"
|
|
for ((index = 0; index < total_profiles; index++)); do
|
|
profile="${profiles[$index]}"
|
|
run_profile "$profile"
|
|
if (( index + 1 < total_profiles )) && (( COOLDOWN_SECS > 0 )); then
|
|
echo
|
|
echo "[${profile}] cooldown ${COOLDOWN_SECS}s before next profile..."
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo "[DRY-RUN] sleep ${COOLDOWN_SECS}"
|
|
else
|
|
sleep "$COOLDOWN_SECS"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
echo
|
|
echo "Done. Profile outputs are under: $OUT_ROOT"
|
|
}
|
|
|
|
main "$@"
|