# Запрос на анализ: чанковая загрузка падает с «Сеть» **Дата:** 2026-06-17 **Версия:** v1.17 **СТРОГО: ТОЛЬКО АНАЛИЗ. КОД НЕ ПРАВИТЬ. Ответ — в этот же файл.** --- ## Симптом Загрузка PDF 153 KB (3 чанка по 50 KB) через `POST /chunk`: - Прогресс быстро доходит до 100% - Затем ~10 секунд висит «Загрузка...» - Итог: `✗ Сеть` (XHR onerror — обрыв соединения, нет HTTP-ответа) Пробовали: - v1.15: чанки в памяти Python `_chunk_store = {}` → «Сеть» - v1.17: чанки в PostgreSQL (таблица `chunks`) → ТО ЖЕ САМОЕ Значит дело НЕ в worker'ах/shared memory — с БД чанки общие для всех процессов. ## Архитектура - **Платформа:** pythonk8s.services.ngcloud.ru (managed Flask-хостинг, деплой = git push) - **Ingress:** nginx перед приложением, таймаут ~30 сек (известно по предыдущему опыту) - **Приложение:** Flask (app.run), Python 3.12 - **БД:** PostgreSQL 17.6, внутренний кластер ## Клиентский код (upload.html) ```javascript async function uploadFileChunked(file, cid, onProgress) { var CHUNK_SIZE = 50 * 1024; // 50 KB var totalChunks = Math.ceil(file.size / CHUNK_SIZE); var uploadId = file.name + '_' + Date.now() + '_' + Math.random(); for (var i = 0; i < totalChunks; i++) { var chunk = file.slice(i * CHUNK_SIZE, Math.min((i+1) * CHUNK_SIZE, file.size)); var resp = await new Promise((resolve, reject) => { var xhr = new XMLHttpRequest(); xhr.open('POST', '/chunk'); xhr.timeout = 60000; xhr.onload = function() { if (xhr.status === 200) { var r = JSON.parse(xhr.responseText); if (r.error) reject(new Error(r.error)); else resolve(r); } else reject(new Error('HTTP ' + xhr.status)); }; xhr.onerror = () => reject(new Error('Сеть')); xhr.ontimeout = () => reject(new Error('Таймаут')); var fd = new FormData(); fd.append('chunk', chunk); fd.append('upload_id', uploadId); fd.append('filename', file.name); fd.append('chunk_index', i); fd.append('total_chunks', totalChunks); if (cid) fd.append('cid', cid); xhr.send(fd); }); // resp = {contract_id: "..."} только на последнем чанке // или {chunk: N, received: N, total: N} на промежуточных } } ``` Файл 153 KB → 3 чанка. Прогресс 33% → 67% → 100% (значит все 3 XHR завершились). Затем 10 сек паузы → «Сеть». ## Серверный код (app.py) ```python def _chunk_upload(self): upload_id = request.form.get("upload_id") chunk_index = int(request.form.get("chunk_index", 0)) total_chunks = int(request.form.get("total_chunks", 1)) chunk_data = request.files.get("chunk").read() # Сохранить в БД db.execute( "INSERT INTO chunks (...) VALUES (...) ON CONFLICT DO NOTHING", (upload_id, chunk_index, chunk_data, ...) ) # Проверить: все чанки получены? received = db.query("SELECT COUNT(*) FROM chunks WHERE upload_id=%s", ...) if received == total_chunks: # Собрать из БД rows = db.query("SELECT chunk_data FROM chunks WHERE upload_id=%s ORDER BY chunk_index", ...) file_bytes = b"".join(...) # Сохранить как документ _save_file_to_db(filename, file_bytes, mime, contract_id) # Очистить чанки db.execute("DELETE FROM chunks WHERE upload_id=%s", ...) return jsonify({"contract_id": str(contract_id)}) return jsonify({"chunk": chunk_index, "received": received, "total": total_chunks}) ``` ## Вопросы для анализа 1. **Почему «Сеть» (XHR onerror), а не HTTP-ошибка?** Сервер не возвращает ответ — соединение обрывается на уровне TCP. Это не таймаут (был бы `ontimeout`) и не HTTP-ошибка (был бы `onload`). 2. **Почему прогресс 100% но потом 10 сек паузы?** Все 3 чанка отправлены и получили ответ? Или третий чанк висит 10 сек и потом обрыв? 3. **Может ли `ON CONFLICT DO NOTHING` + `SELECT COUNT(*)` дать гонку?** Два worker'а одновременно пишут чанки — COUNT может не увидеть только что вставленный? 4. **Может ли сборка (`b"".join` + `_save_file_to_db`) быть медленной и вызывать таймаут Ingress?** 153 KB — вроде быстро, но если БД тормозит... 5. **Может ли `client_max_body_size` на nginx резать чанки?** 50 KB — вроде мало. 6. **Есть ли ограничение на количество запросов от одного клиента?** Rate limiting? 7. **Что видят логи сервера?** Есть ли способ посмотреть? ## Что нужно **ТОЛЬКО АНАЛИЗ. КОД НЕ ТРОГАТЬ.** 1. Определить наиболее вероятную причину обрыва 2. Предложить способ диагностики (как узнать точно) 3. Предложить решение (без правки кода)