#!/bin/bash # compare_sqs.sh — сравнительный бенчмарк Yandex MQ vs shared-sqs # Запуск из ЛОКАЛИ для равных условий # 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_ENDPOINT="https://message-queue.api.cloud.yandex.net" Y_QURL="https://message-queue.api.cloud.yandex.net/b1gatc4m3hv1ldldhljp/dj60000000k2jb2o02mt/foropus" # Our SQS O_AK="SSAK-ed0b0c64dcc135adad9e11be" O_SK="f917c133e4ad74cf37c8e3ba29d74a39f42f117a202bd672436c92fdb6b6f3dc" O_REGION="us-east-1" O_ENDPOINT="https://qu.kube5s.ru" O_QURL="https://qu.kube5s.ru/t-e0ce25e83be94c58/foropus-test" # ═══ ПАРАМЕТРЫ ═══ SEQ_COUNT=20 # последовательных send/receive PAR_WORKERS=10 # параллельных воркеров PAR_MSGS=5 # сообщений на воркер (итого 50) BURST=30 # burst одновременно TMPDIR_CMP=$(mktemp -d /tmp/sqs_compare_XXXXX) trap "rm -rf $TMPDIR_CMP" EXIT # ═══ ХЕЛПЕРЫ ═══ ysqs() { AWS_ACCESS_KEY_ID="$Y_AK" AWS_SECRET_ACCESS_KEY="$Y_SK" \ AWS_DEFAULT_REGION="$Y_REGION" \ aws --endpoint-url "$Y_ENDPOINT" --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_ENDPOINT" --output json sqs "$@" 2>&1 } # ms_since — миллисекунды с момента $1 ms_now() { date +%s%3N; } echo "╔═══════════════════════════════════════════════════════════════════╗" echo "║ BENCHMARK: Yandex MQ vs shared-sqs — $(date '+%Y-%m-%d %H:%M:%S') ║" echo "║ Из локали, равные условия ║" echo "╚═══════════════════════════════════════════════════════════════════╝" echo "" # ═══════════════════════════════════════ # 1. SEQUENTIAL SEND — latency per message # ═══════════════════════════════════════ echo "═══ 1. Sequential SendMessage (${SEQ_COUNT} msgs) ═══" # Yandex echo -n " Yandex: " Y_START=$(ms_now) Y_SEND_OK=0 for i in $(seq 1 $SEQ_COUNT); do R=$(ysqs send-message --queue-url "$Y_QURL" --message-body "bench-seq-${i}") if echo "$R" | grep -q "MessageId"; then Y_SEND_OK=$((Y_SEND_OK+1)); fi echo -n "." done Y_SEND_MS=$(( $(ms_now) - Y_START )) Y_SEND_AVG=$(( Y_SEND_MS / SEQ_COUNT )) echo " ${Y_SEND_OK}/${SEQ_COUNT} ok, ${Y_SEND_MS}ms total, avg=${Y_SEND_AVG}ms" # Ours echo -n " Ours: " O_START=$(ms_now) O_SEND_OK=0 for i in $(seq 1 $SEQ_COUNT); do R=$(osqs send-message --queue-url "$O_QURL" --message-body "bench-seq-${i}") if echo "$R" | grep -q "MessageId"; then O_SEND_OK=$((O_SEND_OK+1)); fi echo -n "." done O_SEND_MS=$(( $(ms_now) - O_START )) O_SEND_AVG=$(( O_SEND_MS / SEQ_COUNT )) echo " ${O_SEND_OK}/${SEQ_COUNT} ok, ${O_SEND_MS}ms total, avg=${O_SEND_AVG}ms" echo "" # ═══════════════════════════════════════ # 2. SEQUENTIAL RECEIVE — latency per message # ═══════════════════════════════════════ echo "═══ 2. Sequential ReceiveMessage (${SEQ_COUNT} msgs) ═══" # Yandex echo -n " Yandex: " Y_START=$(ms_now) Y_RECV_OK=0 for i in $(seq 1 $SEQ_COUNT); do R=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds 2) if echo "$R" | grep -q "MessageId"; then Y_RECV_OK=$((Y_RECV_OK+1)) H=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+') ysqs delete-message --queue-url "$Y_QURL" --receipt-handle "$H" >/dev/null 2>&1 fi echo -n "." done Y_RECV_MS=$(( $(ms_now) - Y_START )) Y_RECV_AVG=$(( Y_RECV_MS / SEQ_COUNT )) echo " ${Y_RECV_OK}/${SEQ_COUNT} ok, ${Y_RECV_MS}ms total, avg=${Y_RECV_AVG}ms" # Ours echo -n " Ours: " O_START=$(ms_now) O_RECV_OK=0 for i in $(seq 1 $SEQ_COUNT); do R=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds 2) if echo "$R" | grep -q "MessageId"; then O_RECV_OK=$((O_RECV_OK+1)) H=$(echo "$R" | grep -oP '"ReceiptHandle"\s*:\s*"\K[^"]+') osqs delete-message --queue-url "$O_QURL" --receipt-handle "$H" >/dev/null 2>&1 fi echo -n "." done O_RECV_MS=$(( $(ms_now) - O_START )) O_RECV_AVG=$(( O_RECV_MS / SEQ_COUNT )) echo " ${O_RECV_OK}/${SEQ_COUNT} ok, ${O_RECV_MS}ms total, avg=${O_RECV_AVG}ms" echo "" # ═══════════════════════════════════════ # 3. PARALLEL SEND — throughput # ═══════════════════════════════════════ TOTAL_PAR=$((PAR_WORKERS * PAR_MSGS)) echo "═══ 3. Parallel SendMessage (${PAR_WORKERS} workers × ${PAR_MSGS} = ${TOTAL_PAR} msgs) ═══" # Yandex echo -n " Yandex: " Y_START=$(ms_now) for w in $(seq 1 $PAR_WORKERS); do ( ok=0 for m in $(seq 1 $PAR_MSGS); do R=$(ysqs send-message --queue-url "$Y_QURL" --message-body "par-${w}-${m}") if echo "$R" | grep -q "MessageId"; then ok=$((ok+1)); fi done echo "$ok" > "${TMPDIR_CMP}/y_par_${w}" ) & done wait Y_PAR_MS=$(( $(ms_now) - Y_START )) Y_PAR_OK=0 for w in $(seq 1 $PAR_WORKERS); do [[ -f "${TMPDIR_CMP}/y_par_${w}" ]] && Y_PAR_OK=$((Y_PAR_OK + $(cat "${TMPDIR_CMP}/y_par_${w}"))) done Y_PAR_RPS=$(( Y_PAR_OK * 1000 / Y_PAR_MS )) echo "${Y_PAR_OK}/${TOTAL_PAR} ok, ${Y_PAR_MS}ms, ~${Y_PAR_RPS} msg/sec" # Ours echo -n " Ours: " O_START=$(ms_now) for w in $(seq 1 $PAR_WORKERS); do ( ok=0 for m in $(seq 1 $PAR_MSGS); do R=$(osqs send-message --queue-url "$O_QURL" --message-body "par-${w}-${m}") if echo "$R" | grep -q "MessageId"; then ok=$((ok+1)); fi done echo "$ok" > "${TMPDIR_CMP}/o_par_${w}" ) & done wait O_PAR_MS=$(( $(ms_now) - O_START )) O_PAR_OK=0 for w in $(seq 1 $PAR_WORKERS); do [[ -f "${TMPDIR_CMP}/o_par_${w}" ]] && O_PAR_OK=$((O_PAR_OK + $(cat "${TMPDIR_CMP}/o_par_${w}"))) done O_PAR_RPS=$(( O_PAR_OK * 1000 / O_PAR_MS )) echo "${O_PAR_OK}/${TOTAL_PAR} ok, ${O_PAR_MS}ms, ~${O_PAR_RPS} msg/sec" echo "" # ═══════════════════════════════════════ # 4. BURST — все сразу # ═══════════════════════════════════════ echo "═══ 4. Burst SendMessage (${BURST} одновременно) ═══" # Yandex echo -n " Yandex: " Y_START=$(ms_now) for b in $(seq 1 $BURST); do ( R=$(ysqs send-message --queue-url "$Y_QURL" --message-body "burst-${b}") echo "$R" | grep -q "MessageId" && echo 1 || echo 0 ) > "${TMPDIR_CMP}/y_burst_${b}" & done wait Y_BURST_MS=$(( $(ms_now) - Y_START )) Y_BURST_OK=0 for b in $(seq 1 $BURST); do [[ -f "${TMPDIR_CMP}/y_burst_${b}" ]] && Y_BURST_OK=$((Y_BURST_OK + $(cat "${TMPDIR_CMP}/y_burst_${b}"))) done echo "${Y_BURST_OK}/${BURST} ok, ${Y_BURST_MS}ms" # Ours echo -n " Ours: " O_START=$(ms_now) for b in $(seq 1 $BURST); do ( R=$(osqs send-message --queue-url "$O_QURL" --message-body "burst-${b}") echo "$R" | grep -q "MessageId" && echo 1 || echo 0 ) > "${TMPDIR_CMP}/o_burst_${b}" & done wait O_BURST_MS=$(( $(ms_now) - O_START )) O_BURST_OK=0 for b in $(seq 1 $BURST); do [[ -f "${TMPDIR_CMP}/o_burst_${b}" ]] && O_BURST_OK=$((O_BURST_OK + $(cat "${TMPDIR_CMP}/o_burst_${b}"))) done echo "${O_BURST_OK}/${BURST} ok, ${O_BURST_MS}ms" echo "" # ═══════════════════════════════════════ # 5. GetQueueAttributes — latency # ═══════════════════════════════════════ echo "═══ 5. GetQueueAttributes (5 раз) ═══" echo -n " Yandex: " Y_START=$(ms_now) for i in $(seq 1 5); do ysqs get-queue-attributes --queue-url "$Y_QURL" --attribute-names ApproximateNumberOfMessages >/dev/null 2>&1 echo -n "." done Y_ATTR_MS=$(( $(ms_now) - Y_START )) Y_ATTR_AVG=$(( Y_ATTR_MS / 5 )) echo " ${Y_ATTR_MS}ms total, avg=${Y_ATTR_AVG}ms" echo -n " Ours: " O_START=$(ms_now) for i in $(seq 1 5); do osqs get-queue-attributes --queue-url "$O_QURL" --attribute-names ApproximateNumberOfMessages >/dev/null 2>&1 echo -n "." done O_ATTR_MS=$(( $(ms_now) - O_START )) O_ATTR_AVG=$(( O_ATTR_MS / 5 )) echo " ${O_ATTR_MS}ms total, avg=${O_ATTR_AVG}ms" echo "" # ═══════════════════════════════════════ # 6. PURGE — очистка после тестов # ═══════════════════════════════════════ echo "═══ 6. Purge (очистка) ═══" ysqs purge-queue --queue-url "$Y_QURL" >/dev/null 2>&1 && echo " Yandex: purged" || echo " Yandex: purge failed (cooldown?)" osqs purge-queue --queue-url "$O_QURL" >/dev/null 2>&1 && echo " Ours: purged" || echo " Ours: purge failed" echo "" # ═══════════════════════════════════════ # ИТОГОВАЯ ТАБЛИЦА # ═══════════════════════════════════════ echo "╔══════════════════════════════════════════════════════════════════════════╗" echo "║ РЕЗУЛЬТАТЫ СРАВНЕНИЯ ║" echo "╠══════════════════════════╦══════════════════╦══════════════════╦════════╣" echo "║ Тест ║ Yandex MQ ║ shared-sqs ║ Winner ║" echo "╠══════════════════════════╬══════════════════╬══════════════════╬════════╣" printf "║ %-24s ║ %4dms avg %2d/%2d ║ %4dms avg %2d/%2d ║ %-6s ║\n" \ "Seq Send (${SEQ_COUNT}msg)" "$Y_SEND_AVG" "$Y_SEND_OK" "$SEQ_COUNT" "$O_SEND_AVG" "$O_SEND_OK" "$SEQ_COUNT" \ "$([ $Y_SEND_AVG -le $O_SEND_AVG ] && echo 'YMQ' || echo 'OURS')" printf "║ %-24s ║ %4dms avg %2d/%2d ║ %4dms avg %2d/%2d ║ %-6s ║\n" \ "Seq Recv+Del (${SEQ_COUNT}msg)" "$Y_RECV_AVG" "$Y_RECV_OK" "$SEQ_COUNT" "$O_RECV_AVG" "$O_RECV_OK" "$SEQ_COUNT" \ "$([ $Y_RECV_AVG -le $O_RECV_AVG ] && echo 'YMQ' || echo 'OURS')" printf "║ %-24s ║ %4dms %3d msg/s ║ %4dms %3d msg/s ║ %-6s ║\n" \ "Parallel Send (${TOTAL_PAR}msg)" "$Y_PAR_MS" "$Y_PAR_RPS" "$O_PAR_MS" "$O_PAR_RPS" \ "$([ $Y_PAR_RPS -ge $O_PAR_RPS ] && echo 'YMQ' || echo 'OURS')" printf "║ %-24s ║ %4dms %2d/%2d ║ %4dms %2d/%2d ║ %-6s ║\n" \ "Burst (${BURST} simultaneous)" "$Y_BURST_MS" "$Y_BURST_OK" "$BURST" "$O_BURST_MS" "$O_BURST_OK" "$BURST" \ "$([ $Y_BURST_MS -le $O_BURST_MS ] && echo 'YMQ' || echo 'OURS')" printf "║ %-24s ║ %4dms avg ║ %4dms avg ║ %-6s ║\n" \ "GetQueueAttributes (5x)" "$Y_ATTR_AVG" "$O_ATTR_AVG" \ "$([ $Y_ATTR_AVG -le $O_ATTR_AVG ] && echo 'YMQ' || echo 'OURS')" echo "╚══════════════════════════╩══════════════════╩══════════════════╩════════╝"