From d0e874d87ebed6ea00cebe59737e641d1d3a4eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 12 Jul 2026 16:05:44 +0400 Subject: [PATCH] =?UTF-8?q?v5.2.0:=20waitress=20=D0=B2=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82=D0=BE=20app.run()=20=E2=80=94=20production=20WSGI,=208?= =?UTF-8?q?=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=B2,=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=20ERR=5FTIMED?= =?UTF-8?q?=5FOUT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- History/2026-07-12-sonnet-query-timeout.md | 32 +++++++++++++++++++ History/2026-07-12-sonnet-response-timeout.md | 19 +++++++++++ requirements.txt | 1 + site/app.py | 5 +-- 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 History/2026-07-12-sonnet-query-timeout.md create mode 100644 History/2026-07-12-sonnet-response-timeout.md diff --git a/History/2026-07-12-sonnet-query-timeout.md b/History/2026-07-12-sonnet-query-timeout.md new file mode 100644 index 0000000..4af31fe --- /dev/null +++ b/History/2026-07-12-sonnet-query-timeout.md @@ -0,0 +1,32 @@ +# Запрос к Соннету — ERR_TIMED_OUT при открытии нового окна (2026-07-12) + +## Симптом + +DrHider на Managed Flask (Штурвал/pythonk8s). При открытии страницы в НОВОМ окне браузера — ERR_TIMED_OUT (20+ секунд). Старые вкладки работают нормально. + +## Факты + +- Изнутри кластера (kubectl exec → curl): HTTP 200 за 2.1 секунды +- Снаружи (браузер, curl с локальной машины): таймаут 10+ секунд +- v1 работало с gunicorn (CMD: `gunicorn --bind :5000 --worker-class gevent`) +- Сейчас: `app.run(host="0.0.0.0", port=5000)` — Flask dev server (Werkzeug) +- Страница: 12 KB HTML, 2 статических файла (logo.svg, favicon.svg — оба 2.8 KB) +- Ноль внешних зависимостей (никаких CDN, шрифтов, JS-библиотек) +- HTTP-заголовки: Cache-Control: no-cache, no-store, must-revalidate +- Штурвал запускает: cd site && python app.py + +## Вопросы + +1. Может ли Flask dev server (Werkzeug) быть причиной ERR_TIMED_OUT на новых соединениях, в то время как gunicorn работал нормально? +2. Если да — какой механизм? (keep-alive, thread pool, socket backlog?) +3. Если нет — что ещё может вызывать таймаут именно на НОВЫХ окнах при работающих старых? +4. Нужно ли вернуть gunicorn или есть другой способ заставить app.run() работать стабильно? +5. Может ли `Cache-Control: no-store` заставлять браузер делать лишние запросы при открытии нового окна? + +## Контекст платформы + +Штурвал — Managed Flask на Kubernetes: +- Запускает: `cd /var/www && pip install -r requirements.txt && cd site && python app.py` +- Readiness probe: `tcp-socket :5000` +- Нет Dockerfile, нет nginx перед приложением +- Ingress: внешний LoadBalancer → ClusterIP сервис → под diff --git a/History/2026-07-12-sonnet-response-timeout.md b/History/2026-07-12-sonnet-response-timeout.md new file mode 100644 index 0000000..16df5e4 --- /dev/null +++ b/History/2026-07-12-sonnet-response-timeout.md @@ -0,0 +1,19 @@ +# Ответ Соннета — ERR_TIMED_OUT (2026-07-12) + +## Причина + +Werkzeug dev server: **один поток на TCP-соединение**. Браузер держит 6 keep-alive соединений на вкладку. 6 потоков заняты idle-соединениями → backlog (5) переполнен → новое окно получает SYN-дроп → таймаут. + +gunicorn+gevent: async I/O, idle-соединения не занимают ресурсы. + +## Решение + +Три варианта: + +| Вариант | Что | Изменения | +|---|---|---| +| A (waitress) | Pure Python WSGI | `requirements.txt` + 2 строки в `app.py` | +| B (gevent) | Async, как gunicorn | `requirements.txt` + 2 строки в `app.py` | +| C (gunicorn) | Точно как v1 | `requirements.txt` + смена CMD в Штурвале | + +Соннет рекомендует: **A** если Штурвал не меняет CMD, **C** если меняет. diff --git a/requirements.txt b/requirements.txt index 8ca3c32..b6541a0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ flask +waitress python-docx pdfplumber httpx diff --git a/site/app.py b/site/app.py index d977a5b..37e0b4e 100644 --- a/site/app.py +++ b/site/app.py @@ -20,7 +20,7 @@ if _sys_path_root not in sys.path: sys.path.insert(0, _sys_path_root) # Версия приложения (меняется при изменениях) -VERSION = "5.1.0" +VERSION = "5.2.0" def create_app(): @@ -61,7 +61,8 @@ app = create_app() # Штурвал запускает: cd site && python app.py if __name__ == "__main__": - app.run(host="0.0.0.0", port=5000) + from waitress import serve + serve(app, host="0.0.0.0", port=5000, threads=8)