docs: сортировка History по темам (sonnet, opus, llm, sse, ux-frontend, upload, infra, plans, tests, base, code-fixes)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# Диагностика медленной/нестабильной загрузки + v0.0.43 — 2026-08-19
|
||||
|
||||
**Дата:** 2026-08-19
|
||||
**Версия:** 0.0.42 → 0.0.43
|
||||
|
||||
---
|
||||
|
||||
## Проблема
|
||||
Юзер: «Таймаут 30с» при загрузке 19 МБ PDF в drhider. Раньше (11с) грузилось.
|
||||
|
||||
## Диагностика (факты)
|
||||
|
||||
### Таймаут 30с
|
||||
- Введён 14.07.2026 (коммит `b6dac6f`, «не висеть бесконечно»), НЕ мой код.
|
||||
- 19 МБ / 30с = 0.63 МБ/с — при медленном/нестабильном канале не хватает.
|
||||
|
||||
### Замеры загрузки с ВМ (внешний путь)
|
||||
- 1 МБ → 0.16с (быстро)
|
||||
- 5 МБ → 51.6с (медленно)
|
||||
- 10 МБ → 52.4с (медленно)
|
||||
- Повтор 10 МБ ×4: **1.07с / 52.3с / 52.3с / 52.3с** — НЕСТАБИЛЬНО, чаще 52с.
|
||||
- Повтор 1 МБ: то 51с, то 0.5с — задержка НЕ зависит от размера.
|
||||
|
||||
### Тайминг (10 МБ)
|
||||
- `connect=0.046с`, `starttransfer=0.09с`, `total=52.5с`.
|
||||
- Сервер отвечает быстро (starttransfer), но соединение «висит» ~52с после ответа.
|
||||
- `Connection: close` — НЕ помогает (то же 52с).
|
||||
|
||||
### Сервер
|
||||
- Из loadtest: внутри кластера 10 МБ = 0.2с — сервер/Flask быстрые.
|
||||
- ClusterIP с ВМ недоступен (ВМ вне кластера) — проверить напрямую не вышло.
|
||||
|
||||
## Применённые аннотации ingress (НЕ помогли)
|
||||
Добавлены на ingress `pythonk8s` (ns 20a75175...):
|
||||
```yaml
|
||||
nginx.ingress.kubernetes.io/proxy-request-buffering: "false"
|
||||
nginx.ingress.kubernetes.io/client-body-buffer-size: "1024m"
|
||||
```
|
||||
- `kubectl patch` — применено, nginx reload (75с).
|
||||
- В конфиге: `client_body_buffer_size 1024m` — применилось.
|
||||
- `proxy_request_buffering on` — НЕ применилось (аннотация не подхватилась shturval-контроллером).
|
||||
- Замер после: 5 МБ = 51.7с, 19 МБ = 53.8с — эффекта НЕТ.
|
||||
|
||||
## Вывод
|
||||
- Задержка ~50с НЕ в коде drhider, НЕ в буферизации nginx, НЕ зависит от размера,
|
||||
НЕСТАБИЛЬНА (то 0.5с, то 52с). Вероятно: внешний путь шлюз/балансировщик/сеть
|
||||
(потеря пакетов / TCP RTO ~50с, или периодическая задержка шлюза).
|
||||
- Совпадает с диагнозом loadtest: «на iot-naeel шлюз держит ~50с».
|
||||
|
||||
## Решение (v0.0.43) — защита от ошибки юзера
|
||||
- `xhr.timeout`: 30000 → **300000 (300с)** — при нестабильной загрузке файл
|
||||
догрузится (медленно, но без «Таймаут 30с»).
|
||||
- Сообщение: «Таймаут 30с» → «Таймаут 300с».
|
||||
|
||||
## Осталось
|
||||
- Аннотации на ingress оставлены (`client-body-buffer-size 1024m` не вредит;
|
||||
`proxy-request-buffering` не работает на shturval — искать правильный способ).
|
||||
- Корень задержки ~50с на внешнем пути — НЕ найден, требует сетевой диагностики
|
||||
(tcpdump, проверка балансировщика/шлюза платформы).
|
||||
@@ -0,0 +1,106 @@
|
||||
# ПЛАН: тест-режим «только загрузка» (для Флэша)
|
||||
|
||||
_Дата: 2026-08-23. Цель: при нажатии «Обфусцировать» — только загрузить файлы
|
||||
(браузер→ВМ→Flask), НЕ запускать обработку (SSE/LLM/обфускацию)._
|
||||
|
||||
## Суть
|
||||
|
||||
Добавить флаг тест-режима в фронтенд. Когда он включён, `uploadFiles()` проходит
|
||||
Фазу 1 полностью (PUT на ВМ + `POST /api/upload_refs` — pull в сессию), а потом
|
||||
**останавливается до Фазы 2** (не открывает EventSource, не запускает обработку).
|
||||
|
||||
Плюс опциональный отладочный endpoint на бэке — проверить, что файлы реально
|
||||
легли в сессию с корректными размерами.
|
||||
|
||||
---
|
||||
|
||||
## Изменение 1 — флаг (site/templates/index.html)
|
||||
|
||||
В начало скрипта, рядом с `VM_UPLOAD_URL` (строка ~201), добавить:
|
||||
|
||||
```js
|
||||
// Тест-режим: открыть страницу с ?upload-only=1 → только загрузка, без обработки
|
||||
const TEST_UPLOAD_ONLY = new URLSearchParams(location.search).get('upload-only') === '1';
|
||||
```
|
||||
|
||||
## Изменение 2 — ранний выход после загрузки (site/templates/index.html)
|
||||
|
||||
В `uploadFiles()`, сразу ПОСЛЕ блока «Шаг 1b» (try/catch `POST /api/upload_refs`)
|
||||
и ПЕРЕД комментарием `// Фаза 2: обработка (SSE — прогресс по каждому файлу)`.
|
||||
|
||||
При этом в шаге 1b зафиксировать число загруженных файлов. Сейчас там:
|
||||
|
||||
```js
|
||||
const data = await resp.json();
|
||||
if (!data.ok) throw new Error(data.error || 'HTTP ' + resp.status);
|
||||
currentSid = data.session;
|
||||
```
|
||||
|
||||
Запомнить count:
|
||||
|
||||
```js
|
||||
const data = await resp.json();
|
||||
if (!data.ok) throw new Error(data.error || 'HTTP ' + resp.status);
|
||||
currentSid = data.session;
|
||||
uploadedCount = data.count || refs.length; // сколько реально легло в сессию
|
||||
```
|
||||
|
||||
(объявить `let uploadedCount = 0;` рядом с `const refs = [];` в начале Фазы 1).
|
||||
|
||||
Сразу после `catch` шага 1b вставить:
|
||||
|
||||
```js
|
||||
if (TEST_UPLOAD_ONLY) {
|
||||
st.className = 'status done';
|
||||
st.textContent = '✅ Загружено ' + uploadedCount + ' файлов (тест: обработка пропущена). Сессия: ' + currentSid;
|
||||
ub.disabled = false;
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
То есть: показываем итог, снова включаем кнопку, выходим из функции. Фаза 2 не выполняется.
|
||||
|
||||
## Изменение 3 (опционально, рекомендуется) — debug-endpoint (site/routes/api_bp.py)
|
||||
|
||||
Чтобы проверить размеры файлов в сессии (не повредились ли при pull), добавить:
|
||||
|
||||
```python
|
||||
@api_bp.route("/session_files/<sid>", methods=["GET"])
|
||||
def session_files(sid):
|
||||
"""Отладка: список файлов сессии с размерами."""
|
||||
files = get_files(sid)
|
||||
if files is None:
|
||||
return jsonify({"ok": False, "error": "Session not found"}), 404
|
||||
return jsonify({
|
||||
"ok": True, "count": len(files),
|
||||
"files": [{"name": n, "size": len(b)} for n, b in files],
|
||||
})
|
||||
```
|
||||
|
||||
`get_files` уже импортирован в `api_bp.py`. Endpoint временный — убрать после теста.
|
||||
|
||||
---
|
||||
|
||||
## Как тестировать
|
||||
|
||||
1. Поднять сервис (локально `cd site && python app.py` или redeploy на Штурвале).
|
||||
2. Открыть `https://drhider.pythonk8s.dev.nubes.ru/?upload-only=1` (или `http://127.0.0.1:5000/?upload-only=1`).
|
||||
3. Выбрать файлы (в т.ч. большой >1МБ), нажать «Обфусцировать».
|
||||
4. Ожидать: прогресс «Загрузка на ВМ…» по каждому файлу → «Передача ссылок…» →
|
||||
«✅ Загружено N файлов (тест: обработка пропущена)». SSE НЕ открывается.
|
||||
5. (Если сделан п.3) в консоли/curl: `curl /api/session_files/<sid>` → сверить `size` с оригиналом.
|
||||
6. Проверить на ВМ: после pull файлы удалены (`ls /var/www/drhider-upload/` пусто).
|
||||
|
||||
## Как вернуть обратно (после теста)
|
||||
|
||||
- Флаг `TEST_UPLOAD_ONLY` без `?upload-only=1` даёт обычное поведение — **ничего выпиливать не надо**,
|
||||
режим включается только query-параметром.
|
||||
- Debug-endpoint (п.3) — убрать, когда тест завершён (или оставить под комментарием «отладка»).
|
||||
|
||||
## Замечания
|
||||
|
||||
- НЕ трогать бэк-логику pull (`/api/upload_refs`) — она уже работает (v0.0.58).
|
||||
- НЕ менять Фазу 2 (SSE) — только добавить ранний `return` до неё.
|
||||
- После правки: `python3 -c "import py_compile; py_compile.compile('site/routes/api_bp.py', doraise=True)"`,
|
||||
JS проверить `node --check` (извлечь блок `<script>` из index.html).
|
||||
- Версию `VERSION` в `site/app.py` НЕ поднимать, пока это тестовый режим — либо поднять, если деплоим.
|
||||
@@ -0,0 +1,55 @@
|
||||
# drhider — тесты загрузки и стресс (2026-08-23, v0.0.59–0.0.60)
|
||||
|
||||
_Платформа: Штурвал, кластер `naeel-test-3` (kubeconfig `~/.kube/config-naeel-test-3` на ВМ).
|
||||
ВМ-буфер: `https://contracts.kube5s.ru/drhider-upload/` (nginx dav). Тест-режим: `?upload-only=1`._
|
||||
|
||||
## Изменения за день
|
||||
- **v0.0.58** — ВМ-загрузка: PUT на ВМ + `/api/upload_refs` (egress pull) + тест-режим `?upload-only=1`.
|
||||
- **v0.0.59** — убран лимит 100 МБ/файл (фронт; остался суммарный 1 ГБ).
|
||||
- **v0.0.60** — дедуп по **имя+размер** (дату игнорируем), при совпадении остаётся **более свежий**.
|
||||
|
||||
## Edge-case тесты (v0.0.59, браузер/Playwright)
|
||||
- Нулевой размер (`empty.txt` 0 B) — принимается, грузится (0 B/s), в сессии size=0. ✅
|
||||
- Бинарный файл (`bin.dat`) — принимается, грузится. ✅
|
||||
- Одинаковое имя, разный размер — суффикс `_2`. ✅
|
||||
- Одинаковые имя+размер, разная дата — **дедуп** (было `_2`, с v0.0.60 — один, свежий). ✅
|
||||
- Один файл в архиве и отдельно — **дедуп** (было `_2` из-за mtime, с v0.0.60 — один). ✅
|
||||
|
||||
## Стресс-тест загрузки (v0.0.60)
|
||||
Под: `pythonk8s-8dbc866bb-d84p6`, requests/limits **cpu 2 / memory 4Gi**.
|
||||
Путь: PUT на ВМ → `POST /api/upload_refs` (один JSON со всеми refs) → `GET /api/session_files`.
|
||||
|
||||
### Сессии (11 успешных + 1 OOM)
|
||||
| Сес. | Файлов | Объём | PUT | refs |
|
||||
|---|---|---|---|---|
|
||||
| A | 120 | 364МБ | 34.4с | 37.8с |
|
||||
| B | 120 | 316МБ | 29.9с | 33.3с |
|
||||
| C | 1×250МБ | 250МБ | 23.2с | 23.9с |
|
||||
| D | 120 | 378МБ | 40.0с | 41.6с |
|
||||
| E | 120 | 301МБ | 26.7с | 31.5с |
|
||||
| F | 1×200МБ | 200МБ | 18.9с | 33.8с |
|
||||
| G | 120 | 360МБ | 34.8с | 36.4с |
|
||||
| H | 120 | 309МБ | 29.0с | 38.4с |
|
||||
| I | 1×250МБ | 250МБ | 23.2с | 24.1с |
|
||||
| J | 120 | 357МБ | 33.4с | 37.9с |
|
||||
| K | 120 | 366МБ | 34.3с | 40.1с |
|
||||
| **L** | 120 | 369МБ | — | **FAIL (OOM)** |
|
||||
| M | 120 | 347МБ | 32.5с | 36.0с (после рестарта) |
|
||||
| N | 120 | 358МБ | 33.1с | 37.5с (после рестарта) |
|
||||
|
||||
### Память пода (рост линейный, ~1.2× объёма сессий)
|
||||
55Mi (база) → 744Mi (3 сес.) → 1991Mi (6 сес.) → 3235Mi (9 сес., ~80% лимита) → **OOMKilled на L**.
|
||||
|
||||
### Итог OOM
|
||||
- `restarts=1, lastReason=OOMKilled, exitCode=137` (лимит 4Gi).
|
||||
- После авто-рестарта под работает, загрузка продолжается (M, N ok).
|
||||
- **Все сессии A–L потеряны** (in-memory хранилище, TTL 30 мин, без персиста).
|
||||
|
||||
## Выводы / факты
|
||||
1. Загрузка через ВМ стабильна на больших объёмах: до OOM — 11 сессий ~3.3ГБ, 0 ошибок, скорость не деградирует.
|
||||
2. Потолок памяти = лимит пода 4Gi; OOM при ~3.5ГБ резидентных сессий.
|
||||
3. Апп переживает OOM (рестарт + продолжение работы), но сессии теряются.
|
||||
4. Код: `session.py MAX_SESSION_BYTES = 500МБ` (на сессию) — OOM реален только при ~8+ одновременных сессий.
|
||||
5. `upload_refs` при переполнении сессии отвечает 404 «Session not found» (вводит в заблуждение — это лимит размера).
|
||||
6. ГРАБЛЯ: на Krupski `dd` алиасится на `docker compose down`; прокси `172.17.192.1:10808` — тесты только `--noproxy '*'`/`NO_PROXY`.
|
||||
7. Браузер-автоматизация (Playwright) видит Windows-ФС (`C:\tmp\...`) — тестовые файлы класть в `/mnt/c/tmp/` (WSL).
|
||||
@@ -0,0 +1,36 @@
|
||||
# 2026-08-24 — Лимиты: 50 МБ на файл, 500 МБ на сессию (v0.0.62)
|
||||
|
||||
## Контекст
|
||||
- После переезда на naeel-test-3 ресурсы инстанса уменьшены до 1 CPU / 2048 MiB
|
||||
(Штурвал, подтверждено kubectl: requests=limits 1CPU/2Gi).
|
||||
- При 2Gi памяти лимит 1ГБ/сессия (фронт) + отсутствие лимита на файл стали опасны
|
||||
(OOM-риск, сессии in-memory и теряются при рестарте).
|
||||
- Пользователь: вернуть лимит — один файл 50 МБ, всего 500 МБ, и показывать лимиты во фронте.
|
||||
|
||||
## Изменения
|
||||
Фронт (site/templates/index.html):
|
||||
- `MAX_FILE_BYTES = 50МБ`, `MAX_SESSION_BYTES = 500МБ` (было 1ГБ, лимит на файл был убран).
|
||||
- `addFileWithDedup`: файл сверх 50 МБ → `overNames` («не учитывается», красный, не обфусцируется);
|
||||
суммарный лимит 500 МБ — как раньше.
|
||||
- Шапка: «Ограничения: один файл не более 50 МБ, суммарно не более 500 МБ...».
|
||||
|
||||
Бэк:
|
||||
- `site/session.py`: добавлен `MAX_FILE_BYTES = 50МБ` (рядом с `MAX_SESSION_BYTES = 500МБ`).
|
||||
- `site/routes/api_bp.py` (защита):
|
||||
- `POST /api/upload`: файл >50 МБ пропускается (log, continue).
|
||||
- `POST /api/upload_refs`: ref с size>50МБ пропускается + DELETE с ВМ;
|
||||
после pull проверяется фактический len(content)>50МБ → пропуск + DELETE.
|
||||
- `site/app.py`: `MAX_CONTENT_LENGTH` 200МБ → 50МБ (защита памяти, согласовано с лимитом файла).
|
||||
|
||||
Версия: 0.0.61 → 0.0.62.
|
||||
|
||||
## Проверка
|
||||
- py_compile app.py / session.py / api_bp.py — OK.
|
||||
- node --check (извлечённый <script> index.html) — OK.
|
||||
- get_errors — нет.
|
||||
|
||||
## Прочее (диагностика 5с)
|
||||
- Медленная загрузка страницы ~5с — НЕ кластер/приложение: `time_namelookup=5.15с`
|
||||
(медленный локальный резолвер: 8.8.8.8 первым в /etc/resolv.conf недоступен, фолбэк на 10.96.x.x).
|
||||
Тот же паттерн на ВМ (5.4с). drhider отвечает за ~0.03с после соединения.
|
||||
- Kubeconfig naeel-test-3 обновлён на ВМ (~/.kube/config-naeel-test-3), kubectl работает.
|
||||
Reference in New Issue
Block a user