запрос на анализ: XHR onerror при загрузке >100KB

This commit is contained in:
“Naeel”
2026-06-17 22:38:04 +04:00
parent 262331fc64
commit 3d3a459fff
+102
View File
@@ -0,0 +1,102 @@
# Запрос на анализ: 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`?