#!/bin/bash # Updated: 2026-04-12 00:00 UTC # benchmark_compare_32k.sh — сравнительный benchmark Yandex MQ vs shared-sqs только до 32KB. set -uo pipefail # ysqs выполняет aws sqs для Yandex MQ. 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 sqs для shared-sqs. 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 возвращает текущее время в миллисекундах. ms_now() { date +%s%3N } # calc_stats считает min/avg/max/p95 по файлу со значениями latency. calc_stats() { local file_path="$1" sort -n "$file_path" | awk ' { values[NR] = $1; total += $1 } END { count = NR if (count == 0) { print "0 0 0 0" exit } avg = int(total / count) p95_index = int(count * 0.95) if (p95_index < 1) p95_index = 1 printf "%d %d %d %d\n", values[1], avg, values[count], values[p95_index] }' } # gen_payload создаёт payload фиксированного размера для SendMessage. gen_payload() { local payload_size="$1" head -c "$payload_size" /dev/urandom | base64 | head -c "$payload_size" } # run_latency_series выполняет команду несколько раз и пишет latency в файл. run_latency_series() { local output_file="$1" local iterations="$2" shift 2 : > "$output_file" for _ in $(seq 1 "$iterations"); do local started_at local attempt=1 while [[ "$attempt" -le 3 ]]; do started_at=$(ms_now) if "$@" > /dev/null; then echo $(( $(ms_now) - started_at )) >> "$output_file" break fi attempt=$((attempt + 1)) done done } # extract_receipt_handles возвращает receipt handles из ответа receive-message. extract_receipt_handles() { python3 -c 'import json,sys data=json.load(sys.stdin) for msg in data.get("Messages", []): handle = msg.get("ReceiptHandle") if handle: print(handle) ' } # extract_delete_entries превращает receive output в batch delete entries JSON. extract_delete_entries() { python3 -c 'import json,sys data=json.load(sys.stdin) entries=[] for index,msg in enumerate(data.get("Messages", []), start=1): handle = msg.get("ReceiptHandle") if handle: entries.append({"Id": str(index), "ReceiptHandle": handle}) print(json.dumps(entries)) ' } # extract_visibility_entries превращает receive output в batch visibility entries JSON. extract_visibility_entries() { python3 -c 'import json,sys data=json.load(sys.stdin) entries=[] for index,msg in enumerate(data.get("Messages", []), start=1): handle = msg.get("ReceiptHandle") if handle: entries.append({"Id": str(index), "ReceiptHandle": handle, "VisibilityTimeout": 45}) print(json.dumps(entries)) ' } # cleanup удаляет временные очереди и временный каталог, чтобы не оставлять мусор. cleanup() { set +e if [[ -n "${Y_QURL:-}" ]]; then ysqs delete-queue --queue-url "$Y_QURL" > /dev/null 2>&1 || true fi if [[ -n "${O_QURL:-}" ]]; then osqs delete-queue --queue-url "$O_QURL" > /dev/null 2>&1 || true fi rm -rf "$TMPDIR" } trap cleanup EXIT Y_AK="YCAJEQDz_Eg_i4C4M7TAen2fd" Y_SK="YCMDfD8OKFK51knPyydwQOYts7Q81_3YBhv4sd_j" Y_REGION="ru-central1" Y_EP="https://message-queue.api.cloud.yandex.net" O_AK="SSAK-ed0b0c64dcc135adad9e11be" O_SK="f917c133e4ad74cf37c8e3ba29d74a39f42f117a202bd672436c92fdb6b6f3dc" O_REGION="us-east-1" O_EP="https://qu.kube5s.ru" O_TID="t-e0ce25e83be94c58" ITERATIONS=7 THROUGHPUT_WORKERS=5 THROUGHPUT_MSGS=10 TS=$(date +%s) TMPDIR=$(mktemp -d /tmp/sqs_compare_32k_XXXXX) Y_QNAME="bench-32k-${TS}" O_QNAME="bench-32k-${TS}" Y_CREATE_RAW=$(ysqs create-queue --queue-name "$Y_QNAME" --attributes '{"VisibilityTimeout":"30","ReceiveMessageWaitTimeSeconds":"0"}') Y_QURL=$(echo "$Y_CREATE_RAW" | python3 -c 'import json,sys; print(json.load(sys.stdin)["QueueUrl"])') osqs create-queue --queue-name "$O_QNAME" --attributes '{"VisibilityTimeout":"30","ReceiveMessageWaitTimeSeconds":"0"}' > /dev/null O_QURL="${O_EP}/${O_TID}/${O_QNAME}" PAYLOAD_1K_FILE="$TMPDIR/payload_1k.txt" PAYLOAD_10K_FILE="$TMPDIR/payload_10k.txt" PAYLOAD_32K_FILE="$TMPDIR/payload_32k.txt" gen_payload 1024 > "$PAYLOAD_1K_FILE" gen_payload 10240 > "$PAYLOAD_10K_FILE" gen_payload 32768 > "$PAYLOAD_32K_FILE" echo "# Benchmark 32KB Max" echo "generated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "iterations=${ITERATIONS}" echo "throughput_workers=${THROUGHPUT_WORKERS}" echo "throughput_msgs=${THROUGHPUT_MSGS}" echo "" echo "## api_latency_ms" echo "operation|provider|min|avg|max|p95|note" run_latency_series "$TMPDIR/y_get_url.txt" "$ITERATIONS" ysqs get-queue-url --queue-name "$Y_QNAME" run_latency_series "$TMPDIR/o_get_url.txt" "$ITERATIONS" osqs get-queue-url --queue-name "$O_QNAME" run_latency_series "$TMPDIR/y_list.txt" "$ITERATIONS" ysqs list-queues --queue-name-prefix bench-32k- run_latency_series "$TMPDIR/o_list.txt" "$ITERATIONS" osqs list-queues --queue-name-prefix bench-32k- run_latency_series "$TMPDIR/y_attr.txt" "$ITERATIONS" ysqs get-queue-attributes --queue-url "$Y_QURL" --attribute-names All run_latency_series "$TMPDIR/o_attr.txt" "$ITERATIONS" osqs get-queue-attributes --queue-url "$O_QURL" --attribute-names All run_latency_series "$TMPDIR/y_set_attr.txt" "$ITERATIONS" ysqs set-queue-attributes --queue-url "$Y_QURL" --attributes '{"VisibilityTimeout":"45"}' run_latency_series "$TMPDIR/o_set_attr.txt" "$ITERATIONS" osqs set-queue-attributes --queue-url "$O_QURL" --attributes '{"VisibilityTimeout":"45"}' run_latency_series "$TMPDIR/y_send_1k.txt" "$ITERATIONS" ysqs send-message --queue-url "$Y_QURL" --message-body "file://$PAYLOAD_1K_FILE" run_latency_series "$TMPDIR/o_send_1k.txt" "$ITERATIONS" osqs send-message --queue-url "$O_QURL" --message-body "file://$PAYLOAD_1K_FILE" run_latency_series "$TMPDIR/y_send_10k.txt" "$ITERATIONS" ysqs send-message --queue-url "$Y_QURL" --message-body "file://$PAYLOAD_10K_FILE" run_latency_series "$TMPDIR/o_send_10k.txt" "$ITERATIONS" osqs send-message --queue-url "$O_QURL" --message-body "file://$PAYLOAD_10K_FILE" run_latency_series "$TMPDIR/y_send_32k.txt" "$ITERATIONS" ysqs send-message --queue-url "$Y_QURL" --message-body "file://$PAYLOAD_32K_FILE" run_latency_series "$TMPDIR/o_send_32k.txt" "$ITERATIONS" osqs send-message --queue-url "$O_QURL" --message-body "file://$PAYLOAD_32K_FILE" BATCH_ENTRIES='[{"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"},{"Id":"10","MessageBody":"b10"}]' run_latency_series "$TMPDIR/y_batch_send.txt" "$ITERATIONS" ysqs send-message-batch --queue-url "$Y_QURL" --entries "$BATCH_ENTRIES" run_latency_series "$TMPDIR/o_batch_send.txt" "$ITERATIONS" osqs send-message-batch --queue-url "$O_QURL" --entries "$BATCH_ENTRIES" for index in $(seq 1 "$ITERATIONS"); do ysqs send-message --queue-url "$Y_QURL" --message-body "recv-y-$index" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "recv-o-$index" > /dev/null done : > "$TMPDIR/y_receive.txt" : > "$TMPDIR/o_receive.txt" : > "$TMPDIR/y_delete.txt" : > "$TMPDIR/o_delete.txt" for _ in $(seq 1 "$ITERATIONS"); do started_at=$(ms_now) y_receive_raw=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds 1) echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_receive.txt" y_handle=$(echo "$y_receive_raw" | extract_receipt_handles | head -1) if [[ -n "$y_handle" ]]; then started_at=$(ms_now) ysqs delete-message --queue-url "$Y_QURL" --receipt-handle "$y_handle" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_delete.txt" fi started_at=$(ms_now) o_receive_raw=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds 1) echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_receive.txt" o_handle=$(echo "$o_receive_raw" | extract_receipt_handles | head -1) if [[ -n "$o_handle" ]]; then started_at=$(ms_now) osqs delete-message --queue-url "$O_QURL" --receipt-handle "$o_handle" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_delete.txt" fi done for index in $(seq 1 10); do ysqs send-message --queue-url "$Y_QURL" --message-body "batch-delete-y-$index" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "batch-delete-o-$index" > /dev/null done y_batch_delete_receive=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 10 --wait-time-seconds 1) o_batch_delete_receive=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 10 --wait-time-seconds 1) y_delete_entries=$(echo "$y_batch_delete_receive" | extract_delete_entries) o_delete_entries=$(echo "$o_batch_delete_receive" | extract_delete_entries) : > "$TMPDIR/y_batch_delete.txt" : > "$TMPDIR/o_batch_delete.txt" for _ in $(seq 1 "$ITERATIONS"); do for index in $(seq 1 10); do ysqs send-message --queue-url "$Y_QURL" --message-body "batch-delete-y-${RANDOM}-${index}" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "batch-delete-o-${RANDOM}-${index}" > /dev/null done y_batch_delete_receive=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 10 --wait-time-seconds 1) o_batch_delete_receive=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 10 --wait-time-seconds 1) y_delete_entries=$(echo "$y_batch_delete_receive" | extract_delete_entries) o_delete_entries=$(echo "$o_batch_delete_receive" | extract_delete_entries) started_at=$(ms_now) ysqs delete-message-batch --queue-url "$Y_QURL" --entries "$y_delete_entries" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_batch_delete.txt" started_at=$(ms_now) osqs delete-message-batch --queue-url "$O_QURL" --entries "$o_delete_entries" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_batch_delete.txt" done for index in $(seq 1 "$ITERATIONS"); do ysqs send-message --queue-url "$Y_QURL" --message-body "visibility-y-$index" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "visibility-o-$index" > /dev/null done : > "$TMPDIR/y_visibility.txt" : > "$TMPDIR/o_visibility.txt" for _ in $(seq 1 "$ITERATIONS"); do y_visibility_receive=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 1 --wait-time-seconds 1) y_visibility_handle=$(echo "$y_visibility_receive" | extract_receipt_handles | head -1) if [[ -n "$y_visibility_handle" ]]; then started_at=$(ms_now) ysqs change-message-visibility --queue-url "$Y_QURL" --receipt-handle "$y_visibility_handle" --visibility-timeout 45 > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_visibility.txt" fi o_visibility_receive=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 1 --wait-time-seconds 1) o_visibility_handle=$(echo "$o_visibility_receive" | extract_receipt_handles | head -1) if [[ -n "$o_visibility_handle" ]]; then started_at=$(ms_now) osqs change-message-visibility --queue-url "$O_QURL" --receipt-handle "$o_visibility_handle" --visibility-timeout 45 > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_visibility.txt" fi done : > "$TMPDIR/y_visibility_batch.txt" : > "$TMPDIR/o_visibility_batch.txt" for _ in $(seq 1 "$ITERATIONS"); do for index in $(seq 1 10); do ysqs send-message --queue-url "$Y_QURL" --message-body "visibility-batch-y-${RANDOM}-${index}" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "visibility-batch-o-${RANDOM}-${index}" > /dev/null done y_visibility_batch_receive=$(ysqs receive-message --queue-url "$Y_QURL" --max-number-of-messages 10 --wait-time-seconds 1) o_visibility_batch_receive=$(osqs receive-message --queue-url "$O_QURL" --max-number-of-messages 10 --wait-time-seconds 1) y_visibility_entries=$(echo "$y_visibility_batch_receive" | extract_visibility_entries) o_visibility_entries=$(echo "$o_visibility_batch_receive" | extract_visibility_entries) started_at=$(ms_now) ysqs change-message-visibility-batch --queue-url "$Y_QURL" --entries "$y_visibility_entries" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_visibility_batch.txt" started_at=$(ms_now) osqs change-message-visibility-batch --queue-url "$O_QURL" --entries "$o_visibility_entries" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_visibility_batch.txt" done : > "$TMPDIR/y_purge.txt" : > "$TMPDIR/o_purge.txt" ysqs send-message --queue-url "$Y_QURL" --message-body "purge-y-${RANDOM}" > /dev/null osqs send-message --queue-url "$O_QURL" --message-body "purge-o-${RANDOM}" > /dev/null started_at=$(ms_now) ysqs purge-queue --queue-url "$Y_QURL" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/y_purge.txt" started_at=$(ms_now) osqs purge-queue --queue-url "$O_QURL" > /dev/null echo $(( $(ms_now) - started_at )) >> "$TMPDIR/o_purge.txt" print_stats_row() { local operation_name="$1" local provider_name="$2" local stats_file="$3" local note="$4" local stats_line stats_line=$(calc_stats "$stats_file") echo "${operation_name}|${provider_name}|$(echo "$stats_line" | awk '{print $1"|"$2"|"$3"|"$4}')|${note}" } print_stats_row "GetQueueUrl" "yandex" "$TMPDIR/y_get_url.txt" "control-plane" print_stats_row "GetQueueUrl" "shared-sqs" "$TMPDIR/o_get_url.txt" "control-plane" print_stats_row "ListQueues" "yandex" "$TMPDIR/y_list.txt" "control-plane" print_stats_row "ListQueues" "shared-sqs" "$TMPDIR/o_list.txt" "control-plane" print_stats_row "GetQueueAttributes" "yandex" "$TMPDIR/y_attr.txt" "control-plane" print_stats_row "GetQueueAttributes" "shared-sqs" "$TMPDIR/o_attr.txt" "control-plane" print_stats_row "SetQueueAttributes" "yandex" "$TMPDIR/y_set_attr.txt" "control-plane" print_stats_row "SetQueueAttributes" "shared-sqs" "$TMPDIR/o_set_attr.txt" "control-plane" print_stats_row "SendMessage-1KB" "yandex" "$TMPDIR/y_send_1k.txt" "payload" print_stats_row "SendMessage-1KB" "shared-sqs" "$TMPDIR/o_send_1k.txt" "payload" print_stats_row "SendMessage-10KB" "yandex" "$TMPDIR/y_send_10k.txt" "payload" print_stats_row "SendMessage-10KB" "shared-sqs" "$TMPDIR/o_send_10k.txt" "payload" print_stats_row "SendMessage-32KB" "yandex" "$TMPDIR/y_send_32k.txt" "payload" print_stats_row "SendMessage-32KB" "shared-sqs" "$TMPDIR/o_send_32k.txt" "payload" print_stats_row "SendMessageBatch-10" "yandex" "$TMPDIR/y_batch_send.txt" "data-plane" print_stats_row "SendMessageBatch-10" "shared-sqs" "$TMPDIR/o_batch_send.txt" "data-plane" print_stats_row "ReceiveMessage" "yandex" "$TMPDIR/y_receive.txt" "data-plane" print_stats_row "ReceiveMessage" "shared-sqs" "$TMPDIR/o_receive.txt" "data-plane" print_stats_row "DeleteMessage" "yandex" "$TMPDIR/y_delete.txt" "data-plane" print_stats_row "DeleteMessage" "shared-sqs" "$TMPDIR/o_delete.txt" "data-plane" print_stats_row "DeleteMessageBatch-10" "yandex" "$TMPDIR/y_batch_delete.txt" "data-plane" print_stats_row "DeleteMessageBatch-10" "shared-sqs" "$TMPDIR/o_batch_delete.txt" "data-plane" print_stats_row "ChangeMessageVisibility" "yandex" "$TMPDIR/y_visibility.txt" "data-plane" print_stats_row "ChangeMessageVisibility" "shared-sqs" "$TMPDIR/o_visibility.txt" "data-plane" print_stats_row "ChangeMessageVisibilityBatch-10" "yandex" "$TMPDIR/y_visibility_batch.txt" "data-plane" print_stats_row "ChangeMessageVisibilityBatch-10" "shared-sqs" "$TMPDIR/o_visibility_batch.txt" "data-plane" print_stats_row "PurgeQueue" "yandex" "$TMPDIR/y_purge.txt" "control-plane" print_stats_row "PurgeQueue" "shared-sqs" "$TMPDIR/o_purge.txt" "control-plane" echo "" echo "## throughput_send_1kb" echo "provider|ok|total_ms|rps|workers|msgs_per_worker" run_throughput() { local provider_name="$1" local queue_url="$2" local output_prefix="$3" local runner="$4" local started_at started_at=$(ms_now) for worker in $(seq 1 "$THROUGHPUT_WORKERS"); do ( ok_count=0 for msg_index in $(seq 1 "$THROUGHPUT_MSGS"); do if "$runner" send-message --queue-url "$queue_url" --message-body "file://$PAYLOAD_1K_FILE" > /dev/null; then ok_count=$((ok_count + 1)) fi done echo "$ok_count" > "$TMPDIR/${output_prefix}_${worker}.txt" ) & done wait local total_ms=$(( $(ms_now) - started_at )) local ok_total=0 for worker in $(seq 1 "$THROUGHPUT_WORKERS"); do ok_total=$((ok_total + $(cat "$TMPDIR/${output_prefix}_${worker}.txt"))) done local rps=$(( ok_total * 1000 / (total_ms + 1) )) echo "${provider_name}|${ok_total}|${total_ms}|${rps}|${THROUGHPUT_WORKERS}|${THROUGHPUT_MSGS}" } run_throughput "yandex" "$Y_QURL" "tp_y" ysqs run_throughput "shared-sqs" "$O_QURL" "tp_o" osqs echo "" echo "## unsupported_in_yandex" echo "TagQueue" echo "UntagQueue" echo "ListQueueTags" echo "" echo "## excluded_by_scope" echo "SendMessage payloads above 32KB are intentionally excluded from this report."