From f33134d0c19c10a397b0f0a3f9422d11978b1d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sat, 15 Aug 2026 20:38:01 +0400 Subject: [PATCH] =?UTF-8?q?HISTORY:=20=D0=BF=D0=B5=D1=80=D0=B8=D0=BE=D0=B4?= =?UTF-8?q?=20=D1=82=D0=B0=D0=B9=D0=BC=D0=B0=D1=83=D1=82=D0=BE=D0=B2=2032.?= =?UTF-8?q?9=D1=81=20(18=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=2032.7-33.0=D1=81)=20=E2=80=94=20=D1=82?= =?UTF-8?q?=D0=B0=D0=B9=D0=BC=D0=B5=D1=80=20=D0=B4=D0=BE=D0=BA=D0=B0=D0=B7?= =?UTF-8?q?=D0=B0=D0=BD;=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20=D1=81=D0=BA=D1=80?= =?UTF-8?q?=D0=B8=D0=BF=D1=82=D0=BE=D0=B2=20=D0=B8=20=D0=BA=D1=80=D0=B5?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=20OK;=20+tcp=5Fmtu=5Fprobe.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY/2026-08-14-session-log.md | 21 ++++++++ tests/tcp_mtu_probe.py | 90 +++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 tests/tcp_mtu_probe.py diff --git a/HISTORY/2026-08-14-session-log.md b/HISTORY/2026-08-14-session-log.md index f13b5ca..99e6794 100644 --- a/HISTORY/2026-08-14-session-log.md +++ b/HISTORY/2026-08-14-session-log.md @@ -1124,3 +1124,24 @@ InvalidClientTokenId. С ВМ я всегда экспортировал кре запрос доходит до пода корректно (SignatureDoesNotMatch), auth работает. Урок: в boto3 всегда передавать aws_access_key_id/aws_secret_access_key явно, не полагаться на env терминала. + +## 2026-08-15 — ПЕРИОД ТАЙМАУТОВ ИЗМЕРЕН: ровно ~32.9с + полная проверка + +**SQS-цикл 10 мин (Фаза 1, локаль, явные креды shared):** 348 раундов +(receive→delete→send 512б), 19 ReadTimeout (30с). Интервалы между сбоями: +32.8, 32.8, 32.8, 32.9, 32.8, 32.9, 32.9, 32.9, 32.8, 32.9, 32.9, 32.7, 32.9, +32.9, 32.9, 32.8, 33.0, 32.8 — **все 18 интервалов в 32.7–33.0с**. +Вывод: это ТОЧНО таймер (равномерность до 0.1с), не случайные потери. +Кандидаты: 32.9с ≈ период какого-то шлюзового цикла (kube-vip announce, +conntrack GC, session timeout) — искать у платформы. +Примечание: ранее оценка «~37с» была по косвенным данным, точный период 32.9с. + +**Полная проверка (по требованию):** +1. py_compile всех tests/*.py — OK. bash -n всех tests/*.sh — OK. +2. Креды YMQ (newsqs) — рабочие (list-queues OK). Креды shared — рабочие. +3. В secrets/ лежат ДВА файла с ОДИНАКОВЫМИ новыми кредами: yandex_ymq_newsqs.txt + и yandex_ymq_fork8s.txt (дубликат, 461 байт, 11:54/11:55). Старых кредов + YCAJEQDz нигде нет. Дубликат fork8s.txt подлежит удалению (по команде). +4. git status: не закоммичен только tests/tcp_mtu_probe.py (сейчас коммитится). +5. Эпизод «403 с локали» — подтверждённо моя ошибка env (креды Яндекса в + терминале), не платформа. Сейчас все команды идут с ЯВНЫМИ кредами. diff --git a/tests/tcp_mtu_probe.py b/tests/tcp_mtu_probe.py new file mode 100644 index 0000000..24c8aeb --- /dev/null +++ b/tests/tcp_mtu_probe.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""tcp_mtu_probe.py — бинарный поиск MTU-порога по TCP к шлюзу Nubes. + +Критерий: время до ответа nginx. Тело дошло → мгновенный ответ (<2с). +Сегмент дропнут (MTU/PMTUD) → ответ не приходит (nginx ждёт тело) → считаем +"не прошло" по таймауту FAST_TIMEOUT. Порог ищем делением пополам между +LO и HI, потом уточняем повторными пробами вокруг порога. +""" +import socket +import ssl +import time + +HOST = "185.247.187.151" +PORT = 443 # реальный путь клиентов; 32391 (NodePort) снаружи закрыт +LO = 512 # заведомо проходит +HI = 20000 # заведомо не проходит (14+ сегментов по MSS) +FAST_TIMEOUT = 20 # быстрее полного зависания ~51с +FAST_OK = 2.0 # ответ быстрее этого = прошло + +ctx = ssl.create_default_context() +ctx.check_hostname = False +ctx.verify_mode = ssl.CERT_NONE + + +def probe(n): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(FAST_TIMEOUT) + t0 = time.time() + try: + s.connect((HOST, PORT)) + mss = s.getsockopt(socket.IPPROTO_TCP, socket.TCP_MAXSEG) + ss = ctx.wrap_socket(s, server_hostname="sqs.containerk8s.dev.nubes.ru") + body = b"x" * n + req = (b"POST / HTTP/1.1\r\n" + b"Host: sqs.containerk8s.dev.nubes.ru\r\n" + b"Content-Type: application/x-www-form-urlencoded\r\n" + b"Content-Length: " + str(n).encode() + b"\r\n" + b"Connection: close\r\n\r\n" + body) + ss.sendall(req) + data = ss.recv(4096) + dt = time.time() - t0 + first = data.split(b"\r\n")[0][:40].decode("latin1", "replace") + return dt, mss, first + except Exception as e: + return time.time() - t0, None, "%s" % type(e).__name__ + finally: + try: + s.close() + except Exception: + pass + + +def passed(n): + dt, mss, resp = probe(n) + ok = dt < FAST_OK + print(" size=%6d: %.2fs mss=%s %s -> %s" % + (n, dt, mss if mss else "-", resp, "OK" if ok else "DROP"), flush=True) + return ok + + +def main(): + # санпроверка границ + print("check LO=%d:" % LO) + if not passed(LO): + print("LO не проходит — границы неверны, выход") + return + print("check HI=%d:" % HI) + if passed(HI): + print("HI тоже проходит мгновенно — порога нет в диапазоне, выход") + return + + lo, hi = LO, HI + while hi - lo > 50: + mid = (lo + hi) // 2 + if passed(mid): + lo = mid + else: + hi = mid + print("\nпорог найден: lo=%d hi=%d (проходит <=lo, дроп >=hi)" % (lo, hi)) + + # уточнение вокруг порога: 3 пробы на каждую из 4 точек рядом + print("\nуточнение (по 3 пробы):") + for n in (lo - 200, lo, (lo + hi) // 2, hi + 200): + res = sum(1 for _ in range(3) if passed(max(n, 1))) + print(" size=%d: %d/3 прошли" % (n, res)) + + +if __name__ == "__main__": + main() +