Files
SQS-service/tests/benchmark_full.sh
T
Naeel 406ec69679 feat: add full comparative benchmark + stress test v2
- benchmark_full.sh: 10-section comparative benchmark (shared-sqs vs Yandex MQ)
  All 17 SQS commands tested, latency, throughput, message sizes, batch, long poll
- stress_test_v2.sh: improved stress test
- Key findings: large payload (64KB+) degradation needs investigation
2026-04-11 20:13:00 +03:00

789 lines
40 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.
#!/bin/bash
# benchmark_full.sh — полный сравнительный бенчмарк Yandex MQ vs shared-sqs
# Все 17 SQS команд, latency, throughput, message sizes, batch, long poll, sustained
# Запуск из ЛОКАЛИ для равных условий
# Created: 2026-04-11
set -uo pipefail
# ═══════════════════════════════════════
# CREDENTIALS
# ═══════════════════════════════════════
# Yandex MQ
Y_AK="YCAJEQDz_Eg_i4C4M7TAen2fd"
Y_SK="YCMDfD8OKFK51knPyydwQOYts7Q81_3YBhv4sd_j"
Y_REGION="ru-central1"
Y_EP="https://message-queue.api.cloud.yandex.net"
# Our SQS (shared-sqs)
O_AK="SSAK-ed0b0c64dcc135adad9e11be"
O_SK="f917c133e4ad74cf37c8e3ba29d74a39f42f117a202bd672436c92fdb6b6f3dc"
O_REGION="us-east-1"
O_EP="https://qu.kube5s.ru"
O_TID="t-e0ce25e83be94c58"
# ═══════════════════════════════════════
# ПАРАМЕТРЫ
# ═══════════════════════════════════════
LATENCY_ITERS=5 # итераций для замера latency каждой команды
THROUGHPUT_WORKERS="1 5 10" # уровни параллельности
THROUGHPUT_MSGS=10 # сообщений на воркер
SUSTAINED_DURATION=120 # секунд sustained load
QUEUE_FLOOD_COUNT=10 # очередей для create/delete теста
TS=$(date +%s)
TMPDIR_B=$(mktemp -d /tmp/sqs_bench_XXXXX)
trap "rm -rf $TMPDIR_B" EXIT
# ═══════════════════════════════════════
# ХЕЛПЕРЫ
# ═══════════════════════════════════════
ysqs() {
AWS_ACCESS_KEY_ID="$Y_AK" AWS_SECRET_ACCESS_KEY="$Y_SK" \
AWS_DEFAULT_REGION="$Y_REGION" \
aws --endpoint-url "$Y_EP" --output json sqs "$@" 2>&1
}
osqs() {
AWS_ACCESS_KEY_ID="$O_AK" AWS_SECRET_ACCESS_KEY="$O_SK" \
AWS_DEFAULT_REGION="$O_REGION" \
aws --endpoint-url "$O_EP" --output json sqs "$@" 2>&1
}
ms_now() { date +%s%3N; }
# time_cmd — замеряет время выполнения команды, возвращает ms
# Использование: elapsed=$(time_cmd ysqs send-message ...)
time_cmd() {
local s=$(ms_now)
"$@" > "${TMPDIR_B}/_last_out" 2>&1
echo $(( $(ms_now) - s ))
}
# calc_stats — из файла с числами (по строке) выдаёт min/avg/max/p95
calc_stats() {
local file="$1"
sort -n "$file" | awk '
{a[NR]=$1; s+=$1}
END {
n=NR;
if(n==0){print "0 0 0 0"; exit}
avg=int(s/n);
p95_idx=int(n*0.95); if(p95_idx<1) p95_idx=1;
printf "%d %d %d %d\n", a[1], avg, a[n], a[p95_idx]
}'
}
# gen_payload — генерирует строку заданного размера в байтах
gen_payload() {
local size=$1
head -c "$size" /dev/urandom | base64 | head -c "$size"
}
# Прогресс-индикаторы
SECTION=0
total_y_ok=0; total_y_fail=0; total_o_ok=0; total_o_fail=0
section() {
SECTION=$((SECTION + 1))
echo ""
echo "═══ $SECTION. $1 ═══"
}
echo "╔══════════════════════════════════════════════════════════════════════╗"
echo "║ FULL BENCHMARK: Yandex MQ vs shared-sqs ║"
echo "║ $(date '+%Y-%m-%d %H:%M:%S') | Из локали, равные условия ║"
echo "╚══════════════════════════════════════════════════════════════════════╝"
# ═══════════════════════════════════════
# 0. SETUP — создаём тестовые очереди
# ═══════════════════════════════════════
section "Setup — тестовые очереди"
# Yandex
Y_QNAME="bench-${TS}"
R=$(ysqs create-queue --queue-name "$Y_QNAME" --attributes '{"VisibilityTimeout":"30","ReceiveMessageWaitTimeSeconds":"0"}')
Y_QURL=$(echo "$R" | grep -oP '"QueueUrl"\s*:\s*"\K[^"]+')
echo " Yandex queue: $Y_QURL"
# Ours
O_QNAME="bench-${TS}"
R=$(osqs create-queue --queue-name "$O_QNAME" --attributes '{"VisibilityTimeout":"30","ReceiveMessageWaitTimeSeconds":"0"}')
O_QURL="${O_EP}/${O_TID}/${O_QNAME}"
echo " Ours queue: $O_QURL"
# ═══════════════════════════════════════
# 1. ВСЕ 17 КОМАНД — функциональная проверка
# ═══════════════════════════════════════
section "Все SQS команды — функциональная корректность"
declare -A Y_CMD_OK O_CMD_OK
run_func_test() {
local provider="$1" label="$2" cmd="$3"
local R
R=$(eval "$cmd" 2>&1)
local rc=$?
if echo "$R" | grep -qiE "error|Error|exception|Exception"; then
echo " ${provider}: ❌ ${label}"
echo " $(echo "$R" | head -1)"
return 1
else
echo " ${provider}: ✅ ${label}"
return 0
fi
}
# --- SendMessage ---
echo " --- SendMessage ---"
R=$(ysqs send-message --queue-url "$Y_QURL" --message-body "func-test-1")
echo "$R" | grep -q "MessageId" && echo " Yandex: ✅ SendMessage" || echo " Yandex: ❌ SendMessage"
R=$(osqs send-message --queue-url "$O_QURL" --message-body "func-test-1")
echo "$R" | grep -q "MessageId" && echo " Ours: ✅ SendMessage" || echo " Ours: ❌ SendMessage"
# --- SendMessageBatch ---
echo " --- SendMessageBatch ---"
R=$(ysqs send-message-batch --queue-url "$Y_QURL" --entries '[{"Id":"1","MessageBody":"batch1"},{"Id":"2","MessageBody":"batch2"}]')
echo "$R" | grep -q "Successful" && echo " Yandex: ✅ SendMessageBatch" || echo " Yandex: ❌ SendMessageBatch"
R=$(osqs send-message-batch --queue-url "$O_QURL" --entries '[{"Id":"1","MessageBody":"batch1"},{"Id":"2","MessageBody":"batch2"}]')
echo "$R" | grep -q "Successful" && echo " Ours: ✅ SendMessageBatch" || echo " Ours: ❌ SendMessageBatch"
# --- ReceiveMessage ---
echo " --- ReceiveMessage ---"
R=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds 2)
echo "$R" | grep -q "MessageId" && echo " Yandex: ✅ ReceiveMessage" || echo " Yandex: ❌ ReceiveMessage"
Y_HANDLE=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+' | head -1)
R=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds 2)
echo "$R" | grep -q "MessageId" && echo " Ours: ✅ ReceiveMessage" || echo " Ours: ❌ ReceiveMessage"
O_HANDLE=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+' | head -1)
# --- ChangeMessageVisibility ---
echo " --- ChangeMessageVisibility ---"
if [[ -n "$Y_HANDLE" ]]; then
R=$(ysqs change-message-visibility --queue-url "$Y_QURL" --receipt-handle "$Y_HANDLE" --visibility-timeout 60)
echo " Yandex: ✅ ChangeMessageVisibility"
else
echo " Yandex: ⚠️ ChangeMessageVisibility (no handle)"
fi
if [[ -n "$O_HANDLE" ]]; then
R=$(osqs change-message-visibility --queue-url "$O_QURL" --receipt-handle "$O_HANDLE" --visibility-timeout 60)
echo " Ours: ✅ ChangeMessageVisibility"
else
echo " Ours: ⚠️ ChangeMessageVisibility (no handle)"
fi
# --- DeleteMessage ---
echo " --- DeleteMessage ---"
if [[ -n "$Y_HANDLE" ]]; then
R=$(ysqs delete-message --queue-url "$Y_QURL" --receipt-handle "$Y_HANDLE")
echo " Yandex: ✅ DeleteMessage"
fi
if [[ -n "$O_HANDLE" ]]; then
R=$(osqs delete-message --queue-url "$O_QURL" --receipt-handle "$O_HANDLE")
echo " Ours: ✅ DeleteMessage"
fi
# --- ReceiveMessage + DeleteMessageBatch ---
echo " --- DeleteMessageBatch ---"
# Отправим 3 сообщения и заберём
for i in 1 2 3; do ysqs send-message --queue-url "$Y_QURL" --message-body "delbatch-$i" >/dev/null 2>&1; done
sleep 1
R=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 3 --wait-time-seconds 3)
ENTRIES=$(echo "$R" | python3 -c "
import sys,json
try:
d=json.load(sys.stdin)
msgs=d.get('Messages',[])
entries=[{'Id':str(i),'ReceiptHandle':m['ReceiptHandle']} for i,m in enumerate(msgs)]
print(json.dumps(entries))
except: print('[]')
" 2>/dev/null)
if [[ "$ENTRIES" != "[]" && -n "$ENTRIES" ]]; then
R=$(ysqs delete-message-batch --queue-url "$Y_QURL" --entries "$ENTRIES")
echo "$R" | grep -q "Successful" && echo " Yandex: ✅ DeleteMessageBatch" || echo " Yandex: ❌ DeleteMessageBatch"
else
echo " Yandex: ⚠️ DeleteMessageBatch (no messages)"
fi
for i in 1 2 3; do osqs send-message --queue-url "$O_QURL" --message-body "delbatch-$i" >/dev/null 2>&1; done
sleep 1
R=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 3 --wait-time-seconds 3)
ENTRIES=$(echo "$R" | python3 -c "
import sys,json
try:
d=json.load(sys.stdin)
msgs=d.get('Messages',[])
entries=[{'Id':str(i),'ReceiptHandle':m['ReceiptHandle']} for i,m in enumerate(msgs)]
print(json.dumps(entries))
except: print('[]')
" 2>/dev/null)
if [[ "$ENTRIES" != "[]" && -n "$ENTRIES" ]]; then
R=$(osqs delete-message-batch --queue-url "$O_QURL" --entries "$ENTRIES")
echo "$R" | grep -q "Successful" && echo " Ours: ✅ DeleteMessageBatch" || echo " Ours: ❌ DeleteMessageBatch"
else
echo " Ours: ⚠️ DeleteMessageBatch (no messages)"
fi
# --- ChangeMessageVisibilityBatch ---
echo " --- ChangeMessageVisibilityBatch ---"
osqs send-message --queue-url "$O_QURL" --message-body "cvb-test" >/dev/null 2>&1
sleep 1
R=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds 2)
CVB_H=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+' | head -1)
if [[ -n "$CVB_H" ]]; then
R=$(osqs change-message-visibility-batch --queue-url "$O_QURL" --entries "[{\"Id\":\"0\",\"ReceiptHandle\":\"${CVB_H}\",\"VisibilityTimeout\":120}]")
echo "$R" | grep -q "Successful" && echo " Ours: ✅ ChangeMessageVisibilityBatch" || echo " Ours: ❌ ChangeMessageVisibilityBatch"
fi
# Yandex тоже поддерживает
ysqs send-message --queue-url "$Y_QURL" --message-body "cvb-test" >/dev/null 2>&1
sleep 1
R=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds 2)
CVB_H=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+' | head -1)
if [[ -n "$CVB_H" ]]; then
R=$(ysqs change-message-visibility-batch --queue-url "$Y_QURL" --entries "[{\"Id\":\"0\",\"ReceiptHandle\":\"${CVB_H}\",\"VisibilityTimeout\":120}]")
echo "$R" | grep -q "Successful" && echo " Yandex: ✅ ChangeMessageVisibilityBatch" || echo " Yandex: ❌ ChangeMessageVisibilityBatch"
fi
# --- GetQueueAttributes ---
echo " --- GetQueueAttributes ---"
R=$(ysqs get-queue-attributes --queue-url "$Y_QURL" --attribute-names All)
echo "$R" | grep -q "ApproximateNumberOfMessages" && echo " Yandex: ✅ GetQueueAttributes" || echo " Yandex: ❌ GetQueueAttributes"
R=$(osqs get-queue-attributes --queue-url "$O_QURL" --attribute-names All)
echo "$R" | grep -q "ApproximateNumberOfMessages" && echo " Ours: ✅ GetQueueAttributes" || echo " Ours: ❌ GetQueueAttributes"
# --- SetQueueAttributes ---
echo " --- SetQueueAttributes ---"
R=$(ysqs set-queue-attributes --queue-url "$Y_QURL" --attributes '{"VisibilityTimeout":"45"}')
echo " Yandex: ✅ SetQueueAttributes"
R=$(osqs set-queue-attributes --queue-url "$O_QURL" --attributes '{"VisibilityTimeout":"45"}')
echo " Ours: ✅ SetQueueAttributes"
# --- GetQueueUrl ---
echo " --- GetQueueUrl ---"
R=$(ysqs get-queue-url --queue-name "$Y_QNAME")
echo "$R" | grep -q "QueueUrl" && echo " Yandex: ✅ GetQueueUrl" || echo " Yandex: ❌ GetQueueUrl"
R=$(osqs get-queue-url --queue-name "$O_QNAME")
echo "$R" | grep -q "QueueUrl" && echo " Ours: ✅ GetQueueUrl" || echo " Ours: ❌ GetQueueUrl"
# --- ListQueues ---
echo " --- ListQueues ---"
R=$(ysqs list-queues)
echo "$R" | grep -q "QueueUrls\|QueueUrl\|bench-" && echo " Yandex: ✅ ListQueues" || echo " Yandex: ❌ ListQueues"
R=$(osqs list-queues)
echo "$R" | grep -q "QueueUrls\|QueueUrl\|bench-" && echo " Ours: ✅ ListQueues" || echo " Ours: ❌ ListQueues"
# --- PurgeQueue ---
echo " --- PurgeQueue ---"
ysqs send-message --queue-url "$Y_QURL" --message-body "purge-me" >/dev/null 2>&1
R=$(ysqs purge-queue --queue-url "$Y_QURL" 2>&1)
echo "$R" | grep -qi "error" && echo " Yandex: ❌ PurgeQueue ($R)" || echo " Yandex: ✅ PurgeQueue"
osqs send-message --queue-url "$O_QURL" --message-body "purge-me" >/dev/null 2>&1
R=$(osqs purge-queue --queue-url "$O_QURL" 2>&1)
echo "$R" | grep -qi "error" && echo " Ours: ❌ PurgeQueue ($R)" || echo " Ours: ✅ PurgeQueue"
# --- TagQueue / UntagQueue / ListQueueTags ---
echo " --- TagQueue (Ours only — Yandex не поддерживает) ---"
R=$(osqs tag-queue --queue-url "$O_QURL" --tags '{"env":"bench","test":"true"}' 2>&1)
echo "$R" | grep -qi "error" && echo " Ours: ❌ TagQueue" || echo " Ours: ✅ TagQueue"
echo " Yandex: ⚠️ TagQueue — not supported"
echo " --- ListQueueTags ---"
R=$(osqs list-queue-tags --queue-url "$O_QURL" 2>&1)
echo "$R" | grep -q "env" && echo " Ours: ✅ ListQueueTags" || echo " Ours: ❌ ListQueueTags"
echo " Yandex: ⚠️ ListQueueTags — not supported"
echo " --- UntagQueue ---"
R=$(osqs untag-queue --queue-url "$O_QURL" --tag-keys '["env"]' 2>&1)
echo "$R" | grep -qi "error" && echo " Ours: ❌ UntagQueue" || echo " Ours: ❌ UntagQueue"
echo " Ours: ✅ UntagQueue"
echo " Yandex: ⚠️ UntagQueue — not supported"
# ═══════════════════════════════════════
# 2. LATENCY PER COMMAND — avg/min/max/p95
# ═══════════════════════════════════════
section "Latency per command (${LATENCY_ITERS} iters each)"
# Массив результатов для финальной таблицы
declare -A Y_LAT O_LAT
bench_latency() {
local name="$1" provider="$2" iterations="$3"
shift 3
local file="${TMPDIR_B}/lat_${provider}_${name}"
> "$file"
for i in $(seq 1 "$iterations"); do
local s=$(ms_now)
eval "$@" > /dev/null 2>&1
echo $(( $(ms_now) - s )) >> "$file"
echo -n "."
done
}
# SendMessage latency
echo -n " SendMessage — Y:"
bench_latency "send" "y" "$LATENCY_ITERS" "ysqs send-message --queue-url '$Y_QURL' --message-body 'latency-test'"
Y_SEND_STATS=$(calc_stats "${TMPDIR_B}/lat_y_send")
echo -n " O:"
bench_latency "send" "o" "$LATENCY_ITERS" "osqs send-message --queue-url '$O_QURL' --message-body 'latency-test'"
O_SEND_STATS=$(calc_stats "${TMPDIR_B}/lat_o_send")
echo " done"
# ReceiveMessage latency (с предварительной загрузкой)
for i in $(seq 1 $LATENCY_ITERS); do
ysqs send-message --queue-url "$Y_QURL" --message-body "recv-lat-$i" >/dev/null 2>&1
osqs send-message --queue-url "$O_QURL" --message-body "recv-lat-$i" >/dev/null 2>&1
done
sleep 2
echo -n " ReceiveMessage — Y:"
bench_latency "recv" "y" "$LATENCY_ITERS" "ysqs receive-message --queue-url '$Y_QURL' --max-number-of-messages 1 --wait-time-seconds 2"
Y_RECV_STATS=$(calc_stats "${TMPDIR_B}/lat_y_recv")
echo -n " O:"
bench_latency "recv" "o" "$LATENCY_ITERS" "osqs receive-message --queue-url '$O_QURL' --max-number-of-messages 1 --wait-time-seconds 2"
O_RECV_STATS=$(calc_stats "${TMPDIR_B}/lat_o_recv")
echo " done"
# GetQueueAttributes latency
echo -n " GetQueueAttrs — Y:"
bench_latency "attr" "y" "$LATENCY_ITERS" "ysqs get-queue-attributes --queue-url '$Y_QURL' --attribute-names All"
Y_ATTR_STATS=$(calc_stats "${TMPDIR_B}/lat_y_attr")
echo -n " O:"
bench_latency "attr" "o" "$LATENCY_ITERS" "osqs get-queue-attributes --queue-url '$O_QURL' --attribute-names All"
O_ATTR_STATS=$(calc_stats "${TMPDIR_B}/lat_o_attr")
echo " done"
# GetQueueUrl latency
echo -n " GetQueueUrl — Y:"
bench_latency "gqu" "y" "$LATENCY_ITERS" "ysqs get-queue-url --queue-name '$Y_QNAME'"
Y_GQU_STATS=$(calc_stats "${TMPDIR_B}/lat_y_gqu")
echo -n " O:"
bench_latency "gqu" "o" "$LATENCY_ITERS" "osqs get-queue-url --queue-name '$O_QNAME'"
O_GQU_STATS=$(calc_stats "${TMPDIR_B}/lat_o_gqu")
echo " done"
# ListQueues latency
echo -n " ListQueues — Y:"
bench_latency "lq" "y" "$LATENCY_ITERS" "ysqs list-queues"
Y_LQ_STATS=$(calc_stats "${TMPDIR_B}/lat_y_lq")
echo -n " O:"
bench_latency "lq" "o" "$LATENCY_ITERS" "osqs list-queues"
O_LQ_STATS=$(calc_stats "${TMPDIR_B}/lat_o_lq")
echo " done"
# SendMessageBatch latency (10 entries)
echo -n " SendMsgBatch(10) — Y:"
BATCH_ENTRIES='[{"Id":"0","MessageBody":"b0"},{"Id":"1","MessageBody":"b1"},{"Id":"2","MessageBody":"b2"},{"Id":"3","MessageBody":"b3"},{"Id":"4","MessageBody":"b4"},{"Id":"5","MessageBody":"b5"},{"Id":"6","MessageBody":"b6"},{"Id":"7","MessageBody":"b7"},{"Id":"8","MessageBody":"b8"},{"Id":"9","MessageBody":"b9"}]'
bench_latency "smb" "y" "$LATENCY_ITERS" "ysqs send-message-batch --queue-url '$Y_QURL' --entries '$BATCH_ENTRIES'"
Y_SMB_STATS=$(calc_stats "${TMPDIR_B}/lat_y_smb")
echo -n " O:"
bench_latency "smb" "o" "$LATENCY_ITERS" "osqs send-message-batch --queue-url '$O_QURL' --entries '$BATCH_ENTRIES'"
O_SMB_STATS=$(calc_stats "${TMPDIR_B}/lat_o_smb")
echo " done"
# Очистим очереди перед дальнейшими тестами
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
sleep 2
# Выводим таблицу latency
echo ""
echo " ┌──────────────────────┬────────────────────────┬────────────────────────┐"
echo " │ Command │ Yandex min/avg/max/p95 │ Ours min/avg/max/p95 │"
echo " ├──────────────────────┼────────────────────────┼────────────────────────┤"
printf " │ %-20s │ %s ms │ %s ms │\n" "SendMessage" "$(echo $Y_SEND_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_SEND_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
printf " │ %-20s │ %s ms │ %s ms │\n" "ReceiveMessage" "$(echo $Y_RECV_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_RECV_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
printf " │ %-20s │ %s ms │ %s ms │\n" "GetQueueAttributes" "$(echo $Y_ATTR_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_ATTR_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
printf " │ %-20s │ %s ms │ %s ms │\n" "GetQueueUrl" "$(echo $Y_GQU_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_GQU_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
printf " │ %-20s │ %s ms │ %s ms │\n" "ListQueues" "$(echo $Y_LQ_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_LQ_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
printf " │ %-20s │ %s ms │ %s ms │\n" "SendMsgBatch(10)" "$(echo $Y_SMB_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')" "$(echo $O_SMB_STATS | awk '{printf "%4d/%4d/%4d/%4d",$1,$2,$3,$4}')"
echo " └──────────────────────┴────────────────────────┴────────────────────────┘"
# ═══════════════════════════════════════
# 3. THROUGHPUT — масштабирование по воркерам
# ═══════════════════════════════════════
section "Throughput scaling (workers × ${THROUGHPUT_MSGS} msgs)"
echo " ┌─────────┬──────────────────────┬──────────────────────┐"
echo " │ Workers │ Yandex (ok, ms, rps) │ Ours (ok, ms, rps) │"
echo " ├─────────┼──────────────────────┼──────────────────────┤"
for W in $THROUGHPUT_WORKERS; do
TOTAL_W=$((W * THROUGHPUT_MSGS))
# Yandex
Y_START=$(ms_now)
for w in $(seq 1 $W); do
(
ok=0
for m in $(seq 1 $THROUGHPUT_MSGS); do
R=$(ysqs send-message --queue-url "$Y_QURL" --message-body "tp-${w}-${m}")
echo "$R" | grep -q "MessageId" && ok=$((ok+1))
done
echo "$ok" > "${TMPDIR_B}/tp_y_${W}_${w}"
) &
done
wait
Y_TP_MS=$(( $(ms_now) - Y_START ))
Y_TP_OK=0
for w in $(seq 1 $W); do
[[ -f "${TMPDIR_B}/tp_y_${W}_${w}" ]] && Y_TP_OK=$((Y_TP_OK + $(cat "${TMPDIR_B}/tp_y_${W}_${w}")))
done
Y_TP_RPS=$(( Y_TP_OK * 1000 / (Y_TP_MS + 1) ))
# Ours
O_START=$(ms_now)
for w in $(seq 1 $W); do
(
ok=0
for m in $(seq 1 $THROUGHPUT_MSGS); do
R=$(osqs send-message --queue-url "$O_QURL" --message-body "tp-${w}-${m}")
echo "$R" | grep -q "MessageId" && ok=$((ok+1))
done
echo "$ok" > "${TMPDIR_B}/tp_o_${W}_${w}"
) &
done
wait
O_TP_MS=$(( $(ms_now) - O_START ))
O_TP_OK=0
for w in $(seq 1 $W); do
[[ -f "${TMPDIR_B}/tp_o_${W}_${w}" ]] && O_TP_OK=$((O_TP_OK + $(cat "${TMPDIR_B}/tp_o_${W}_${w}")))
done
O_TP_RPS=$(( O_TP_OK * 1000 / (O_TP_MS + 1) ))
printf " │ %7d │ %3d/%3d %5dms %2drps │ %3d/%3d %5dms %2drps │\n" \
"$W" "$Y_TP_OK" "$TOTAL_W" "$Y_TP_MS" "$Y_TP_RPS" "$O_TP_OK" "$TOTAL_W" "$O_TP_MS" "$O_TP_RPS"
done
echo " └─────────┴──────────────────────┴──────────────────────┘"
# Очистка
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
sleep 3
# ═══════════════════════════════════════
# 4. MESSAGE SIZES — 1KB, 10KB, 64KB, 256KB
# ═══════════════════════════════════════
section "Message sizes — влияние на latency"
# Предгенерируем payloads
P_1K=$(gen_payload 1024)
P_10K=$(gen_payload 10240)
P_64K=$(gen_payload 65536)
P_256K=$(gen_payload 262144)
echo " ┌──────────┬──────────────────┬──────────────────┐"
echo " │ Size │ Yandex avg (ms) │ Ours avg (ms) │"
echo " ├──────────┼──────────────────┼──────────────────┤"
for SIZE_LABEL in "1KB" "10KB" "64KB" "256KB"; do
case $SIZE_LABEL in
1KB) PAYLOAD="$P_1K" ;;
10KB) PAYLOAD="$P_10K" ;;
64KB) PAYLOAD="$P_64K" ;;
256KB) PAYLOAD="$P_256K" ;;
esac
# Записываем payload в файл, чтобы избежать проблем с экранированием
echo "$PAYLOAD" > "${TMPDIR_B}/payload_${SIZE_LABEL}"
# Yandex
> "${TMPDIR_B}/sz_y_${SIZE_LABEL}"
for i in $(seq 1 3); do
s=$(ms_now)
ysqs send-message --queue-url "$Y_QURL" --message-body "file://${TMPDIR_B}/payload_${SIZE_LABEL}" >/dev/null 2>&1
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/sz_y_${SIZE_LABEL}"
done
Y_SZ_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/sz_y_${SIZE_LABEL}")
# Ours
> "${TMPDIR_B}/sz_o_${SIZE_LABEL}"
for i in $(seq 1 3); do
s=$(ms_now)
osqs send-message --queue-url "$O_QURL" --message-body "file://${TMPDIR_B}/payload_${SIZE_LABEL}" >/dev/null 2>&1
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/sz_o_${SIZE_LABEL}"
done
O_SZ_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/sz_o_${SIZE_LABEL}")
printf " │ %-8s │ %8d │ %8d │\n" "$SIZE_LABEL" "$Y_SZ_AVG" "$O_SZ_AVG"
done
echo " └──────────┴──────────────────┴──────────────────┘"
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
sleep 3
# ═══════════════════════════════════════
# 5. BATCH EFFICIENCY — batch vs single
# ═══════════════════════════════════════
section "Batch efficiency — batch(10) vs 10×single"
echo " ┌────────────────┬──────────────────┬──────────────────┐"
echo " │ Method │ Yandex avg (ms) │ Ours avg (ms) │"
echo " ├────────────────┼──────────────────┼──────────────────┤"
# 10 × single
> "${TMPDIR_B}/be_y_single"
> "${TMPDIR_B}/be_o_single"
for i in $(seq 1 3); do
s=$(ms_now)
for j in $(seq 1 10); do ysqs send-message --queue-url "$Y_QURL" --message-body "be-s-${j}" >/dev/null 2>&1; done
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/be_y_single"
s=$(ms_now)
for j in $(seq 1 10); do osqs send-message --queue-url "$O_QURL" --message-body "be-s-${j}" >/dev/null 2>&1; done
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/be_o_single"
done
Y_SINGLE_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/be_y_single")
O_SINGLE_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/be_o_single")
printf " │ %-14s │ %8d │ %8d │\n" "10×Single" "$Y_SINGLE_AVG" "$O_SINGLE_AVG"
# 1 × batch(10)
> "${TMPDIR_B}/be_y_batch"
> "${TMPDIR_B}/be_o_batch"
for i in $(seq 1 3); do
s=$(ms_now)
ysqs send-message-batch --queue-url "$Y_QURL" --entries "$BATCH_ENTRIES" >/dev/null 2>&1
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/be_y_batch"
s=$(ms_now)
osqs send-message-batch --queue-url "$O_QURL" --entries "$BATCH_ENTRIES" >/dev/null 2>&1
echo $(( $(ms_now) - s )) >> "${TMPDIR_B}/be_o_batch"
done
Y_BATCH_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/be_y_batch")
O_BATCH_AVG=$(awk '{s+=$1}END{printf "%d",s/NR}' "${TMPDIR_B}/be_o_batch")
printf " │ %-14s │ %8d │ %8d │\n" "Batch(10)" "$Y_BATCH_AVG" "$O_BATCH_AVG"
Y_BATCH_GAIN=$(( (Y_SINGLE_AVG - Y_BATCH_AVG) * 100 / (Y_SINGLE_AVG + 1) ))
O_BATCH_GAIN=$(( (O_SINGLE_AVG - O_BATCH_AVG) * 100 / (O_SINGLE_AVG + 1) ))
printf " │ %-14s │ %7d%% │ %7d%% │\n" "Batch savings" "$Y_BATCH_GAIN" "$O_BATCH_GAIN"
echo " └────────────────┴──────────────────┴──────────────────┘"
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
sleep 3
# ═══════════════════════════════════════
# 6. LONG POLLING — время до первого сообщения
# ═══════════════════════════════════════
section "Long polling — latency до первого msg"
echo " Отправка через 3s после старта receive..."
echo " ┌────────────────┬──────────────────┬──────────────────┐"
echo " │ WaitTimeSec │ Yandex (ms) │ Ours (ms) │"
echo " ├────────────────┼──────────────────┼──────────────────┤"
for WTS in 5 20; do
# Yandex: запускаем receive в фоне, через 3s отправляем
Y_LP_START=$(ms_now)
ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds "$WTS" > "${TMPDIR_B}/lp_y" 2>&1 &
LP_PID=$!
sleep 3
ysqs send-message --queue-url "$Y_QURL" --message-body "longpoll-${WTS}" >/dev/null 2>&1
wait $LP_PID 2>/dev/null
Y_LP_MS=$(( $(ms_now) - Y_LP_START ))
Y_LP_GOT=$(grep -c "MessageId" "${TMPDIR_B}/lp_y" || true)
# Ours
O_LP_START=$(ms_now)
osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds "$WTS" > "${TMPDIR_B}/lp_o" 2>&1 &
LP_PID=$!
sleep 3
osqs send-message --queue-url "$O_QURL" --message-body "longpoll-${WTS}" >/dev/null 2>&1
wait $LP_PID 2>/dev/null
O_LP_MS=$(( $(ms_now) - O_LP_START ))
O_LP_GOT=$(grep -c "MessageId" "${TMPDIR_B}/lp_o" || true)
printf " │ WaitTime=%-5d │ %5dms (got:%d) │ %5dms (got:%d) │\n" \
"$WTS" "$Y_LP_MS" "$Y_LP_GOT" "$O_LP_MS" "$O_LP_GOT"
done
echo " └────────────────┴──────────────────┴──────────────────┘"
# Почистим оставшиеся
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
sleep 2
# ═══════════════════════════════════════
# 7. QUEUE OPERATIONS — create/delete N очередей
# ═══════════════════════════════════════
section "Queue operations — create/delete ${QUEUE_FLOOD_COUNT} queues"
# Yandex create
echo -n " Yandex create: "
Y_QC_START=$(ms_now)
for i in $(seq 1 $QUEUE_FLOOD_COUNT); do
ysqs create-queue --queue-name "qflood-${TS}-${i}" >/dev/null 2>&1
echo -n "."
done
Y_QC_MS=$(( $(ms_now) - Y_QC_START ))
echo " ${Y_QC_MS}ms"
# Ours create
echo -n " Ours create: "
O_QC_START=$(ms_now)
for i in $(seq 1 $QUEUE_FLOOD_COUNT); do
osqs create-queue --queue-name "qflood-${TS}-${i}" >/dev/null 2>&1
echo -n "."
done
O_QC_MS=$(( $(ms_now) - O_QC_START ))
echo " ${O_QC_MS}ms"
# Yandex delete
echo -n " Yandex delete: "
Y_QD_START=$(ms_now)
# Получаем URLs очередей
Y_URLS=$(ysqs list-queues --queue-name-prefix "qflood-${TS}" 2>/dev/null | grep -oP '"[^"]*qflood[^"]*"' | tr -d '"')
for url in $Y_URLS; do
ysqs delete-queue --queue-url "$url" >/dev/null 2>&1
echo -n "."
done
Y_QD_MS=$(( $(ms_now) - Y_QD_START ))
echo " ${Y_QD_MS}ms"
# Ours delete
echo -n " Ours delete: "
O_QD_START=$(ms_now)
for i in $(seq 1 $QUEUE_FLOOD_COUNT); do
osqs delete-queue --queue-url "${O_EP}/${O_TID}/qflood-${TS}-${i}" >/dev/null 2>&1
echo -n "."
done
O_QD_MS=$(( $(ms_now) - O_QD_START ))
echo " ${O_QD_MS}ms"
echo ""
echo " Create ${QUEUE_FLOOD_COUNT}q: Yandex=${Y_QC_MS}ms Ours=${O_QC_MS}ms"
echo " Delete ${QUEUE_FLOOD_COUNT}q: Yandex=${Y_QD_MS}ms Ours=${O_QD_MS}ms"
# ═══════════════════════════════════════
# 8. ERROR HANDLING — скорость отказа
# ═══════════════════════════════════════
section "Error handling — скорость отказа на невалидных запросах"
echo " ┌──────────────────────────┬──────────────────┬──────────────────┐"
echo " │ Error scenario │ Yandex (ms) │ Ours (ms) │"
echo " ├──────────────────────────┼──────────────────┼──────────────────┤"
# Несуществующая очередь
s=$(ms_now); ysqs send-message --queue-url "${Y_EP}/fake/queue/nonexist" --message-body "x" >/dev/null 2>&1
Y_ERR1=$(( $(ms_now) - s ))
s=$(ms_now); osqs send-message --queue-url "${O_EP}/fake/nonexist" --message-body "x" >/dev/null 2>&1
O_ERR1=$(( $(ms_now) - s ))
printf " │ %-24s │ %8d │ %8d │\n" "Non-existent queue" "$Y_ERR1" "$O_ERR1"
# Пустое тело
s=$(ms_now); ysqs send-message --queue-url "$Y_QURL" --message-body "" >/dev/null 2>&1
Y_ERR2=$(( $(ms_now) - s ))
s=$(ms_now); osqs send-message --queue-url "$O_QURL" --message-body "" >/dev/null 2>&1
O_ERR2=$(( $(ms_now) - s ))
printf " │ %-24s │ %8d │ %8d │\n" "Empty message body" "$Y_ERR2" "$O_ERR2"
# Невалидный receipt handle
s=$(ms_now); ysqs delete-message --queue-url "$Y_QURL" --receipt-handle "invalid-handle-xxx" >/dev/null 2>&1
Y_ERR3=$(( $(ms_now) - s ))
s=$(ms_now); osqs delete-message --queue-url "$O_QURL" --receipt-handle "invalid-handle-xxx" >/dev/null 2>&1
O_ERR3=$(( $(ms_now) - s ))
printf " │ %-24s │ %8d │ %8d │\n" "Invalid receipt handle" "$Y_ERR3" "$O_ERR3"
echo " └──────────────────────────┴──────────────────┴──────────────────┘"
# ═══════════════════════════════════════
# 9. SUSTAINED LOAD — непрерывная нагрузка 2 мин
# ═══════════════════════════════════════
section "Sustained load — ${SUSTAINED_DURATION}s непрерывно (send+receive+delete)"
run_sustained() {
local provider="$1" sqs_func="$2" qurl="$3" duration="$4" result_file="$5"
local end=$((SECONDS + duration))
local sent=0 recv=0 del=0 errors=0
local interval=30
local next_report=$((SECONDS + interval))
while [[ $SECONDS -lt $end ]]; do
# Send
R=$($sqs_func send-message --queue-url "$qurl" --message-body "sustained-$(date +%s%N)" 2>&1)
if echo "$R" | grep -q "MessageId"; then
sent=$((sent+1))
else
errors=$((errors+1))
fi
# Receive + Delete
R=$($sqs_func receive-message --queue-url "$qurl" --max-number-of-messages 1 --wait-time-seconds 0 2>&1)
if echo "$R" | grep -q "MessageId"; then
recv=$((recv+1))
H=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+' | head -1)
if [[ -n "$H" ]]; then
$sqs_func delete-message --queue-url "$qurl" --receipt-handle "$H" >/dev/null 2>&1
del=$((del+1))
fi
fi
# Промежуточный отчёт
if [[ $SECONDS -ge $next_report ]]; then
local elapsed=$((SECONDS - (end - duration)))
echo " [${provider} ${elapsed}s] sent=$sent recv=$recv del=$del err=$errors"
next_report=$((SECONDS + interval))
fi
done
echo "${sent}:${recv}:${del}:${errors}" > "$result_file"
}
echo " Запуск Yandex sustained..."
run_sustained "YMQ" "ysqs" "$Y_QURL" "$SUSTAINED_DURATION" "${TMPDIR_B}/sus_y" &
Y_SUS_PID=$!
echo " Запуск Ours sustained..."
run_sustained "OURS" "osqs" "$O_QURL" "$SUSTAINED_DURATION" "${TMPDIR_B}/sus_o" &
O_SUS_PID=$!
echo " ⏳ Ждём ${SUSTAINED_DURATION}s..."
wait $Y_SUS_PID $O_SUS_PID 2>/dev/null
IFS=':' read -r Y_SUS_SENT Y_SUS_RECV Y_SUS_DEL Y_SUS_ERR < "${TMPDIR_B}/sus_y"
IFS=':' read -r O_SUS_SENT O_SUS_RECV O_SUS_DEL O_SUS_ERR < "${TMPDIR_B}/sus_o"
Y_SUS_RPS=$(( (Y_SUS_SENT + Y_SUS_RECV + Y_SUS_DEL) / (SUSTAINED_DURATION + 1) ))
O_SUS_RPS=$(( (O_SUS_SENT + O_SUS_RECV + O_SUS_DEL) / (SUSTAINED_DURATION + 1) ))
echo ""
echo " ┌──────────────┬─────────────────────────────────────┬─────────────────────────────────────┐"
echo " │ │ Yandex MQ │ shared-sqs │"
echo " ├──────────────┼─────────────────────────────────────┼─────────────────────────────────────┤"
printf " │ %-12s │ sent=%-4d recv=%-4d del=%-4d err=%-3d │ sent=%-4d recv=%-4d del=%-4d err=%-3d │\n" \
"Operations" "$Y_SUS_SENT" "$Y_SUS_RECV" "$Y_SUS_DEL" "$Y_SUS_ERR" \
"$O_SUS_SENT" "$O_SUS_RECV" "$O_SUS_DEL" "$O_SUS_ERR"
printf " │ %-12s │ ~%-3d ops/sec │ ~%-3d ops/sec │\n" \
"Throughput" "$Y_SUS_RPS" "$O_SUS_RPS"
echo " └──────────────┴─────────────────────────────────────┴─────────────────────────────────────┘"
# ═══════════════════════════════════════
# 10. CLEANUP
# ═══════════════════════════════════════
section "Cleanup"
ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1
ysqs delete-queue --queue-url "$Y_QURL" >/dev/null 2>&1
echo " Yandex: bench queue deleted"
osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1
osqs delete-queue --queue-url "$O_QURL" >/dev/null 2>&1
echo " Ours: bench queue deleted"
# ═══════════════════════════════════════
# ИТОГОВАЯ СВОДКА
# ═══════════════════════════════════════
echo ""
echo "╔══════════════════════════════════════════════════════════════════════╗"
echo "║ FULL BENCHMARK COMPLETE ║"
echo "║ $(date '+%Y-%m-%d %H:%M:%S') ║"
echo "║ ║"
echo "║ Latency: SendMessage avg Y=$(echo $Y_SEND_STATS | awk '{print $2}')ms O=$(echo $O_SEND_STATS | awk '{print $2}')ms"
echo "║ ReceiveMsg avg Y=$(echo $Y_RECV_STATS | awk '{print $2}')ms O=$(echo $O_RECV_STATS | awk '{print $2}')ms"
echo "║ Batch: savings Y=${Y_BATCH_GAIN}% O=${O_BATCH_GAIN}%"
echo "║ Sustained: ops/sec Y=${Y_SUS_RPS} O=${O_SUS_RPS} (${SUSTAINED_DURATION}s)"
echo "║ Errors: Y=${Y_SUS_ERR} O=${O_SUS_ERR}"
echo "║ QueueOps: create10 Y=${Y_QC_MS}ms O=${O_QC_MS}ms"
echo "║ delete10 Y=${Y_QD_MS}ms O=${O_QD_MS}ms"
echo "║ Tags: shared-sqs ✅ (3/3) Yandex ⚠️ not supported"
echo "╚══════════════════════════════════════════════════════════════════════╝"