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

3.6 KiB
Raw Blame History

Запрос на анализ: XHR onerror («Сеть») при POST /upload с телом > 100KB

Дата: 2026-06-17 Версия: v1.41 ТОЛЬКО АНАЛИЗ. КОД НЕ ПРАВИТЬ.


Что поменялось в понимании

После просмотра текущего кода причина выглядит уже не как request.get_data() сама по себе.

Ветка /upload сейчас делает так:

  1. читает всё тело через request.get_data(as_text=True)
  2. выдёргивает filename, data, cid regex-ом
  3. если cid пустой, синхронно создаёт контракт в PostgreSQL
  4. затем синхронно вызывает redis_client.push(...)
  5. только после этого возвращает jsonify({"contract_id": ...})

Самое подозрительное место — именно синхронный Redis push в запросе, а не чтение тела. Если Redis/DNS/сеть внутри кластера тормозит или подвисает, клиент уже видит xhr.onerror как «Сеть», хотя тело запроса успело уйти целиком.


Текущий код

# app.py
raw = request.get_data(as_text=True)
...
redis_client.push({"filename": filename, "b64": b64, "mime": mime, "cid": str(cid)})
return jsonify({"contract_id": str(cid)})
# redis_client.py
_r = redis.Redis(
    host=os.getenv("REDIS_HOST", "redisk8s....svc.cluster.local"),
    port=int(os.getenv("REDIS_PORT", "6379")),
    password=os.getenv("REDIS_PASS", "..."),
    decode_responses=True,
    socket_connect_timeout=5,
)

def push(task: dict):
    _get().rpush(UPLOAD_QUEUE, json.dumps(task, ensure_ascii=False))

Локальная гипотеза

request.get_data() не ломает соединение на 200KB. Запрос уже дошёл.

Соединение рвётся или уходит в таймаут на этапе синхронного rpush() в Redis, либо на первом обращении к Redis-клиенту, если DNS/соединение внутри кластера нестабильны. Это объясняет, почему:

  • маленькие тела проходят чаще;
  • сервер иногда успевает обработать и записать лог;
  • xhr.upload показывает 100%, а затем приходит onerror;
  • curl даёт плавающий HTTP:000 / ReadTimeout.

Что надо проверить первым

Самый дешёвый дискриминирующий тест — замерить время именно вокруг redis_client.push() и временно обойти его для /upload.

Если ответ сразу стабилизируется, проблема подтверждается.

Если нет, следующая точка проверки — внешний таймаут/обрыв на ingress или gunicorn, но по текущему коду Redis выглядит ближе всего к критическому узлу.


Вывод

В v1.40 сместили фокус с парсинга тела на более поздний синхронный шаг. Сам get_data() теперь выглядит не как корень проблемы, а как просто первая стадия запроса. Корневой кандидат — блокирующий redis_client.push() в обработчике /upload.