Files
contracts/History/sessions/session-08-upload-saga.md
T

98 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Сессия #8 — Upload Saga: 26 версий до рабочей загрузки
_Дата: 2026-06-17_
_Версия: v1.12 → v1.26_
---
## Проблема
Загрузка файлов > 50-100 KB на managed-платформе pythonk8s.services.ngcloud.ru случайно обрывалась с `✗ Сеть` (XHR onerror, HTTP 000).
## Хронология попыток
| v | Что пробовали | Результат |
|---|---|---|
| 1.12-1.13 | FormData/multipart POST | ✗ Сеть > 100KB |
| 1.14 | Promise fix (uploadFileXHR) | ✗ |
| 1.15-1.17 | Чанки 50KB FormData → БД | Только чанк 0, остальные не доходят |
| 1.18 | Одно DB-соединение вместо 7 | ✗ |
| 1.19 | debug_log таблица | ✗ |
| 1.20 | Пул соединений psycopg2 | ✗ |
| 1.21 | base64 JSON вместо FormData | ✗ |
| 1.22 | Пул: keepalive, reconnect | ✗ |
| 1.23 | Всегда одно соединение (убрали if/cid) | ✗ |
| 1.24-1.25 | base64 как TEXT (без decode) + логи | ✗ — `original_bytes NOT NULL` |
| **1.26** | **`original_bytes DROP NOT NULL`** | **✓ РАБОТАЕТ** |
## Корневые причины (две)
### 1. `original_bytes BYTEA NOT NULL`
Вставляли только `original_b64 TEXT`, а `original_bytes` требовал значения. PostgreSQL выдавал 500, но клиент не видел ответа из-за таймаута Ingress.
### 2. `base64.b64decode()` при загрузке
Декодирование 200KB base64 → 150KB байт → BYTEA INSERT занимало >30 сек. Ingress обрывал соединение.
## Рабочий рецепт
```python
# _upload_json (app.py)
# 1. Принимаем base64 как строку
# 2. INSERT TEXT (не BYTEA) — мгновенно
# 3. Без base64.decode()
# 4. Одно DB-соединение на весь запрос
cur.execute(
"INSERT INTO documents (filename, mime_type, original_b64, status) VALUES (%s,%s,%s,'uploaded')",
(filename, mime, b64), # b64 — строка как есть
)
```
```sql
-- schema.py
original_bytes BYTEA, -- было NOT NULL → теперь nullable
original_b64 TEXT, -- новое: base64 строка
```
```javascript
// Клиент: FileReader.readAsDataURL → base64 → XHR JSON POST /upload
xhr.send(JSON.stringify({
filename: file.name,
data: b64, // base64 строка
cid: contractId
}));
```
## Пул соединений (db.py)
```python
ThreadedConnectionPool(
minconn=2, maxconn=5,
keepalives=1,
keepalives_idle=30,
keepalives_interval=10,
keepalives_count=3,
connect_timeout=10,
)
# Проверка живости: SELECT 1 при каждом getconn
# Переподключение при OperationalError/InterfaceError
```
## Как дебажили
1. `debug_log` таблица — 12 шагов лога в `_upload_json`
2. curl тесты: 10×150KB — случайные HTTP 000
3. `/test` endpoint: прямой SQL INSERT — работал, но только с малыми телами
4. Исключение FormData → переход на JSON
5. Исключение `base64.decode()` → TEXT как есть
6. `ALTER TABLE ... DROP NOT NULL` — последний фикс
## Выученные уроки
1. **Managed-платформа ≠ свой сервер** — лимиты невидимы и неконтролируемы
2. **TEXT INSERT быстрее BYTEA INSERT** — не гнать decode при загрузке
3. **NOT NULL constraint** — проверять схему при добавлении колонок
4. **debug_log в БД** — единственный способ увидеть что происходит внутри
5. **Версия в шаблоне** — перед КАЖДЫМ коммитом
6. **Пул соединений с keepalive** — обязательно для managed PostgreSQL