# Запрос на анализ: 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`?