155 lines
4.5 KiB
Bash
Executable File
155 lines
4.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
api_port="${TIKU_API_PORT:-5091}"
|
|
mode="${TIKU_MODE:-mixed}"
|
|
rate="${TIKU_RATE:-100}"
|
|
duration="${TIKU_DURATION:-30s}"
|
|
tenant_code="${TIKU_TENANT_CODE:-demo-crm-school}"
|
|
redis_container="${TIKU_REDIS_CONTAINER:-tiku-redis}"
|
|
database_name="${TIKU_DATABASE_NAME:-tiku}"
|
|
run_stamp="$(date +%Y%m%d-%H%M%S)"
|
|
result_dir="${repo_root}/artifacts/performance/${run_stamp}-${mode}-${rate}rps"
|
|
api_pid=""
|
|
|
|
cleanup() {
|
|
if [[ -n "${api_pid}" ]] && kill -0 "${api_pid}" 2>/dev/null; then
|
|
kill "${api_pid}" 2>/dev/null || true
|
|
wait "${api_pid}" 2>/dev/null || true
|
|
fi
|
|
api_pid=""
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
mkdir -p "${result_dir}"
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "docker is required for the Redis readiness check" >&2
|
|
exit 1
|
|
fi
|
|
if ! docker inspect "${redis_container}" >/dev/null 2>&1; then
|
|
echo "Redis container '${redis_container}' does not exist" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "$(docker inspect -f '{{.State.Running}}' "${redis_container}")" != "true" ]]; then
|
|
echo "Redis container '${redis_container}' is not running" >&2
|
|
exit 1
|
|
fi
|
|
docker exec "${redis_container}" redis-cli ping | grep -qx PONG
|
|
|
|
if ! command -v psql >/dev/null 2>&1; then
|
|
echo "psql is required to capture PostgreSQL metrics" >&2
|
|
exit 1
|
|
fi
|
|
psql -d "${database_name}" -Atqc 'select 1' | grep -qx 1
|
|
|
|
(
|
|
cd "${repo_root}"
|
|
dotnet build TIKU-BACKEND.slnx -c Release --no-restore
|
|
)
|
|
|
|
host_url="http://127.0.0.1:${api_port}"
|
|
load_url="${host_url}"
|
|
|
|
(
|
|
cd "${repo_root}"
|
|
exec env \
|
|
ASPNETCORE_ENVIRONMENT=Development \
|
|
ASPNETCORE_URLS="http://0.0.0.0:${api_port}" \
|
|
ConnectionStrings__Redis=localhost:6379 \
|
|
RateLimiting__Enabled=false \
|
|
BackgroundProcessing__Enabled=false \
|
|
Serilog__MinimumLevel__Default=Warning \
|
|
dotnet run --project Tiku.Api -c Release --no-build --no-launch-profile
|
|
) >"${result_dir}/api.log" 2>&1 &
|
|
api_pid=$!
|
|
|
|
ready_body=""
|
|
for _ in $(seq 1 60); do
|
|
if ready_body="$(curl -fsS "${host_url}/api/health/ready" 2>/dev/null)"; then
|
|
break
|
|
fi
|
|
if ! kill -0 "${api_pid}" 2>/dev/null; then
|
|
echo "Tiku.Api stopped during startup; see ${result_dir}/api.log" >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if [[ -z "${ready_body}" ]]; then
|
|
echo "Tiku.Api did not become ready; see ${result_dir}/api.log" >&2
|
|
exit 1
|
|
fi
|
|
printf '%s\n' "${ready_body}" >"${result_dir}/readiness.json"
|
|
if ! printf '%s' "${ready_body}" | grep -q '"database":true'; then
|
|
echo "PostgreSQL is not ready: ${ready_body}" >&2
|
|
exit 1
|
|
fi
|
|
if ! printf '%s' "${ready_body}" | grep -q '"redis":{"configured":true,"ready":true}'; then
|
|
echo "Redis is not configured and ready: ${ready_body}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
capture_dependencies() {
|
|
local suffix="$1"
|
|
psql -d "${database_name}" -P pager=off -c \
|
|
"select datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit, tup_returned, tup_fetched from pg_stat_database where datname = current_database();" \
|
|
>"${result_dir}/postgres-${suffix}.txt"
|
|
docker exec "${redis_container}" redis-cli INFO stats \
|
|
>"${result_dir}/redis-${suffix}.txt"
|
|
}
|
|
|
|
capture_dependencies before
|
|
{
|
|
sw_vers
|
|
printf 'logical_cpu: '
|
|
sysctl -n hw.logicalcpu
|
|
printf 'memory_bytes: '
|
|
sysctl -n hw.memsize
|
|
dotnet --info
|
|
} >"${result_dir}/host.txt"
|
|
|
|
set +e
|
|
if command -v k6 >/dev/null 2>&1; then
|
|
load_url="${host_url}"
|
|
k6 run \
|
|
--summary-export "${result_dir}/summary.json" \
|
|
-e "TIKU_BASE_URL=${load_url}" \
|
|
-e "TIKU_HOST_HEADER=localhost:${api_port}" \
|
|
-e "TIKU_TENANT_CODE=${tenant_code}" \
|
|
-e "TIKU_MODE=${mode}" \
|
|
-e "TIKU_RATE=${rate}" \
|
|
-e "TIKU_DURATION=${duration}" \
|
|
"${repo_root}/tools/performance/tiku-api.js" \
|
|
| tee "${result_dir}/k6.txt"
|
|
k6_exit=${PIPESTATUS[0]}
|
|
else
|
|
load_url="http://host.docker.internal:${api_port}"
|
|
docker run --rm \
|
|
-v "${repo_root}:/work:ro" \
|
|
-v "${result_dir}:/results" \
|
|
grafana/k6:latest \
|
|
run \
|
|
--summary-export /results/summary.json \
|
|
-e "TIKU_BASE_URL=${load_url}" \
|
|
-e "TIKU_HOST_HEADER=localhost:${api_port}" \
|
|
-e "TIKU_TENANT_CODE=${tenant_code}" \
|
|
-e "TIKU_MODE=${mode}" \
|
|
-e "TIKU_RATE=${rate}" \
|
|
-e "TIKU_DURATION=${duration}" \
|
|
/work/tools/performance/tiku-api.js \
|
|
| tee "${result_dir}/k6.txt"
|
|
k6_exit=${PIPESTATUS[0]}
|
|
fi
|
|
set -e
|
|
|
|
cleanup
|
|
capture_dependencies after
|
|
printf 'mode=%s\nrate=%s\nduration=%s\ntenant=%s\nk6_exit=%s\n' \
|
|
"${mode}" "${rate}" "${duration}" "${tenant_code}" "${k6_exit}" \
|
|
>"${result_dir}/run.env"
|
|
|
|
echo "Results: ${result_dir}"
|
|
exit "${k6_exit}"
|