#!/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 "╚══════════════════════════════════════════════════════════════════════╝"