From a502d7890af549ce39bfdfdb373b816273540d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sat, 15 Aug 2026 09:17:04 +0400 Subject: [PATCH] =?UTF-8?q?HISTORY:=20P1.4/P2.5/P3.8=20PASS,=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=20api=5Ftest;=20+cli=5Fload=5Ftest.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY/2026-08-14-session-log.md | 33 +++++++++++++++++ tests/cli_load_test.py | 59 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/cli_load_test.py diff --git a/HISTORY/2026-08-14-session-log.md b/HISTORY/2026-08-14-session-log.md index b223afc..94a8cec 100644 --- a/HISTORY/2026-08-14-session-log.md +++ b/HISTORY/2026-08-14-session-log.md @@ -823,3 +823,36 @@ SIGKILL/рестарте пода запись не успевает → оче удалённые НЕ вернулись (осталась только сирота от первого FAIL-прогона DLQ — удалена вручную). Фикс работает. + +## 2026-08-15 (продолжение) — план Соннета: P1.4, P2.5, P3.7, P3.8, фикс api_test + +**Регрессия api_test после фиксов удалений (найдена и закрыта)**: +- После деплоя синхронных удалений api_test стал 21/22: FAIL на DeleteMessage + (ReceiptHandleIsInvalid), стабильно 2 прогона. +- Диагностика: тест делал receive → ChangeMessageVisibility(timeout=**1с**) → delete. + По AWS-семантике через 1с сообщение снова видимо и handle сбрасывается → + delete по старому handle → ReceiptHandleIsInvalid. Сетевые задержки >1с + (в т.ч. шум MTU-проблемы платформы) приводили к случайному FAIL. +- Замеры: CV=1с → run1 FAIL, runs 2-3 OK; CV=5с → **5/5 OK (fails=0/5)**. + Сервис корректен (AWS-семантика), тест использовал слишком агрессивный таймаут. +- Фикс: `tests/api_test.sh` — `--visibility-timeout 1` → `5` + комментарий. +- Прогон после фикса: **22/22**. Коммит bdc7336, запушен. + +**P1.4 long-poll leak (200 клиентов)**: +- 200 одновременных receive с WaitTimeSeconds=20 (boto3, без ретраев), очередь + longpoll-leak-*, после — cleanup. Ошибок клиента: 0. +- Threads пода: 12 до → **13 после**. Утечки горутин/потоков НЕТ. PASS. + +**P2.5 CLI v2 в нагрузке** (`tests/cli_load_test.py`, на ВМ): +- 50 циклов aws cli send/receive/delete = 150 операций, failed=0. +- Латентность: send p50=775мс p95=936мс; receive p50=761мс p95=826мс; + delete p50=761мс p95=839мс. PASS. + +**P3.8 граница лимита очередей (DefaultTenantMaxQueues=50)**: +- Создание до отказа: ровно **50** создаются, **51-я → LimitExceeded** + (AWS.SimpleQueueService.LimitExceeded). После cleanup 0 очередей. PASS. + +**P3.7 RSS 2+ часа (запущен фоновый мониторинг)**: +- nohup на ВМ: каждые 60с пишет VmRSS/Threads в `rss_monitor.log` (125 сэмплов). +- Стартовый сэмпл 08:16 +0300: VmRSS=16588 kB, Threads=13. +- Результат — после завершения мониторинга. diff --git a/tests/cli_load_test.py b/tests/cli_load_test.py new file mode 100644 index 0000000..3dbc262 --- /dev/null +++ b/tests/cli_load_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""CLI v2 (aws cli) в нагрузке: 50 циклов send/receive/delete с замером латентности. +Запуск на ВМ: python3 tests/cli_load_test.py""" +import boto3 +import subprocess +import time +import os +import re + +ENV = dict(os.environ) +EP = 'https://sqs.containerk8s.dev.nubes.ru' +sqs = boto3.client('sqs', endpoint_url=EP, region_name='us-east-1') + + +def cli(*a, tmo=20): + t = time.time() + p = subprocess.run(['aws', '--endpoint-url', EP, '--region', 'us-east-1', 'sqs', *a], + env=ENV, capture_output=True, text=True, timeout=tmo) + return p, time.time() - t + + +def main(): + q = 'cliload-%d' % int(time.time()) + sqs.create_queue(QueueName=q) + u = EP + '/' + q + lat = {'send': [], 'receive': [], 'delete': []} + failed = 0 + for i in range(50): + p, t = cli('send-message', '--queue-url', u, '--message-body', 'msg-%d' % i) + lat['send'].append(t) + if p.returncode != 0: + failed += 1 + print('send FAIL', i, p.stderr[:120]) + continue + p, t = cli('receive-message', '--queue-url', u, '--max-number-of-messages', '1', '--visibility-timeout', '3') + lat['receive'].append(t) + m = re.search(r'"ReceiptHandle":\s*"([^"]+)"', p.stdout) + if not m: + failed += 1 + print('receive FAIL', i, p.stdout[:100], p.stderr[:100]) + continue + p, t = cli('delete-message', '--queue-url', u, '--receipt-handle', m.group(1)) + lat['delete'].append(t) + if p.returncode != 0: + failed += 1 + print('delete FAIL', i, p.stderr[:120]) + for k in lat: + v = sorted(lat[k]) + n = len(v) + if n: + print('%s: n=%d p50=%.0fms p95=%.0fms max=%.0fms' % (k, n, v[n // 2] * 1000, v[int(n * .95)] * 1000, v[-1] * 1000)) + else: + print('%s: n=0' % k) + sqs.delete_queue(QueueUrl=u) + print('CLI load done, failed=%d' % failed) + + +if __name__ == '__main__': + main()