Files
contracts/History/upload-analysis-v40.md
T

103 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.
# Запрос на анализ: XHR onerror («Сеть») при POST /upload с телом > 100KB
**Дата:** 2026-06-17
**Версия:** v1.40
**ТОЛЬКО АНАЛИЗ. КОД НЕ ПРАВИТЬ.**
---
## Симптом
Браузер отправляет XHR POST на `/upload` с JSON-телом ~200KB (base64 файла). Прогресс 100% (данные ушли). Затем `xhr.onerror` → «Сеть». Сервер иногда успевает обработать (логи подтверждают), иногда нет.
Прямой curl с такими же данными — иногда HTTP 200, иногда ReadTimeout.
## Что ПРОБОВАЛИ (40 версий)
| v | Подход | Результат |
|---|---|---|
| 1.0-1.13 | FormData multipart POST | ✗ Сеть |
| 1.14-1.20 | Чанки 50KB, пул БД, keepalive | ✗ |
| 1.21-1.25 | JSON/base64, TEXT вместо BYTEA | частично ✓ |
| 1.26 | `original_bytes DROP NOT NULL` + `original_b64 TEXT` | ✓ РАБОТАЛО (!) |
| 1.27-1.36 | camelot, tabula, libreoffice для PDF | регресс загрузки |
| 1.37 | Логи в памяти | ✗ |
| 1.38-1.39 | Redis очередь (RPUSH → 200 сразу) | ✗ |
| 1.40 | Сырой regex вместо get_json() | ✗ |
## Текущий код (самое быстрое что смогли)
```python
# app.py — НЕТ JSON-парсинга, НЕТ base64.decode(), НЕТ DB INSERT
def _upload_json_impl(self):
raw = request.get_data(as_text=True)
import re
m = re.search(r'"data"\s*:\s*"([^"]*)"', raw)
b64 = m.group(1)
m = re.search(r'"filename"\s*:\s*"([^"]*)"', raw)
filename = m.group(1) if m else ""
m = re.search(r'"cid"\s*:\s*"([^"]*)"', raw)
cid = m.group(1) if m else None
# Контракт — синхронно (быстро)
if not cid:
conn = db.connect()
cur = conn.cursor()
cur.execute("INSERT INTO contracts ... RETURNING id")
cid = cur.fetchone()[0]
conn.commit()
db.put_conn(conn)
# Файл — в Redis (мгновенно, без DB)
redis_client.push({"filename": filename, "b64": b64, "mime": mime, "cid": str(cid)})
return jsonify({"contract_id": str(cid)})
```
```python
# db.py — пул с keepalive
ThreadedConnectionPool(minconn=2, maxconn=10, keepalives=1, connect_timeout=10)
```
```javascript
// upload.html — XHR без ретраев
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.timeout = 30000;
xhr.onerror = function() { reject(new Error('Сеть')); };
xhr.send(JSON.stringify({filename: file.name, data: b64, cid: cid || null}));
```
## Инфраструктура
```
Браузер → ddos-guard → nginx/Ingress → gunicorn → Flask
Redis (RPUSH) → воркер (BRPOP → DB)
```
- Платформа: pythonk8s.services.ngcloud.ru (managed)
- Redis: redisk8s....svc.cluster.local
- PostgreSQL: postgresqlk8s-master...svc.cluster.local
- Пул БД: 2-10 соединений, keepalive
## Тесты curl
```
100KB #1: HTTP:000
100KB #2: HTTP:000
100KB #3: HTTP:200
100KB #4: HTTP:000
100KB #5: HTTP:000
10KB: всегда HTTP 200
```
## Вопросы
1. Почему `request.get_data()` для 200KB тела иногда убивает соединение ещё до ответа? Это на уровне gunicorn/Flask/Werkzeug?
2. Может ли nginx/Ingress иметь `client_max_body_size` или `proxy_request_buffering` который режет тело?
3. Почему v1.26 работал, а v1.40 с тем же `/upload` (без изменений в логике) — нет?
4. Может ли проблема быть в HTTP/2 vs HTTP/1.1? curl с `--http2` иногда помогает на этом хостинге.
5. Redis внутри кластера — может ли `redis.Redis()` блокироваться на DNS-резолве `.svc.cluster.local`?