553 lines
21 KiB
Bash
553 lines
21 KiB
Bash
#!/bin/bash
|
||
# compare_sqs.sh — Сравнительный тест двух SQS реализаций
|
||
# Created: 2026-04-10
|
||
#
|
||
# Сравниваем:
|
||
# A) sqs-operator — https://sqs.kube5s.ru/sqs/{tenant} (1 тенант = 1 namespace в k8s)
|
||
# B) shared-sqs — https://qu.kube5s.ru (Single-process, multitenancy через API)
|
||
#
|
||
# Метрики:
|
||
# - Время создания тенанта
|
||
# - Время CRUD очереди (create/send/receive/delete)
|
||
# - Время удаления тенанта
|
||
# - Ресурсы pods после N тенантов (kubectl top)
|
||
#
|
||
# Запуск:
|
||
# bash compare_sqs.sh [TENANTS]
|
||
# TENANTS=10 bash compare_sqs.sh
|
||
# TENANTS=20 bash compare_sqs.sh
|
||
# TENANTS=50 bash compare_sqs.sh
|
||
#
|
||
# Требования:
|
||
# - kubectl с доступом к кластеру
|
||
# - curl
|
||
# - python3
|
||
# - aws CLI (для shared-sqs CRUD)
|
||
#
|
||
# Для sqs-operator: тенант создаётся через kubectl apply QueueService CR
|
||
# Для shared-sqs: тенант создаётся через POST /admin/tenants
|
||
|
||
set -uo pipefail
|
||
|
||
# ════════════════════════════════════════════
|
||
# КОНФИГ
|
||
# ════════════════════════════════════════════
|
||
|
||
# Количество тенантов — передаётся аргументом или переменной или по умолчанию 10
|
||
TENANTS="${1:-${TENANTS:-10}}"
|
||
|
||
# sqs-operator
|
||
OP_HOST="https://sqs.kube5s.ru"
|
||
OP_CR_NAMESPACE="sqs-operator-system"
|
||
# Namespace тенантов в sqs-operator: sless-fn-{tenant}
|
||
OP_TENANT_NS_PREFIX="sless-fn"
|
||
# Шаблон QueueService CR (apiVersion из реального кода)
|
||
OP_CR_TEMPLATE='apiVersion: sqs.kube5s.ru/v1alpha1
|
||
kind: QueueService
|
||
metadata:
|
||
name: CRNAME
|
||
namespace: sqs-operator-system
|
||
spec:
|
||
tenantId: TENANTID
|
||
memoryMB: 64
|
||
storageMB: 512
|
||
persistence: false'
|
||
|
||
# shared-sqs
|
||
SS_HOST="https://qu.kube5s.ru"
|
||
SS_ADMIN_TOKEN="${SS_ADMIN_TOKEN:-sqs-admin-7a7d8bd0c060a75c198d48680f34077a}"
|
||
SS_REGION="us-east-1"
|
||
SS_NAMESPACE="sless" # namespace где живёт shared-sqs pod (для kubectl top)
|
||
|
||
# Имя пода shared-sqs в k8s (для ресурсов)
|
||
SS_DEPLOY_LABEL="app=shared-sqs"
|
||
OP_MANAGER_LABEL="control-plane=controller-manager"
|
||
OP_TENANT_LABEL_PREFIX="sless.dev/tenant"
|
||
|
||
# Таймаут ожидания QueueService Ready (секунд)
|
||
OP_READY_TIMEOUT=120
|
||
|
||
# ════════════════════════════════════════════
|
||
# УТИЛИТЫ
|
||
# ════════════════════════════════════════════
|
||
|
||
PASS=0; FAIL=0
|
||
RESULTS_FILE="/tmp/sqs_compare_$(date +%s).log"
|
||
|
||
ok() { echo " ✅ $1"; PASS=$((PASS+1)); }
|
||
fail() { echo " ❌ $1"; FAIL=$((FAIL+1)); }
|
||
hdr() { echo ""; echo "══════════════════════════════════════════"; echo " $1"; echo "══════════════════════════════════════════"; }
|
||
ts() { date '+%H:%M:%S'; }
|
||
elapsed_ms() {
|
||
# $1 = start seconds (с дробной частью от date +%s%3N)
|
||
local start="$1"
|
||
local end
|
||
end=$(date +%s%3N)
|
||
echo $(( end - start ))
|
||
}
|
||
|
||
# Запись результата в файл для итоговой таблицы
|
||
record() {
|
||
# $1=impl $2=phase $3=n_tenants $4=value_ms $5=label
|
||
echo "$1,$2,$3,$4,$5" >> "$RESULTS_FILE"
|
||
}
|
||
|
||
# ─── sqs-operator API ───
|
||
op_sqs() {
|
||
# $1=endpoint $2=ak $3=sk $4=query_params
|
||
curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" \
|
||
--user "$2:$3" \
|
||
"${1}/?${4}&Version=2012-11-05"
|
||
}
|
||
|
||
# Ожидать QueueService Phase=Ready
|
||
op_wait_ready() {
|
||
local crname="$1" max_sec="$2"
|
||
local start
|
||
start=$(date +%s)
|
||
while true; do
|
||
local phase
|
||
phase=$(kubectl get queueservice "$crname" -n "$OP_CR_NAMESPACE" \
|
||
-o jsonpath='{.status.phase}' 2>/dev/null || echo "")
|
||
[ "$phase" = "Ready" ] && return 0
|
||
local now
|
||
now=$(date +%s)
|
||
[ $(( now - start )) -ge "$max_sec" ] && return 1
|
||
sleep 2
|
||
done
|
||
}
|
||
|
||
# Проверить что QueueService CR API доступен (идемпотентно)
|
||
op_check_api() {
|
||
local tenantid="$1"
|
||
local ep="${OP_HOST}/sqs/${tenantid}"
|
||
# Для credentials нужен secret в k8s
|
||
local ns="${OP_TENANT_NS_PREFIX}-${tenantid}"
|
||
local ak sk
|
||
ak=$(kubectl -n "$ns" get secret "sqs-creds-${tenantid}" \
|
||
-o jsonpath='{.data.accessKey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
|
||
sk=$(kubectl -n "$ns" get secret "sqs-creds-${tenantid}" \
|
||
-o jsonpath='{.data.secretKey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
|
||
echo "$ak:$sk:$ep"
|
||
}
|
||
|
||
# ─── shared-sqs Admin API ───
|
||
ss_admin() {
|
||
local method="$1" path="$2" body="${3:-}"
|
||
if [[ -n "$body" ]]; then
|
||
curl -sf --max-time 15 \
|
||
-X "$method" \
|
||
-H "Authorization: Bearer $SS_ADMIN_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$body" \
|
||
"${SS_HOST}${path}" 2>&1
|
||
else
|
||
curl -sf --max-time 15 \
|
||
-X "$method" \
|
||
-H "Authorization: Bearer $SS_ADMIN_TOKEN" \
|
||
"${SS_HOST}${path}" 2>&1
|
||
fi
|
||
}
|
||
|
||
ss_sqs() {
|
||
# $1=ak $2=sk $3=action_params
|
||
curl -sk --max-time 15 \
|
||
-u "$1:$2" \
|
||
"${SS_HOST}/?${3}&Version=2012-11-05" 2>&1
|
||
}
|
||
|
||
# ════════════════════════════════════════════
|
||
# ПРОВЕРКА ДОСТУПНОСТИ
|
||
# ════════════════════════════════════════════
|
||
hdr "0. Проверка доступности (N=$TENANTS тенантов)"
|
||
|
||
OP_HEALTH=$(curl -sk --max-time 5 -o /dev/null -w "%{http_code}" "${OP_HOST}/sqs/test001" 2>/dev/null || echo "0")
|
||
SS_HEALTH=$(curl -sk --max-time 5 -o /dev/null -w "%{http_code}" "${SS_HOST}/health" 2>/dev/null || echo "0")
|
||
|
||
echo " sqs-operator (${OP_HOST}): HTTP $OP_HEALTH"
|
||
echo " shared-sqs (${SS_HOST}): HTTP $SS_HEALTH"
|
||
|
||
# kubectl доступен?
|
||
KUBECTL_OK=false
|
||
if kubectl cluster-info &>/dev/null; then
|
||
KUBECTL_OK=true
|
||
echo " kubectl: OK ($(kubectl config current-context))"
|
||
else
|
||
echo " kubectl: НЕДОСТУПЕН — метрики ресурсов будут пропущены"
|
||
fi
|
||
|
||
# aws cli доступен?
|
||
AWS_CLI_OK=false
|
||
if command -v aws &>/dev/null; then
|
||
AWS_CLI_OK=true
|
||
echo " aws CLI: OK"
|
||
else
|
||
echo " aws CLI: НЕДОСТУПЕН — shared-sqs CRUD будет через curl"
|
||
fi
|
||
|
||
# ════════════════════════════════════════════
|
||
# ФАЗА 1 — Создание N тенантов
|
||
# ════════════════════════════════════════════
|
||
hdr "1. Создание $TENANTS тенантов"
|
||
|
||
# Массивы для хранения созданных тенантов
|
||
declare -a OP_TENANTS=() # tenantId
|
||
declare -a OP_CR_NAMES=() # имя CR
|
||
declare -a SS_TENANT_IDS=() # UUID тенанта
|
||
declare -a SS_AK_LIST=() # Access Keys
|
||
declare -a SS_SK_LIST=() # Secret Keys
|
||
|
||
echo ""
|
||
echo " ── A) sqs-operator ──"
|
||
echo " (каждый тенант = QueueService CR → новый namespace + deployment + secret)"
|
||
echo ""
|
||
|
||
OP_TOTAL_CREATE_MS=0
|
||
OP_CREATE_TIMES=()
|
||
|
||
for i in $(seq 1 "$TENANTS"); do
|
||
tid="cmp-op-$(printf '%03d' $i)-$$"
|
||
crname="cmp-cr-$(printf '%03d' $i)-$$"
|
||
|
||
# Применяем CR
|
||
CR_YAML=$(echo "$OP_CR_TEMPLATE" | sed "s/CRNAME/$crname/g" | sed "s/TENANTID/$tid/g")
|
||
t_start=$(date +%s%3N)
|
||
echo "$CR_YAML" | kubectl apply -f - &>/dev/null
|
||
apply_ok=$?
|
||
|
||
if [[ $apply_ok -ne 0 ]]; then
|
||
echo " ❌ Тенант #$i ($tid): kubectl apply failed"
|
||
continue
|
||
fi
|
||
|
||
# Ждём Ready
|
||
if op_wait_ready "$crname" "$OP_READY_TIMEOUT"; then
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
OP_CREATE_TIMES+=("$t_ms")
|
||
OP_TOTAL_CREATE_MS=$(( OP_TOTAL_CREATE_MS + t_ms ))
|
||
printf " ✅ #%-3d %-40s %dms\n" "$i" "$tid" "$t_ms"
|
||
record "sqs-operator" "tenant_create" "$i" "$t_ms" "$tid"
|
||
else
|
||
echo " ⚠️ #$i ($tid): timeout — не стал Ready за ${OP_READY_TIMEOUT}s"
|
||
record "sqs-operator" "tenant_create_timeout" "$i" "$OP_READY_TIMEOUT\000" "$tid"
|
||
fi
|
||
|
||
OP_TENANTS+=("$tid")
|
||
OP_CR_NAMES+=("$crname")
|
||
done
|
||
|
||
OP_CREATED=${#OP_TENANTS[@]}
|
||
if [[ $OP_CREATED -gt 0 ]]; then
|
||
OP_AVG_CREATE=$(( OP_TOTAL_CREATE_MS / OP_CREATED ))
|
||
# Медиана
|
||
OP_SORTED_TIMES=($(printf '%s\n' "${OP_CREATE_TIMES[@]}" | sort -n))
|
||
OP_MEDIAN_CREATE=${OP_SORTED_TIMES[$(( OP_CREATED / 2 ))]}
|
||
printf "\n Итого sqs-operator: создано %d/%d avg=%dms median=%dms\n" \
|
||
"$OP_CREATED" "$TENANTS" "$OP_AVG_CREATE" "$OP_MEDIAN_CREATE"
|
||
fi
|
||
|
||
echo ""
|
||
echo " ── B) shared-sqs ──"
|
||
echo " (тенант = POST /admin/tenants, всё в памяти одного процесса)"
|
||
echo ""
|
||
|
||
SS_TOTAL_CREATE_MS=0
|
||
SS_CREATE_TIMES=()
|
||
|
||
for i in $(seq 1 "$TENANTS"); do
|
||
tname="cmp-ss-$(printf '%03d' $i)-$$"
|
||
t_start=$(date +%s%3N)
|
||
|
||
RESP=$(ss_admin POST /admin/tenants "{\"name\":\"${tname}\",\"max_queues\":50}" 2>&1)
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
|
||
if echo "$RESP" | grep -q "access_key"; then
|
||
ak=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_key'])" 2>/dev/null || echo "")
|
||
sk=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['secret_key'])" 2>/dev/null || echo "")
|
||
tid=$(echo "$RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" 2>/dev/null || echo "")
|
||
|
||
SS_CREATE_TIMES+=("$t_ms")
|
||
SS_TOTAL_CREATE_MS=$(( SS_TOTAL_CREATE_MS + t_ms ))
|
||
printf " ✅ #%-3d %-40s %dms (id=%s)\n" "$i" "$tname" "$t_ms" "${tid:0:12}..."
|
||
record "shared-sqs" "tenant_create" "$i" "$t_ms" "$tname"
|
||
|
||
SS_TENANT_IDS+=("$tid")
|
||
SS_AK_LIST+=("$ak")
|
||
SS_SK_LIST+=("$sk")
|
||
else
|
||
echo " ❌ #$i ($tname): ошибка: ${RESP:0:100}"
|
||
record "shared-sqs" "tenant_create_error" "$i" "0" "$tname"
|
||
fi
|
||
done
|
||
|
||
SS_CREATED=${#SS_TENANT_IDS[@]}
|
||
if [[ $SS_CREATED -gt 0 ]]; then
|
||
SS_AVG_CREATE=$(( SS_TOTAL_CREATE_MS / SS_CREATED ))
|
||
SS_SORTED_TIMES=($(printf '%s\n' "${SS_CREATE_TIMES[@]}" | sort -n))
|
||
SS_MEDIAN_CREATE=${SS_SORTED_TIMES[$(( SS_CREATED / 2 ))]}
|
||
printf "\n Итого shared-sqs: создано %d/%d avg=%dms median=%dms\n" \
|
||
"$SS_CREATED" "$TENANTS" "$SS_AVG_CREATE" "$SS_MEDIAN_CREATE"
|
||
fi
|
||
|
||
# ════════════════════════════════════════════
|
||
# ФАЗА 2 — CRUD очереди (первые 3 тенанта)
|
||
# ════════════════════════════════════════════
|
||
hdr "2. CRUD очереди (send+receive+delete, первые 3 тенанта каждой реализации)"
|
||
|
||
CRUD_LIMIT=3
|
||
|
||
echo ""
|
||
echo " ── A) sqs-operator CRUD ──"
|
||
echo ""
|
||
|
||
OP_CRUD_TIMES=()
|
||
for idx in $(seq 0 $(( CRUD_LIMIT - 1 ))); do
|
||
[[ $idx -ge ${#OP_TENANTS[@]} ]] && break
|
||
tid="${OP_TENANTS[$idx]}"
|
||
crname="${OP_CR_NAMES[$idx]}"
|
||
creds=$(op_check_api "$tid")
|
||
ak=$(echo "$creds" | cut -d: -f1)
|
||
sk=$(echo "$creds" | cut -d: -f2)
|
||
ep=$(echo "$creds" | cut -d: -f3-)
|
||
|
||
if [[ -z "$ak" || -z "$sk" ]]; then
|
||
echo " ⚠️ #$idx skipped — нет credentials (тенант не Ready?)"
|
||
continue
|
||
fi
|
||
|
||
qname="crud-q-$$-$idx"
|
||
t_start=$(date +%s%3N)
|
||
|
||
# CreateQueue
|
||
R=$(curl -sk --max-time 15 --aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${ep}/?Action=CreateQueue&QueueName=${qname}&Version=2012-11-05")
|
||
QURL=$(echo "$R" | grep -oP "(?<=<QueueUrl>)[^<]+" || echo "")
|
||
|
||
if [[ -z "$QURL" ]]; then
|
||
echo " ❌ #$idx CreateQueue failed: ${R:0:80}"
|
||
continue
|
||
fi
|
||
|
||
# SendMessage
|
||
curl -sk --max-time 15 --aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${ep}/?Action=SendMessage&QueueUrl=${QURL}&MessageBody=compare-test-${idx}&Version=2012-11-05" >/dev/null
|
||
|
||
# ReceiveMessage
|
||
R=$(curl -sk --max-time 15 --aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${ep}/?Action=ReceiveMessage&QueueUrl=${QURL}&MaxNumberOfMessages=1&Version=2012-11-05")
|
||
RECEIPT=$(echo "$R" | grep -oP "(?<=<ReceiptHandle>)[^<]+" | head -1 || echo "")
|
||
|
||
# DeleteMessage
|
||
if [[ -n "$RECEIPT" ]]; then
|
||
RECEIPT_ENC=$(echo "$RECEIPT" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))")
|
||
curl -sk --max-time 15 --aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${ep}/?Action=DeleteMessage&QueueUrl=${QURL}&ReceiptHandle=${RECEIPT_ENC}&Version=2012-11-05" >/dev/null
|
||
fi
|
||
|
||
# DeleteQueue
|
||
curl -sk --max-time 15 --aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${ep}/?Action=DeleteQueue&QueueUrl=${QURL}&Version=2012-11-05" >/dev/null
|
||
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
OP_CRUD_TIMES+=("$t_ms")
|
||
printf " ✅ Tenant #%d %-36s CRUD %dms\n" "$idx" "$tid" "$t_ms"
|
||
record "sqs-operator" "crud" "$idx" "$t_ms" "$tid"
|
||
done
|
||
|
||
echo ""
|
||
echo " ── B) shared-sqs CRUD ──"
|
||
echo ""
|
||
|
||
SS_CRUD_TIMES=()
|
||
for idx in $(seq 0 $(( CRUD_LIMIT - 1 ))); do
|
||
[[ $idx -ge ${#SS_TENANT_IDS[@]} ]] && break
|
||
tid="${SS_TENANT_IDS[$idx]}"
|
||
ak="${SS_AK_LIST[$idx]}"
|
||
sk="${SS_SK_LIST[$idx]}"
|
||
qname="crud-q-$$-$idx"
|
||
|
||
t_start=$(date +%s%3N)
|
||
|
||
# CreateQueue — shared-sqs требует AWS SigV4, не Basic Auth
|
||
R=$(curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${SS_HOST}/?Action=CreateQueue&QueueName=${qname}&Version=2012-11-05")
|
||
QURL=$(echo "$R" | grep -oP "(?<=<QueueUrl>)[^<]+" || echo "")
|
||
|
||
if [[ -z "$QURL" ]]; then
|
||
echo " ❌ #$idx CreateQueue failed: ${R:0:80}"
|
||
continue
|
||
fi
|
||
|
||
# SendMessage
|
||
curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${SS_HOST}/?Action=SendMessage&QueueUrl=${QURL}&MessageBody=compare-test-${idx}&Version=2012-11-05" >/dev/null
|
||
|
||
# ReceiveMessage
|
||
R=$(curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${SS_HOST}/?Action=ReceiveMessage&QueueUrl=${QURL}&MaxNumberOfMessages=1&Version=2012-11-05")
|
||
RECEIPT=$(echo "$R" | grep -oP "(?<=<ReceiptHandle>)[^<]+" | head -1 || echo "")
|
||
|
||
# DeleteMessage
|
||
if [[ -n "$RECEIPT" ]]; then
|
||
RECEIPT_ENC=$(echo "$RECEIPT" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))")
|
||
curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${SS_HOST}/?Action=DeleteMessage&QueueUrl=${QURL}&ReceiptHandle=${RECEIPT_ENC}&Version=2012-11-05" >/dev/null
|
||
fi
|
||
|
||
# DeleteQueue
|
||
curl -sk --max-time 15 \
|
||
--aws-sigv4 "aws:amz:us-east-1:sqs" --user "$ak:$sk" \
|
||
"${SS_HOST}/?Action=DeleteQueue&QueueUrl=${QURL}&Version=2012-11-05" >/dev/null
|
||
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
SS_CRUD_TIMES+=("$t_ms")
|
||
printf " ✅ Tenant #%d %-36s CRUD %dms\n" "$idx" "${tid:0:20}..." "$t_ms"
|
||
record "shared-sqs" "crud" "$idx" "$t_ms" "${tid:0:12}"
|
||
done
|
||
|
||
# ════════════════════════════════════════════
|
||
# ФАЗА 3 — Ресурсы (kubectl top)
|
||
# ════════════════════════════════════════════
|
||
hdr "3. Ресурсы при $TENANTS тенантах (kubectl top)"
|
||
|
||
if [[ "$KUBECTL_OK" == "true" ]]; then
|
||
echo ""
|
||
echo " ── A) sqs-operator pods ──"
|
||
# Controller manager
|
||
echo " -- Controller manager:"
|
||
kubectl top pods -n "$OP_CR_NAMESPACE" -l "$OP_MANAGER_LABEL" 2>/dev/null || echo " (kubectl top недоступен — metrics-server?)"
|
||
# Tenant pods — все ns с prefix sless-fn-cmp-
|
||
echo " -- Tenant pods (cmp-* namespaces):"
|
||
for tid in "${OP_TENANTS[@]}"; do
|
||
ns="${OP_TENANT_NS_PREFIX}-${tid}"
|
||
kubectl top pods -n "$ns" 2>/dev/null | grep -v "^NAME" | \
|
||
awk -v ns="$ns" '{printf " %-50s CPU=%-8s MEM=%s\n", ns"/"$1, $2, $3}' 2>/dev/null || true
|
||
done
|
||
# Суммарно: кол-во pods
|
||
OP_NS_COUNT=$(kubectl get ns | grep -c "sless-fn-cmp-" 2>/dev/null || echo "?")
|
||
echo " -- Итого namespace под тенанты: $OP_NS_COUNT"
|
||
|
||
echo ""
|
||
echo " ── B) shared-sqs pod ──"
|
||
kubectl top pods -n "$SS_NAMESPACE" -l "$SS_DEPLOY_LABEL" 2>/dev/null || \
|
||
kubectl top pods -n "$SS_NAMESPACE" 2>/dev/null | grep -i "shared-sqs\|sqs" | \
|
||
awk '{printf " %-50s CPU=%-8s MEM=%s\n", $1, $2, $3}' || \
|
||
echo " (pod не найден в namespace $SS_NAMESPACE)"
|
||
echo " -- Тенанты в памяти: $SS_CREATED (0 дополнительных pods)"
|
||
else
|
||
echo " kubectl недоступен — пропускаем"
|
||
fi
|
||
|
||
# ════════════════════════════════════════════
|
||
# ФАЗА 4 — Cleanup
|
||
# ════════════════════════════════════════════
|
||
hdr "4. Cleanup"
|
||
|
||
echo ""
|
||
echo " ── A) sqs-operator ──"
|
||
OP_DEL_TIMES=()
|
||
for i in "${!OP_CR_NAMES[@]}"; do
|
||
crname="${OP_CR_NAMES[$i]}"
|
||
t_start=$(date +%s%3N)
|
||
kubectl delete queueservice "$crname" -n "$OP_CR_NAMESPACE" --wait=false &>/dev/null
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
OP_DEL_TIMES+=("$t_ms")
|
||
record "sqs-operator" "tenant_delete" "$i" "$t_ms" "$crname"
|
||
done
|
||
echo " Удалено ${#OP_CR_NAMES[@]} CR (без ожидания термин.)"
|
||
|
||
echo ""
|
||
echo " ── B) shared-sqs ──"
|
||
SS_DEL_TIMES=()
|
||
for i in "${!SS_TENANT_IDS[@]}"; do
|
||
tid="${SS_TENANT_IDS[$i]}"
|
||
t_start=$(date +%s%3N)
|
||
ss_admin DELETE "/admin/tenants/$tid" >/dev/null 2>&1
|
||
t_ms=$(elapsed_ms "$t_start")
|
||
SS_DEL_TIMES+=("$t_ms")
|
||
record "shared-sqs" "tenant_delete" "$i" "$t_ms" "${tid:0:12}"
|
||
done
|
||
|
||
if [[ ${#SS_DEL_TIMES[@]} -gt 0 ]]; then
|
||
SS_AVG_DEL=$(python3 -c "t=[${SS_DEL_TIMES[*]}]; print(int(sum(t)/len(t)))" 2>/dev/null || echo "?")
|
||
echo " Удалено ${#SS_DEL_TIMES[@]} тенантов avg=${SS_AVG_DEL}ms"
|
||
fi
|
||
|
||
# ════════════════════════════════════════════
|
||
# ИТОГОВАЯ ТАБЛИЦА
|
||
# ════════════════════════════════════════════
|
||
hdr "ИТОГ — Сравнительная таблица (N=$TENANTS)"
|
||
|
||
python3 - << PYEOF
|
||
import csv, sys
|
||
|
||
data = {}
|
||
|
||
# Парсим results file
|
||
try:
|
||
with open("$RESULTS_FILE") as f:
|
||
for row in csv.reader(f):
|
||
if len(row) < 5:
|
||
continue
|
||
impl, phase, n, val_str, label = row
|
||
try:
|
||
val = int(val_str)
|
||
except:
|
||
continue
|
||
key = (impl, phase)
|
||
data.setdefault(key, []).append(val)
|
||
except FileNotFoundError:
|
||
print("Нет результатов")
|
||
sys.exit(0)
|
||
|
||
def stats(vals):
|
||
if not vals:
|
||
return "нет данных"
|
||
vals_s = sorted(vals)
|
||
n = len(vals_s)
|
||
avg = int(sum(vals_s) / n)
|
||
med = vals_s[n // 2]
|
||
mn = vals_s[0]
|
||
mx = vals_s[-1]
|
||
return f"avg={avg}ms med={med}ms min={mn}ms max={mx}ms n={n}"
|
||
|
||
print("")
|
||
print(f"{'Метрика':<30} {'sqs-operator':<50} {'shared-sqs':<50}")
|
||
print("-" * 130)
|
||
|
||
phases = [
|
||
("tenant_create", "Создание тенанта"),
|
||
("crud", "CRUD очереди (create+send+recv+del)"),
|
||
("tenant_delete", "Удаление тенанта"),
|
||
]
|
||
|
||
for phase_key, phase_label in phases:
|
||
op_vals = data.get(("sqs-operator", phase_key), [])
|
||
ss_vals = data.get(("shared-sqs", phase_key), [])
|
||
print(f" {phase_label:<28} {stats(op_vals):<50} {stats(ss_vals):<50}")
|
||
|
||
print("")
|
||
|
||
# Вывод победителя по скорости создания
|
||
op_create = data.get(("sqs-operator", "tenant_create"), [])
|
||
ss_create = data.get(("shared-sqs", "tenant_create"), [])
|
||
if op_create and ss_create:
|
||
op_avg = sum(op_create) / len(op_create)
|
||
ss_avg = sum(ss_create) / len(ss_create)
|
||
ratio = op_avg / ss_avg if ss_avg > 0 else float('inf')
|
||
faster = "shared-sqs" if ss_avg < op_avg else "sqs-operator"
|
||
print(f" Создание тенанта: {faster} быстрее в {ratio:.1f}x")
|
||
print(f" sqs-operator: {op_avg:.0f}ms | shared-sqs: {ss_avg:.0f}ms")
|
||
|
||
print("")
|
||
print(f" Файл подробных данных: $RESULTS_FILE")
|
||
PYEOF
|
||
|
||
echo ""
|
||
echo " Готово! $(date '+%Y-%m-%d %H:%M:%S')"
|