v0.0.72: ретраи pull (3 попытки), корректная ошибка лимита сессии, счётчик дедупа
Deploy drhider / validate (push) Canceled after 0s

This commit is contained in:
“Naeel”
2026-08-24 19:14:53 +03:00
parent 0479afb44a
commit e31476ebab
5 changed files with 66 additions and 16 deletions
+1
View File
@@ -106,6 +106,7 @@
- **2026-08-24-time-tickers-estimates.md** — v0.0.640.0.65: тикер текущего файла на всех этапах, мгновенные оценки времени по файлам и суммарно.
- **2026-08-24-folder-select-implemented.md** — v0.0.68: кнопка «Выбрать папку» (webkitdirectory, рекурсивно, относительный путь).
- **2026-08-24-folder-zip-not-lost.md** — v0.0.69: архивы из папки без документов не теряются (добавляются как есть); раскрытие zip с документами; склонение счётчика.
- **2026-08-24-pull-retry-limit-counter.md** — v0.0.72: ретраи pull из ВМ, корректная ошибка лимита сессии, счётчик дедупа.
- **2026-08-24-upload-logic-schema.md** — подробная схема логики загрузки + найденные баги/несоответствия.
- **2026-08-24-help-button-limits-text.md** — v0.0.70: заметная кнопка HELP (вместо «?») + пояснение статуса «пропущен» в ограничениях.
- **2026-08-24-ui-locks-session-freeze.md** — v0.0.66: блокировки UI (выбор/удаление) на время работы, заморозка сессии + кнопка «Новая сессия».
@@ -0,0 +1,26 @@
# v0.0.72 — Ретраи pull, корректная ошибка лимита, счётчик дедупа (2026-08-24)
_code-fixes. По итогам код-ревью (см. `infra/2026-08-24-upload-logic-schema.md`)._
## 1. Ретраи pull (site/routes/api_bp.py, `upload_refs`)
- `PULL_RETRIES = 3`, `PULL_RETRY_DELAY = 2` (сек).
- Pull из ВМ-буфера теперь в цикле: при любой ошибке (DNS `gaierror -5`, сеть) — до 3 попыток
с паузой 2с. После исчерпания — проброс исходной ошибки (502 «Pull failed»).
- Закрывает инцидент 18:42 (разовый DNS-сбой ронял всю загрузку).
## 2. Корректная ошибка лимита сессии (`upload_refs`)
- `add_file` возвращает `False` и для «сессия исчезла», и для «превышен лимит 500МБ».
- Теперь: при `False` — если `get_files(sid) is None` → 404 «Session not found»;
иначе (лимит) → файл пропускается (`delete` с ВМ) и загрузка продолжается.
Раньше оба случая давали ложное «Session not found».
## 3. Счётчик «Добавлено из папки» (site/templates/index.html)
- `addFileWithDedup` теперь возвращает `true` (файл добавлен) / `false` (дедуп).
- `added++` только при `true` — дубли больше не завышают счётчик.
- Проверено: папка [Договор.txt, Договор.txt (дубль), Акт.txt] → «Добавлено из папки: 2 файла».
## Проверка
- `node --check` — OK, `py_compile` (app.py, api_bp.py) — OK.
- Локально: счётчик дедупа работает (2 файла при одном дубле).
- Ретраи/лимит — на проде после деплоя.
- Версия 0.0.71 → 0.0.72.
+1 -1
View File
@@ -21,7 +21,7 @@ if _sys_path_root not in sys.path:
sys.path.insert(0, _sys_path_root)
# Версия приложения (меняется при изменениях)
VERSION = "0.0.71"
VERSION = "0.0.72"
def setup_logging():
+31 -5
View File
@@ -30,6 +30,10 @@ from session import (create_session, add_file, get_files, store_result,
api_bp = Blueprint("api", __name__, url_prefix="/api")
log = logging.getLogger("routes.api_bp")
# Ретраи pull из ВМ-буфера: защита от разовых DNS/сетевых сбоев (gaierror -5 и т.п.)
PULL_RETRIES = 3
PULL_RETRY_DELAY = 2 # секунды между попытками
def _disconnect_exceptions():
"""Исключения, означающие отключение клиента SSE."""
@@ -105,9 +109,23 @@ def upload_refs():
except Exception:
pass
continue
with client.stream("GET", url) as resp:
resp.raise_for_status()
content = b"".join(resp.iter_bytes())
# Pull с ретраями: разовые DNS/сетевые сбои не роняют всю загрузку
content = None
last_err = None
for attempt in range(PULL_RETRIES):
try:
with client.stream("GET", url) as resp:
resp.raise_for_status()
content = b"".join(resp.iter_bytes())
last_err = None
break
except Exception as e:
last_err = e
log.warning("upload_refs: pull attempt %d/%d failed sid=%s file=%r: %r",
attempt + 1, PULL_RETRIES, sid, name, e)
time.sleep(PULL_RETRY_DELAY)
if content is None:
raise last_err if last_err else RuntimeError("pull failed")
log.info("upload_refs: pulled sid=%s file=%r size=%d", sid, name, len(content))
if len(content) > MAX_FILE_BYTES:
log.warning("upload_refs: pulled file exceeds %dMB, skip sid=%s file=%r size=%d",
@@ -118,8 +136,16 @@ def upload_refs():
pass
continue
if not add_file(sid, name, content):
log.warning("upload_refs: session not found/limit, sid=%s file=%r", sid, name)
return jsonify({"ok": False, "error": "Session not found"}), 404
# Различить: сессия исчезла vs превышен суммарный лимит сессии
if get_files(sid) is None:
log.warning("upload_refs: session not found, sid=%s file=%r", sid, name)
return jsonify({"ok": False, "error": "Session not found"}), 404
log.warning("upload_refs: session limit exceeded, skip sid=%s file=%r", sid, name)
try:
client.delete(url)
except Exception:
pass
continue
try:
client.delete(url) # убрать файл с ВМ после загрузки
except Exception:
+7 -10
View File
@@ -527,7 +527,7 @@ function addFileWithDedup(file) {
const idx = sf.findIndex(f => f.name === name);
if (idx >= 0) sf[idx] = new File([file], name, { lastModified: mtime });
}
return;
return false; // дедуп: файл не добавлен (обновлена только дата)
}
// Имя то же, размер другой — добавить с суффиксом _2, _3...
const dot = name.lastIndexOf('.');
@@ -548,9 +548,10 @@ function addFileWithDedup(file) {
if (over) {
overNames.add(name);
sf.push(new File([file], name, { lastModified: mtime }));
return;
return true;
}
sf.push(new File([file], name, { lastModified: mtime }));
return true;
}
fi.addEventListener('change', async () => {
@@ -613,21 +614,17 @@ folderInput.addEventListener('change', async () => {
if (nested.length) {
for (const nf of nested) {
const nm = relDir ? relDir + '/' + nf.name : nf.name;
addFileWithDedup(new File([nf], nm, { lastModified: nf.lastModified }));
added++;
if (addFileWithDedup(new File([nf], nm, { lastModified: nf.lastModified }))) added++;
}
} else {
// в архиве нет документов — добавить архив как есть, чтобы не терялся
addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }));
added++;
if (addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }))) added++;
}
} catch (err) {
addFileWithDedup(new File([f], rel, { lastModified: f.lastModified })); // zip как есть
added++;
if (addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }))) added++; // zip как есть
}
} else if (DOC_EXTS.some(e => low.endsWith(e))) {
addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }));
added++;
if (addFileWithDedup(new File([f], rel, { lastModified: f.lastModified }))) added++;
}
// иначе — не документ, пропускаем
}