session-07: чанковая загрузка, worker'ы, одно DB-соединение

This commit is contained in:
“Naeel”
2026-06-17 17:37:54 +04:00
parent 95d5f28e00
commit f90a91b6c5
+79
View File
@@ -0,0 +1,79 @@
# Сессия #7 — чанковая загрузка, worker'ы, одно соединение
_Дата: 2026-06-17_
_Версия: v1.15 → v1.18_
---
## Проблема
Загрузка PDF 153 KB падала с «✗ Сеть»:
- v1.14: прямая загрузка (один POST) → Ingress режет `client_max_body_size`
- v1.15-16: чанки 50KB в памяти `_chunk_store = {}` → worker'ы разные, чанки теряются
- v1.17: чанки в PostgreSQL (таблица `chunks`) → всё равно «Сеть»
- v1.18: ОДНО DB-соединение на сборку → ?
## Хронология
### v1.14: прямая загрузка
`POST /` с файлом 153 KB → Ingress (nginx) обрывает → `XHR onerror` → «Сеть»
### v1.15: чанки 50KB в памяти
`POST /chunk` × 3. `_chunk_store = {}` в памяти модуля.
**Ошибка:** dict живёт в одном worker'е. Gunicorn с >1 worker → чанки на разных процессах → второй не видит первый → обрыв.
### v1.16: try/except + проверка doc_id
Добавлена обработка ошибок при сборке. **Откачено** (не решило проблему worker'ов).
### v1.17: чанки в БД
Таблица `chunks(upload_id, chunk_index, chunk_data, ...)`. `ON CONFLICT DO NOTHING`.
**Всё равно «Сеть»** — worker'ы больше не проблема, но осталась другая.
### v1.18: одно DB-соединение (текущая)
**Диагноз:** `_chunk_upload` при сборке делал до 7 отдельных `db.execute`/`db.query`
каждое = новый `psycopg2.connect`. На managed-платформе это медленно → таймаут Ingress → обрыв.
**Исправление:**
- `_save_file_to_db(filename, file_bytes, mime, contract_id, conn=None)` — принимает соединение
- `_chunk_upload` открывает ОДНО соединение на всю сборку:
- SELECT chunks
- INSERT contracts (если нужно)
- INSERT documents
- SELECT id документа
- INSERT supplements
- DELETE chunks
- Все 6 запросов в одной транзакции, одно подключение.
---
## Архитектура чанков (текущая)
```
Клиент: file.slice(0,50KB) → XHR POST /chunk
file.slice(50KB,100KB) → XHR POST /chunk
file.slice(100KB,150KB) → XHR POST /chunk
Сервер /chunk:
1. INSERT INTO chunks (...) ON CONFLICT DO NOTHING ← 1 conn
2. SELECT COUNT(*) FROM chunks ← 1 conn
3. Если received == total_chunks:
ОДНО соединение:
SELECT chunk_data ORDER BY chunk_index
SELECT metadata
INSERT INTO contracts (если нет cid)
COMMIT
→ _save_file_to_db(conn=...)
INSERT INTO documents
SELECT id
INSERT INTO supplements
COMMIT
DELETE FROM chunks
ОТВЕТ {"contract_id": "..."}
```
## Выученные уроки
1. **`_chunk_store` в памяти не работает с несколькими worker'ами** — нужна общая БД.
2. **Много отдельных `db.execute` = много `psycopg2.connect`** — на managed-платформе медленно.
3. **Всегда одно соединение для зависимых операций** — сборка + сохранение должны быть атомарны.
4. **Анализ агентом помог** — указал на `_save_file_to_db` как узкое место.