HISTORY: период таймаутов 32.9с (18 интервалов 32.7-33.0с) — таймер доказан; полная проверка скриптов и кредов OK; +tcp_mtu_probe.py

This commit is contained in:
“Naeel”
2026-08-15 20:38:01 +04:00
parent 65f151d093
commit f33134d0c1
2 changed files with 111 additions and 0 deletions
+21
View File
@@ -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 (креды Яндекса в
терминале), не платформа. Сейчас все команды идут с ЯВНЫМИ кредами.
+90
View File
@@ -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()