80 lines
3.7 KiB
Markdown
80 lines
3.7 KiB
Markdown
# Сессия #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` как узкое место.
|