Files
Deal/scripts/loadtest/api-loadtest.sh
T
Rustam Khalimov 9e07568ddd Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы
ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ,
инструкция пользователя, техдокументация, код-стайл), бэклог,
скрипты развёртывания и архив прототипа LeadRadar.
2026-09-11 02:50:17 +03:00

99 lines
4.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Простой нагрузочный смоук-тест API Deal (этап 12, пакет C).
#
# Что делает: логинится (по умолчанию admin/admin), затем в цикле дёргает два
# читающих эндпоинта — GET /api/containers и GET /api/cards — до истечения
# DURATION_SECONDS, замеряя HTTP-код и время ответа. В конце печатает сводку:
# число запросов, ошибки (не 200), RPS, средняя/p95-латентность (сек).
#
# Зависимости: только bash и curl. Ничего не пишет в БД; читающие ручки.
#
# Запуск (из корня репозитория, dev-API на :5080):
# bash scripts/loadtest/api-loadtest.sh
# BASE_URL=http://localhost:5080 LOGIN=admin PASSWORD=admin \
# DURATION_SECONDS=20 PAUSE_SECONDS=0.1 bash scripts/loadtest/api-loadtest.sh
#
# Переменные окружения:
# BASE_URL базовый URL API (по умолчанию http://localhost:5080)
# LOGIN/PASSWORD учётные данные тенанта (по умолчанию admin/admin — dev-дефолт bootstrap)
# DURATION_SECONDS длительность прогона, с (по умолчанию 15)
# PAUSE_SECONDS пауза между итерациями, с (по умолчанию 0.2)
# CURL_TIMEOUT таймаут запроса, с (по умолчанию 5)
#
# Внимание: в PROD включён rate-limit (api-политика — 600/мин на тенанта). Для
# нагрузочного прогона используйте dev-стенд или поднимите лимит.
set -euo pipefail
BASE_URL="${BASE_URL:-http://localhost:5080}"
LOGIN="${LOGIN:-admin}"
PASSWORD="${PASSWORD:-admin}"
DURATION_SECONDS="${DURATION_SECONDS:-15}"
PAUSE_SECONDS="${PAUSE_SECONDS:-0.2}"
CURL_TIMEOUT="${CURL_TIMEOUT:-5}"
command -v curl >/dev/null 2>&1 || { echo "Ошибка: нужен curl" >&2; exit 127; }
tmp_dir="$(mktemp -d)"
cookie_jar="${tmp_dir}/cookies.txt"
lat_file="${tmp_dir}/latencies.txt"
trap 'rm -rf "${tmp_dir}"' EXIT
login_code="$(curl -sS -o "${tmp_dir}/login.json" -w '%{http_code}' \
-c "${cookie_jar}" -H 'Content-Type: application/json' \
-d "{\"login\":\"${LOGIN}\",\"password\":\"${PASSWORD}\"}" \
--max-time "${CURL_TIMEOUT}" "${BASE_URL}/api/auth/login" || true)"
[ -n "${login_code}" ] || login_code="000"
if [ "${login_code}" != "200" ]; then
echo "LOGIN FAILED (HTTP ${login_code}): $(cat "${tmp_dir}/login.json" 2>/dev/null)" >&2
echo "Проверьте BASE_URL=${BASE_URL} и что API поднят." >&2
exit 1
fi
echo "login OK (${LOGIN}) → прогон ${DURATION_SECONDS} с, пауза ${PAUSE_SECONDS} с"
started_at="$(date +%s)"
end_at=$((started_at + DURATION_SECONDS))
total=0
errors=0
while [ "$(date +%s)" -lt "${end_at}" ]; do
for path in /api/containers /api/cards; do
result="$(curl -sS -o /dev/null -w '%{http_code} %{time_total}' \
-b "${cookie_jar}" --max-time "${CURL_TIMEOUT}" "${BASE_URL}${path}" || true)"
[ -n "${result}" ] || result="000 0"
code="${result%% *}"
elapsed="${result##* }"
total=$((total + 1))
if [ "${code}" != "200" ]; then
errors=$((errors + 1))
echo " ! ${path} → HTTP ${code}" >&2
fi
echo "${elapsed}" >> "${lat_file}"
done
sleep "${PAUSE_SECONDS}"
done
elapsed_total=$(( $(date +%s) - started_at ))
[ "${elapsed_total}" -lt 1 ] && elapsed_total=1
if [ "${total}" -eq 0 ]; then
echo "Запросов не сделано (DURATION_SECONDS слишком мал?)" >&2
exit 1
fi
avg="$(awk '{sum += $1} END {printf "%.4f", sum / NR}' "${lat_file}")"
p95_index=$(( (total * 95 + 99) / 100 ))
p95="$(sort -n "${lat_file}" | sed -n "${p95_index}p")"
max="$(sort -n "${lat_file}" | tail -n 1)"
rps="$(awk -v n="${total}" -v d="${elapsed_total}" 'BEGIN {printf "%.2f", n / d}')"
echo "---------------------------------------------"
echo "запросов: ${total}"
echo "ошибок: ${errors}"
echo "длительность: ${elapsed_total} с"
echo "RPS: ${rps}"
echo "латентность: avg=${avg}s p95=${p95}s max=${max}s"
echo "---------------------------------------------"
[ "${errors}" -eq 0 ] || exit 2