Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user