mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 21:26:28 +00:00
test(scripts): add reset-safe internode metric sampling (#6437)
Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -5,7 +5,8 @@ set -euo pipefail
|
|||||||
# Reuses scripts/run_object_batch_bench.sh and exports reproducible artifacts:
|
# Reuses scripts/run_object_batch_bench.sh and exports reproducible artifacts:
|
||||||
# - run manifest with scenario/tool metadata and git revision
|
# - run manifest with scenario/tool metadata and git revision
|
||||||
# - object benchmark summaries per scenario/workload/concurrency
|
# - object benchmark summaries per scenario/workload/concurrency
|
||||||
# - optional internode operation metric deltas from a Prometheus text endpoint
|
# - optional internode operation metric deltas from Prometheus text snapshots or
|
||||||
|
# PromQL increase() queries
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
@@ -32,6 +33,8 @@ SCENARIOS="local=http://127.0.0.1:9000,distributed=http://127.0.0.1:9001"
|
|||||||
# Optional Prometheus text exposition URL.
|
# Optional Prometheus text exposition URL.
|
||||||
# If empty, metrics delta collection is skipped.
|
# If empty, metrics delta collection is skipped.
|
||||||
INTERNODE_METRICS_URL=""
|
INTERNODE_METRICS_URL=""
|
||||||
|
INTERNODE_METRICS_MODE="text-delta"
|
||||||
|
INTERNODE_METRICS_RANGE=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
@@ -53,7 +56,9 @@ Optional:
|
|||||||
--samples <n> Default: 20000 (for s3bench)
|
--samples <n> Default: 20000 (for s3bench)
|
||||||
--warp-bin <path> Default: warp
|
--warp-bin <path> Default: warp
|
||||||
--s3bench-bin <path> Default: s3bench
|
--s3bench-bin <path> Default: s3bench
|
||||||
--metrics-url <url> Prometheus text endpoint for internode metrics delta
|
--metrics-url <url> Metrics URL for internode deltas. Use a Prometheus text endpoint with text-delta mode, or /api/v1/query with prometheus-increase mode
|
||||||
|
--metrics-mode <mode> text-delta or prometheus-increase. Default: text-delta
|
||||||
|
--metrics-range <range> PromQL range for prometheus-increase mode. Default: --duration
|
||||||
--out-dir <dir> Default: target/bench/internode-transport-<timestamp>
|
--out-dir <dir> Default: target/bench/internode-transport-<timestamp>
|
||||||
--extra-args "<args>" Passed to run_object_batch_bench.sh --extra-args
|
--extra-args "<args>" Passed to run_object_batch_bench.sh --extra-args
|
||||||
--insecure TLS insecure (self-signed)
|
--insecure TLS insecure (self-signed)
|
||||||
@@ -62,6 +67,7 @@ Optional:
|
|||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- This baseline covers S3 PUT/GET workloads and records internode metric deltas when --metrics-url is set.
|
- This baseline covers S3 PUT/GET workloads and records internode metric deltas when --metrics-url is set.
|
||||||
|
- Use --metrics-mode prometheus-increase with a Prometheus /api/v1/query URL when service restarts may reset counters between runs.
|
||||||
- The run manifest intentionally omits access keys, secret keys, and extra args to avoid writing credentials to artifacts.
|
- The run manifest intentionally omits access keys, secret keys, and extra args to avoid writing credentials to artifacts.
|
||||||
- Healing/replication-specific workloads should be run separately and appended to the same artifact directory.
|
- Healing/replication-specific workloads should be run separately and appended to the same artifact directory.
|
||||||
USAGE
|
USAGE
|
||||||
@@ -91,6 +97,8 @@ parse_args() {
|
|||||||
--warp-bin) WARP_BIN="$2"; shift 2 ;;
|
--warp-bin) WARP_BIN="$2"; shift 2 ;;
|
||||||
--s3bench-bin) S3BENCH_BIN="$2"; shift 2 ;;
|
--s3bench-bin) S3BENCH_BIN="$2"; shift 2 ;;
|
||||||
--metrics-url) INTERNODE_METRICS_URL="$2"; shift 2 ;;
|
--metrics-url) INTERNODE_METRICS_URL="$2"; shift 2 ;;
|
||||||
|
--metrics-mode) INTERNODE_METRICS_MODE="$2"; shift 2 ;;
|
||||||
|
--metrics-range) INTERNODE_METRICS_RANGE="$2"; shift 2 ;;
|
||||||
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
||||||
--extra-args) EXTRA_ARGS="$2"; shift 2 ;;
|
--extra-args) EXTRA_ARGS="$2"; shift 2 ;;
|
||||||
--insecure) INSECURE=true; shift ;;
|
--insecure) INSECURE=true; shift ;;
|
||||||
@@ -118,6 +126,13 @@ validate_args() {
|
|||||||
echo "ERROR: --scenarios cannot be empty" >&2
|
echo "ERROR: --scenarios cannot be empty" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "${INTERNODE_METRICS_MODE}" != "text-delta" && "${INTERNODE_METRICS_MODE}" != "prometheus-increase" ]]; then
|
||||||
|
echo "ERROR: --metrics-mode must be text-delta or prometheus-increase" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ "${INTERNODE_METRICS_MODE}" == "prometheus-increase" && -n "${INTERNODE_METRICS_URL}" && -z "${INTERNODE_METRICS_RANGE}" ]]; then
|
||||||
|
INTERNODE_METRICS_RANGE="${DURATION}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_output() {
|
setup_output() {
|
||||||
@@ -159,6 +174,8 @@ write_run_manifest() {
|
|||||||
echo "samples=${SAMPLES}"
|
echo "samples=${SAMPLES}"
|
||||||
echo "insecure=${INSECURE}"
|
echo "insecure=${INSECURE}"
|
||||||
echo "metrics_url=${INTERNODE_METRICS_URL:-N/A}"
|
echo "metrics_url=${INTERNODE_METRICS_URL:-N/A}"
|
||||||
|
echo "metrics_mode=${INTERNODE_METRICS_MODE}"
|
||||||
|
echo "metrics_range=${INTERNODE_METRICS_RANGE:-N/A}"
|
||||||
echo "out_dir=${OUT_DIR}"
|
echo "out_dir=${OUT_DIR}"
|
||||||
echo "extra_args_present=$([[ -n "${EXTRA_ARGS}" ]] && echo true || echo false)"
|
echo "extra_args_present=$([[ -n "${EXTRA_ARGS}" ]] && echo true || echo false)"
|
||||||
echo "access_key=REDACTED"
|
echo "access_key=REDACTED"
|
||||||
@@ -166,6 +183,72 @@ write_run_manifest() {
|
|||||||
} > "${manifest}"
|
} > "${manifest}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collect_internode_increases() {
|
||||||
|
local snapshot_file="$1"
|
||||||
|
if [[ -z "${INTERNODE_METRICS_URL}" ]]; then
|
||||||
|
: > "${snapshot_file}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "${DRY_RUN}" == "true" ]]; then
|
||||||
|
: > "${snapshot_file}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "${INTERNODE_METRICS_URL}" "${INTERNODE_METRICS_RANGE}" "${snapshot_file}" <<'PY'
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
query_url, prom_range, snapshot_raw = sys.argv[1:]
|
||||||
|
snapshot_path = pathlib.Path(snapshot_raw)
|
||||||
|
metrics = [
|
||||||
|
"rustfs_system_network_internode_operation_requests_outgoing_total",
|
||||||
|
"rustfs_system_network_internode_operation_requests_incoming_total",
|
||||||
|
"rustfs_system_network_internode_operation_errors_total",
|
||||||
|
"rustfs_system_network_internode_operation_classified_errors_total",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def escape_label(value):
|
||||||
|
return value.replace("\\", "\\\\").replace("\n", "\\n").replace('"', '\\"')
|
||||||
|
|
||||||
|
|
||||||
|
def query_prometheus(query):
|
||||||
|
separator = "&" if "?" in query_url else "?"
|
||||||
|
url = f"{query_url}{separator}{urllib.parse.urlencode({'query': query})}"
|
||||||
|
request = urllib.request.Request(url, headers={"Accept": "application/json"})
|
||||||
|
with urllib.request.urlopen(request, timeout=30) as response:
|
||||||
|
payload = json.loads(response.read().decode("utf-8"))
|
||||||
|
if payload.get("status") != "success":
|
||||||
|
raise RuntimeError(f"Prometheus query failed: {payload!r}")
|
||||||
|
return payload.get("data", {}).get("result", [])
|
||||||
|
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
try:
|
||||||
|
for metric_name in metrics:
|
||||||
|
query = f"sum by(operation, backend) (increase({metric_name}[{prom_range}]))"
|
||||||
|
for sample in query_prometheus(query):
|
||||||
|
labels = {
|
||||||
|
key: value
|
||||||
|
for key, value in sample.get("metric", {}).items()
|
||||||
|
if key in {"operation", "backend"}
|
||||||
|
}
|
||||||
|
value = sample.get("value", [None, None])[1]
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
label_text = ",".join(f'{key}="{escape_label(value)}"' for key, value in sorted(labels.items()))
|
||||||
|
lines.append(f"{metric_name}{{{label_text}}} {value}")
|
||||||
|
except Exception as err: # noqa: BLE001 - shell harness reports and skips metrics on query errors.
|
||||||
|
snapshot_path.write_text(f"# prometheus_increase_failed error={err}\n", encoding="utf-8")
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
snapshot_path.write_text("\n".join(lines) + ("\n" if lines else ""), encoding="utf-8")
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
collect_internode_snapshot() {
|
collect_internode_snapshot() {
|
||||||
local snapshot_file="$1"
|
local snapshot_file="$1"
|
||||||
if [[ -z "${INTERNODE_METRICS_URL}" ]]; then
|
if [[ -z "${INTERNODE_METRICS_URL}" ]]; then
|
||||||
@@ -202,7 +285,7 @@ extract_internode_rows() {
|
|||||||
n = split($0, parts, " ")
|
n = split($0, parts, " ")
|
||||||
value = parts[n]
|
value = parts[n]
|
||||||
gsub(/[[:space:]]+/, "", value)
|
gsub(/[[:space:]]+/, "", value)
|
||||||
if (value ~ /^[0-9]+([.][0-9]+)?$/) {
|
if (value ~ /^[-+]?[0-9]+([.][0-9]+)?([eE][-+]?[0-9]+)?$/) {
|
||||||
print metric "," op "," backend "," value
|
print metric "," op "," backend "," value
|
||||||
}
|
}
|
||||||
}' "${src}"
|
}' "${src}"
|
||||||
@@ -243,6 +326,30 @@ append_metric_deltas() {
|
|||||||
rm -f "${before_rows}" "${after_rows}"
|
rm -f "${before_rows}" "${after_rows}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
append_metric_increases() {
|
||||||
|
local scenario="$1"
|
||||||
|
local workload="$2"
|
||||||
|
local conc="$3"
|
||||||
|
local size="$4"
|
||||||
|
local increase_file="$5"
|
||||||
|
|
||||||
|
local increase_rows
|
||||||
|
increase_rows="$(mktemp)"
|
||||||
|
extract_internode_rows "${increase_file}" > "${increase_rows}"
|
||||||
|
|
||||||
|
awk -F',' -v scenario="${scenario}" -v workload="${workload}" -v conc="${conc}" -v size="${size}" '
|
||||||
|
{
|
||||||
|
metric = $1
|
||||||
|
operation = $2
|
||||||
|
backend = $3
|
||||||
|
delta = $4 + 0
|
||||||
|
printf "%s,%s,%s,%s,%s,%s,%s,%.0f,%.0f,%.0f\n", scenario, workload, conc, size, metric, operation, backend, 0, delta, delta
|
||||||
|
}
|
||||||
|
' "${increase_rows}" >> "${OUT_DIR}/internode_metric_deltas.csv"
|
||||||
|
|
||||||
|
rm -f "${increase_rows}"
|
||||||
|
}
|
||||||
|
|
||||||
append_object_summary() {
|
append_object_summary() {
|
||||||
local scenario="$1"
|
local scenario="$1"
|
||||||
local endpoint="$2"
|
local endpoint="$2"
|
||||||
@@ -274,9 +381,10 @@ run_workload() {
|
|||||||
local bucket="${BUCKET_PREFIX}-${scenario}-${workload}-c${conc}"
|
local bucket="${BUCKET_PREFIX}-${scenario}-${workload}-c${conc}"
|
||||||
local before_metrics="${run_dir}/metrics_before.prom"
|
local before_metrics="${run_dir}/metrics_before.prom"
|
||||||
local after_metrics="${run_dir}/metrics_after.prom"
|
local after_metrics="${run_dir}/metrics_after.prom"
|
||||||
|
local increase_metrics="${run_dir}/metrics_increase.prom"
|
||||||
|
|
||||||
mkdir -p "${run_dir}"
|
mkdir -p "${run_dir}"
|
||||||
if [[ -n "${INTERNODE_METRICS_URL}" ]]; then
|
if [[ -n "${INTERNODE_METRICS_URL}" && "${INTERNODE_METRICS_MODE}" == "text-delta" ]]; then
|
||||||
collect_internode_snapshot "${before_metrics}"
|
collect_internode_snapshot "${before_metrics}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -311,9 +419,12 @@ run_workload() {
|
|||||||
"${cmd[@]}"
|
"${cmd[@]}"
|
||||||
|
|
||||||
append_object_summary "${scenario}" "${endpoint}" "${workload}" "${conc}" "${run_dir}"
|
append_object_summary "${scenario}" "${endpoint}" "${workload}" "${conc}" "${run_dir}"
|
||||||
if [[ -n "${INTERNODE_METRICS_URL}" ]]; then
|
if [[ -n "${INTERNODE_METRICS_URL}" && "${INTERNODE_METRICS_MODE}" == "text-delta" ]]; then
|
||||||
collect_internode_snapshot "${after_metrics}"
|
collect_internode_snapshot "${after_metrics}"
|
||||||
append_metric_deltas "${scenario}" "${workload}" "${conc}" "all_sizes" "${before_metrics}" "${after_metrics}"
|
append_metric_deltas "${scenario}" "${workload}" "${conc}" "all_sizes" "${before_metrics}" "${after_metrics}"
|
||||||
|
elif [[ -n "${INTERNODE_METRICS_URL}" && "${INTERNODE_METRICS_MODE}" == "prometheus-increase" ]]; then
|
||||||
|
collect_internode_increases "${increase_metrics}"
|
||||||
|
append_metric_increases "${scenario}" "${workload}" "${conc}" "all_sizes" "${increase_metrics}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +461,11 @@ main() {
|
|||||||
validate_args
|
validate_args
|
||||||
require_cmd awk
|
require_cmd awk
|
||||||
if [[ -n "${INTERNODE_METRICS_URL}" && "${DRY_RUN}" != "true" ]]; then
|
if [[ -n "${INTERNODE_METRICS_URL}" && "${DRY_RUN}" != "true" ]]; then
|
||||||
require_cmd curl
|
if [[ "${INTERNODE_METRICS_MODE}" == "text-delta" ]]; then
|
||||||
|
require_cmd curl
|
||||||
|
else
|
||||||
|
require_cmd python3
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ ! -x "${OBJECT_BENCH_SCRIPT}" ]]; then
|
if [[ ! -x "${OBJECT_BENCH_SCRIPT}" ]]; then
|
||||||
echo "ERROR: benchmark script not executable: ${OBJECT_BENCH_SCRIPT}" >&2
|
echo "ERROR: benchmark script not executable: ${OBJECT_BENCH_SCRIPT}" >&2
|
||||||
|
|||||||
Reference in New Issue
Block a user