docs: agent models summary, available models list, cluster migration guide (2026-04-13)

This commit is contained in:
Repinoid
2026-04-13 12:22:45 +03:00
parent 4cd16521e6
commit 06767fdac9
7 changed files with 1761 additions and 0 deletions
+426
View File
@@ -0,0 +1,426 @@
#!/bin/bash
# 2026-04-09 — Нагрузочное тестирование SQS API
# Замеряет: throughput, latency, конкурентность, стабильность под нагрузкой
# Запуск: bash sqs_load_test.sh
set -euo pipefail
# ═══════════════════════════════════════════
# КОНФИГУРАЦИЯ
# ═══════════════════════════════════════════
export PATH="$HOME/.local/bin:$PATH"
SQS_HOST="https://sqs.kube5s.ru"
SQS_BASE="${SQS_HOST}/sqs/test001"
export AWS_ACCESS_KEY_ID="SQSAK-test001-ae4dc8"
export AWS_SECRET_ACCESS_KEY="J8mElXq8Nwww8iFRtGJCm8KfxM9XSTluTqS2A67xsEE"
export AWS_DEFAULT_REGION="us-east-1"
ENDPOINT="--endpoint-url ${SQS_BASE}"
AUTH="${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}"
VERSION="2012-11-05"
TS=$(date +%s)
TMPDIR=$(mktemp -d /tmp/sqs-load-XXXXX)
# Параметры нагрузки
MESSAGES_SINGLE=100 # send/receive в одну очередь
MESSAGES_BURST=50 # burst за раз
PARALLEL_WORKERS=5 # параллельные воркеры
QUEUES_COUNT=10 # кол-во очередей для multi-queue теста
echo "╔══════════════════════════════════════════════════════╗"
echo "║ SQS Load Test — $(date '+%Y-%m-%d %H:%M:%S') ║"
echo "║ Endpoint: ${SQS_BASE} ║"
echo "║ Temp: ${TMPDIR} ║"
echo "╚══════════════════════════════════════════════════════╝"
echo ""
# Утилита замера времени (ms)
ms_now() { date +%s%N | cut -b1-13; }
sqs_call() {
curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?${1}&Version=${VERSION}" 2>&1
}
# ═══════════════════════════════════════════
# ТЕСТ 1: Latency отдельных операций
# ═══════════════════════════════════════════
echo "── ТЕСТ 1: Latency отдельных операций ──"
Q1="loadq-latency-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q1}" >/dev/null
# SendMessage latency (20 вызовов)
SEND_TIMES=()
for i in $(seq 1 20); do
T1=$(ms_now)
sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MessageBody=latency-test-${i}" >/dev/null
T2=$(ms_now)
SEND_TIMES+=( $((T2 - T1)) )
done
# Статистика
SEND_TOTAL=0
SEND_MIN=999999
SEND_MAX=0
for t in "${SEND_TIMES[@]}"; do
SEND_TOTAL=$((SEND_TOTAL + t))
[ "$t" -lt "$SEND_MIN" ] && SEND_MIN=$t
[ "$t" -gt "$SEND_MAX" ] && SEND_MAX=$t
done
SEND_AVG=$((SEND_TOTAL / ${#SEND_TIMES[@]}))
echo " SendMessage (20x): avg=${SEND_AVG}ms min=${SEND_MIN}ms max=${SEND_MAX}ms"
# ReceiveMessage latency (20 вызовов)
RECV_TIMES=()
for i in $(seq 1 20); do
T1=$(ms_now)
sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q1}&MaxNumberOfMessages=1" >/dev/null
T2=$(ms_now)
RECV_TIMES+=( $((T2 - T1)) )
done
RECV_TOTAL=0
RECV_MIN=999999
RECV_MAX=0
for t in "${RECV_TIMES[@]}"; do
RECV_TOTAL=$((RECV_TOTAL + t))
[ "$t" -lt "$RECV_MIN" ] && RECV_MIN=$t
[ "$t" -gt "$RECV_MAX" ] && RECV_MAX=$t
done
RECV_AVG=$((RECV_TOTAL / ${#RECV_TIMES[@]}))
echo " ReceiveMessage (20x): avg=${RECV_AVG}ms min=${RECV_MIN}ms max=${RECV_MAX}ms"
# ListQueues latency
LIST_TIMES=()
for i in $(seq 1 10); do
T1=$(ms_now)
sqs_call "Action=ListQueues" >/dev/null
T2=$(ms_now)
LIST_TIMES+=( $((T2 - T1)) )
done
LIST_TOTAL=0
for t in "${LIST_TIMES[@]}"; do LIST_TOTAL=$((LIST_TOTAL + t)); done
LIST_AVG=$((LIST_TOTAL / ${#LIST_TIMES[@]}))
echo " ListQueues (10x): avg=${LIST_AVG}ms"
sqs_call "Action=PurgeQueue&QueueUrl=${SQS_BASE}/test001/${Q1}" >/dev/null
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q1}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 2: Последовательный throughput — 100 сообщений
# ═══════════════════════════════════════════
echo "── ТЕСТ 2: Последовательный throughput (${MESSAGES_SINGLE} сообщений) ──"
Q2="loadq-seq-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q2}" >/dev/null
# Отправка
T_START=$(ms_now)
SEND_OK=0
SEND_ERR=0
for i in $(seq 1 $MESSAGES_SINGLE); do
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q2}&MessageBody=seq-msg-${i}")
if echo "$R" | grep -q "MessageId"; then
SEND_OK=$((SEND_OK + 1))
else
SEND_ERR=$((SEND_ERR + 1))
fi
done
T_END=$(ms_now)
SEND_ELAPSED=$((T_END - T_START))
SEND_RPS=$(( MESSAGES_SINGLE * 1000 / (SEND_ELAPSED + 1) ))
echo " Send: ${SEND_OK} OK / ${SEND_ERR} ERR за ${SEND_ELAPSED}ms (${SEND_RPS} msg/s)"
# Получение
T_START=$(ms_now)
RECV_OK=0
EMPTY=0
for attempt in $(seq 1 $((MESSAGES_SINGLE + 50))); do
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q2}&MaxNumberOfMessages=10")
COUNT=$(echo "$R" | grep -c "<Message>" || true)
if [ "$COUNT" -gt 0 ]; then
RECV_OK=$((RECV_OK + COUNT))
else
EMPTY=$((EMPTY + 1))
[ "$EMPTY" -ge 5 ] && break
fi
[ "$RECV_OK" -ge "$MESSAGES_SINGLE" ] && break
done
T_END=$(ms_now)
RECV_ELAPSED=$((T_END - T_START))
RECV_RPS=$(( RECV_OK * 1000 / (RECV_ELAPSED + 1) ))
echo " Recv: ${RECV_OK}/${MESSAGES_SINGLE} за ${RECV_ELAPSED}ms (${RECV_RPS} msg/s)"
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q2}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 3: Параллельная отправка (N воркеров)
# ═══════════════════════════════════════════
echo "── ТЕСТ 3: Параллельная отправка (${PARALLEL_WORKERS} воркеров × ${MESSAGES_BURST} msg) ──"
Q3="loadq-par-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q3}" >/dev/null
# Функция воркера — записывает результат в файл
worker_send() {
local wid=$1
local ok=0
local err=0
for i in $(seq 1 $MESSAGES_BURST); do
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q3}&MessageBody=w${wid}-msg${i}&Version=${VERSION}" 2>&1)
if echo "$R" | grep -q "MessageId"; then
ok=$((ok + 1))
else
err=$((err + 1))
fi
done
echo "${ok}:${err}" > "${TMPDIR}/worker-${wid}.result"
}
T_START=$(ms_now)
for w in $(seq 1 $PARALLEL_WORKERS); do
worker_send $w &
done
wait
T_END=$(ms_now)
PAR_ELAPSED=$((T_END - T_START))
# Собираем результаты
PAR_OK=0
PAR_ERR=0
for w in $(seq 1 $PARALLEL_WORKERS); do
if [ -f "${TMPDIR}/worker-${w}.result" ]; then
RES=$(cat "${TMPDIR}/worker-${w}.result")
PAR_OK=$((PAR_OK + ${RES%%:*}))
PAR_ERR=$((PAR_ERR + ${RES##*:}))
fi
done
PAR_TOTAL=$((PARALLEL_WORKERS * MESSAGES_BURST))
PAR_RPS=$(( PAR_OK * 1000 / (PAR_ELAPSED + 1) ))
echo " Send: ${PAR_OK}/${PAR_TOTAL} OK, ${PAR_ERR} ERR за ${PAR_ELAPSED}ms (${PAR_RPS} msg/s)"
# Параллельное получение
worker_recv() {
local wid=$1
local ok=0
local empty=0
for _ in $(seq 1 $((MESSAGES_BURST + 20))); do
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q3}&MaxNumberOfMessages=10&Version=${VERSION}" 2>&1)
COUNT=$(echo "$R" | grep -c "<Message>" || true)
if [ "$COUNT" -gt 0 ]; then
ok=$((ok + COUNT))
else
empty=$((empty + 1))
[ "$empty" -ge 3 ] && break
fi
done
echo "${ok}" > "${TMPDIR}/recv-${wid}.result"
}
T_START=$(ms_now)
for w in $(seq 1 $PARALLEL_WORKERS); do
worker_recv $w &
done
wait
T_END=$(ms_now)
RECV_PAR_ELAPSED=$((T_END - T_START))
RECV_PAR_OK=0
for w in $(seq 1 $PARALLEL_WORKERS); do
if [ -f "${TMPDIR}/recv-${w}.result" ]; then
RECV_PAR_OK=$((RECV_PAR_OK + $(cat "${TMPDIR}/recv-${w}.result")))
fi
done
RECV_PAR_RPS=$(( RECV_PAR_OK * 1000 / (RECV_PAR_ELAPSED + 1) ))
echo " Recv: ${RECV_PAR_OK}/${PAR_TOTAL} за ${RECV_PAR_ELAPSED}ms (${RECV_PAR_RPS} msg/s)"
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q3}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 4: Много очередей одновременно
# ═══════════════════════════════════════════
echo "── ТЕСТ 4: Создание ${QUEUES_COUNT} очередей + send/receive в каждую ──"
T_START=$(ms_now)
CREATE_OK=0
for i in $(seq 1 $QUEUES_COUNT); do
R=$(sqs_call "Action=CreateQueue&QueueName=loadq-multi-${TS}-${i}")
if echo "$R" | grep -q "CreateQueueResult"; then
CREATE_OK=$((CREATE_OK + 1))
fi
done
T_CRE=$(ms_now)
echo " Create: ${CREATE_OK}/${QUEUES_COUNT} за $((T_CRE - T_START))ms"
# Send 5 msg в каждую параллельно
multi_send() {
local qi=$1
local ok=0
for m in $(seq 1 5); do
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${qi}&MessageBody=multi-${qi}-${m}&Version=${VERSION}" 2>&1)
echo "$R" | grep -q "MessageId" && ok=$((ok + 1))
done
echo "$ok" > "${TMPDIR}/multi-send-${qi}.result"
}
T_START=$(ms_now)
for i in $(seq 1 $QUEUES_COUNT); do
multi_send $i &
done
wait
T_SEND=$(ms_now)
MULTI_SEND_OK=0
for i in $(seq 1 $QUEUES_COUNT); do
[ -f "${TMPDIR}/multi-send-${i}.result" ] && MULTI_SEND_OK=$((MULTI_SEND_OK + $(cat "${TMPDIR}/multi-send-${i}.result")))
done
echo " Send (5 × ${QUEUES_COUNT}): ${MULTI_SEND_OK}/$((QUEUES_COUNT * 5)) за $((T_SEND - T_START))ms"
# Receive из каждой
multi_recv() {
local qi=$1
local ok=0
for _ in $(seq 1 10); do
R=$(curl -s --max-time 30 -u "$AUTH" "${SQS_BASE}?Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${qi}&MaxNumberOfMessages=10&Version=${VERSION}" 2>&1)
COUNT=$(echo "$R" | grep -c "<Message>" || true)
ok=$((ok + COUNT))
[ "$COUNT" -eq 0 ] && break
done
echo "$ok" > "${TMPDIR}/multi-recv-${qi}.result"
}
T_START=$(ms_now)
for i in $(seq 1 $QUEUES_COUNT); do
multi_recv $i &
done
wait
T_RECV=$(ms_now)
MULTI_RECV_OK=0
for i in $(seq 1 $QUEUES_COUNT); do
[ -f "${TMPDIR}/multi-recv-${i}.result" ] && MULTI_RECV_OK=$((MULTI_RECV_OK + $(cat "${TMPDIR}/multi-recv-${i}.result")))
done
echo " Recv: ${MULTI_RECV_OK}/$((QUEUES_COUNT * 5)) за $((T_RECV - T_START))ms"
# Удаление
T_START=$(ms_now)
for i in $(seq 1 $QUEUES_COUNT); do
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/loadq-multi-${TS}-${i}" >/dev/null &
done
wait
T_DEL=$(ms_now)
echo " Delete ${QUEUES_COUNT} queues: $((T_DEL - T_START))ms"
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 5: Стресс — быстрая send+receive петля
# ═══════════════════════════════════════════
echo "── ТЕСТ 5: Stress loop — send+receive 60 сек ──"
Q5="loadq-stress-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q5}" >/dev/null
STRESS_DURATION=60
STRESS_SEND=0
STRESS_RECV=0
STRESS_ERR=0
T_START=$(date +%s)
while true; do
NOW=$(date +%s)
ELAPSED=$((NOW - T_START))
[ "$ELAPSED" -ge "$STRESS_DURATION" ] && break
# Send
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MessageBody=stress-${STRESS_SEND}")
if echo "$R" | grep -q "MessageId"; then
STRESS_SEND=$((STRESS_SEND + 1))
else
STRESS_ERR=$((STRESS_ERR + 1))
fi
# Receive
R=$(sqs_call "Action=ReceiveMessage&QueueUrl=${SQS_BASE}/test001/${Q5}&MaxNumberOfMessages=10")
COUNT=$(echo "$R" | grep -c "<Message>" || true)
STRESS_RECV=$((STRESS_RECV + COUNT))
done
T_END=$(date +%s)
STRESS_REAL=$((T_END - T_START))
STRESS_SEND_RPS=$((STRESS_SEND / (STRESS_REAL + 1)))
STRESS_RECV_RPS=$((STRESS_RECV / (STRESS_REAL + 1)))
echo " Duration: ${STRESS_REAL}s"
echo " Sent: ${STRESS_SEND} (${STRESS_SEND_RPS} msg/s)"
echo " Received: ${STRESS_RECV} (${STRESS_RECV_RPS} msg/s)"
echo " Errors: ${STRESS_ERR}"
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q5}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 6: Большие сообщения
# ═══════════════════════════════════════════
echo "── ТЕСТ 6: Размер сообщений ──"
Q6="loadq-size-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q6}" >/dev/null
for SIZE in 1 1024 10240 65536 131072 262144; do
BODY=$(python3 -c "print('X' * ${SIZE})" 2>/dev/null)
T1=$(ms_now)
R=$(curl -s --max-time 30 -u "$AUTH" -X POST "${SQS_BASE}?Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q6}&Version=${VERSION}" --data-urlencode "MessageBody=${BODY}" 2>&1)
T2=$(ms_now)
LAT=$((T2 - T1))
if echo "$R" | grep -q "MessageId"; then
echo " ${SIZE} bytes: ✅ OK (${LAT}ms)"
else
ERR_TYPE=$(echo "$R" | grep -oP '(?<=<Code>)[^<]+' | head -1)
echo " ${SIZE} bytes: ❌ REJECTED — ${ERR_TYPE:-unknown} (${LAT}ms)"
fi
done
sqs_call "Action=PurgeQueue&QueueUrl=${SQS_BASE}/test001/${Q6}" >/dev/null
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q6}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ТЕСТ 7: Целостность данных
# ═══════════════════════════════════════════
echo "── ТЕСТ 7: Целостность данных (MD5 проверка) ──"
Q7="loadq-md5-${TS}"
sqs_call "Action=CreateQueue&QueueName=${Q7}" >/dev/null
MD5_OK=0
MD5_FAIL=0
for i in $(seq 1 20); do
MSG="integrity-check-${i}-$(head -c 16 /dev/urandom | base64 | tr -d '/+=')"
EXPECTED_MD5=$(echo -n "$MSG" | md5sum | awk '{print $1}')
R=$(sqs_call "Action=SendMessage&QueueUrl=${SQS_BASE}/test001/${Q7}&MessageBody=${MSG}")
RETURNED_MD5=$(echo "$R" | grep -oP '(?<=<MD5OfMessageBody>)[^<]+' | head -1)
if [ "$EXPECTED_MD5" = "$RETURNED_MD5" ]; then
MD5_OK=$((MD5_OK + 1))
else
MD5_FAIL=$((MD5_FAIL + 1))
echo " MISMATCH msg=${MSG}: expected=${EXPECTED_MD5} got=${RETURNED_MD5}"
fi
done
echo " MD5 check: ${MD5_OK}/20 OK, ${MD5_FAIL} FAIL"
sqs_call "Action=DeleteQueue&QueueUrl=${SQS_BASE}/test001/${Q7}" >/dev/null
echo ""
# ═══════════════════════════════════════════
# ИТОГО
# ═══════════════════════════════════════════
echo "╔══════════════════════════════════════════════════════╗"
echo "║ НАГРУЗОЧНЫЙ ТЕСТ ЗАВЕРШЁН ║"
echo "║ $(date '+%Y-%m-%d %H:%M:%S') ║"
echo "╚══════════════════════════════════════════════════════╝"
# Очистка temp
rm -rf "$TMPDIR"