diff --git a/History/2026-07-11-ws-stall-final.md b/History/2026-07-11-ws-stall-final.md new file mode 100644 index 0000000..2f14996 --- /dev/null +++ b/History/2026-07-11-ws-stall-final.md @@ -0,0 +1,71 @@ +# 2026-07-11 — Диагностика WebSocket 51s stall: итог + +## Проблема + +WebSocket-загрузка файлов через ingress (Flask + flask-sock + Werkzeug). +Файлы >1 MB: ~20-30% соединений быстрые (~0.5с), ~70-80% — 51 секунда. +Задержка **не зависит от размера файла** (2MB и 4MB — одинаково 51с). + +## Что проверено (полный список) + +### Код приложения +| Изменение | Результат | +|---|---| +| Baseline (без flow control) | ~20% fast | +| `bufferedAmount` flow control | ~20% (затык на 20% вместо 90%) | +| Server-side ACK (чанк→ack→след) | 0% (сломано полностью) | +| Возврат к baseline | ~20% fast | + +### Платформа (Shturval + Cilium) +| Изменение | Результат | +|---|---| +| nodeSelector (под = нода с ingress) | ~20% fast | +| Scale ingress до 4 (на всех нодах) | ~20% fast | +| hostNetwork на python поде | ~20% fast | +| keepalive=0 к upstream | ~30% fast | + +### Конфигурация nginx (ingress) +- `proxy_buffering off`, `proxy_request_buffering off` +- `proxy_send_timeout 600s`, `proxy_read_timeout 600s` +- ModSecurity OWASP CRS 4.10 (DetectionOnly) +- `client_body_buffer_size 2m` +- Без `limit_rate`, без `limit_req` + +### Сеть +- Cilium Geneve tunnel mode, kube-proxy replacement +- kube-vip ARP mode на всех 4 нодах +- `enable-host-legacy-routing: true` + +## Ключевой тест + +5×2MB через VIP с моей машины (CLI, не браузер): + +``` +#0: total=52.2s sent=52.0s resp=0.2s +#1: total=0.7s sent=0.6s resp=0.1s ← FAST +#2: total=50.8s sent=50.6s resp=0.2s +#3: total=52.3s sent=52.1s resp=0.2s +#4: total=51.9s sent=51.8s resp=0.1s +``` + +Задержка в фазе `send()` — клиент блокируется при отправке данных. +TCP backpressure: где-то в цепочке буфер переполняется и не дрейнуется. + +## Вывод + +**Проблема на стороне платформы (Shturval/nginx-ingress/Cilium), не приложения.** + +51-секундная задержка константна, не зависит от: +- Размера файла +- Node placement +- Cilium Geneve vs hostNetwork +- Количества ingress реплик +- keepalive к upstream + +Единственный стабильный метод: HTTP POST (но Werkzeug рвёт соединение в 30-50% случаев). + +## Текущее состояние + +- Код: baseline v1.0.18 (без flow control, без ACK) +- Кластер: все изменения откачены +- WebSocket работает, но с 51-секундной паузой в 70-80% случаев diff --git a/site/app.py b/site/app.py index 9750389..de0fde6 100644 --- a/site/app.py +++ b/site/app.py @@ -7,7 +7,7 @@ from flask_sock import Sock app = Flask(__name__) sock = Sock(app) -VERSION = '1.0.17' +VERSION = '1.0.18' @app.route('/')