Долгий metaDL больше не убивается агрессивным таймаутом: дефолт magnet_timeout 30m → 24h (страховочный предохранитель), базис отсчёта — added_on из qBittorrent, а не created_at (переживает retry/усыновление). Авто-восстановление: фоновая сверка возвращает в поток задачи, упавшие по нашей нетерпеливости (magnet_timeout/stalled), когда источник ожил и продвинулся за условие падения (downloading/completed по статусу торрента); qbit_error не воскрешается. Конфликт idempotency (infohash занят другой активной задачей) — оставляем в failed. Уведомления: любой переход в failed/stuck пингует автора (включая приёмный qbit_add через ingest), с дебаунсом против спама при флаппинге stalled. Ручной retry добавлен в веб-UI и Telegram; Retry перецепляется к живому торренту вместо слепого Add. Дельта state-reconciliation влита в живые спеки; обновлён workflow.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
5.1 KiB
5.1 KiB
1. Константы и базис таймаута
- 1.1 Вынести
error_code-строки (magnet_timeout,stalled,qbit_error— вworker;qbit_add— вingest) в именованные константы;error_codeуже читается изstore.Download.ErrorCode(миграция не нужна) - 1.2 В
checkTimeoutsсчитать возраст отtorrent.AddedOn(UTC) через хелперtorrentAge(фолбэк наcreated_at, еслиadded_onнет) - 1.3 Поднять дефолт
[worker].magnet_timeoutдо24hвinternal/config/config.goиconfig.example.toml(с пометкой про страховку)
2. Уведомление о падении
- 2.1 Добавить
EventFailedвNotifyEvent(worker.go) - 2.2 В
transitionслатьEventFailedпри переходе вStateFailedиStateStuck(неблокирующе, внеw.mu) - 2.3 Обработать
EventFailedв Telegram (renderFailed+retryKeyboard); httpapiNotifierне реализует — только tgbot
3. Авто-восстановление в сверке
- 3.1 Восстановление
idempotency_keyотдельным методом стора НЕ нужно:SetDownloadStateсам ставит ключ вinfohashдля нетерминального состояния - 3.2 Реализовать
reconcileRecovery(подw.mu, изPoll): задачи вfailed/stuckсerror_codemagnet_timeout/stalled, поиск вbyHash - 3.3 Воскрешать только при прогрессе торрента (
torrentProgressed):magnet_timeout—!isMeta;stalled—!isStalledDL; неclassErrored; целевое состояние изclassify(recoveredState: ready →completed, downloading →downloading) - 3.4 Конфликт занятого
idempotency_key: пре-проверка черезFindActiveByInfohash— оставляем вfailed+ лог - 3.5 Тесты (
recovery_test.go): метаданные →downloading; готов →completed; всё ещёmetaDL→failed;qbit_error/ошибка не воскрешаются; нет источника →failed; конфликт ключа →failed
4. Починка Retry и ручной retry в транспортах
- 4.1
Worker.Retry: при живом торренте — без повторногоAdd, толькоdownloading;Addлишь когда раздачи нет - 4.2 Тест: retry при живом торренте не делает
Addи не падает на ближайшем тике (базисadded_on) - 4.3 Кнопка retry в веб-UI (
internal/httpapi+index.html) дляfailed/stuck; роут/ui/downloads/{id}/retry; тестTestUIRetry - 4.4 Действие retry в Telegram-боте (колбэк
retry:+ кнопка); тестыTestBot_CallbackRetry,TestBot_NotifyFailed
5. Документация спек
- 5.1 Обновить
docs/specs/workflow.md:magnet_timeoutкак страховка, базисadded_on, уведомление оfailed/stuck, восстановление и retry - 5.2
openspec validate --strict download-failure-recovery— без ошибок
6. Проверка
- 6.1
task lint(0 issues) иtask test(зелёные) - 6.2 Ревью кода (чекпоинт до archive) — 8-угловой multi-agent проход
7. Фиксы по ревью кода
- 7.1 Конфликт
idempotency_key: проверкаFindActiveByInfohashраспространена на веткуcompleted(а не толькоdownloading) — иначе constraint-ошибка и зависание вfailedс логом каждый тик (тестTestRecoverySkipsConflictOnCompleted) - 7.2 Recovery не пишет в
error_msgздоровой задачи (передаём"", причину — в лог), иначе заметка светилась бы как ошибка в UI/REST - 7.3 Эффективность:
reconcileRecoveryгрузит кандидатов черезstore.ListRecoverable(SQL-фильтр поerror_code), а не вычитывает все failed/stuck каждый тик - 7.4 Дебаунс уведомлений о падении (
shouldNotifyFail, окно 1h) — мерцающий stalled-торрент не спамитEventFailed(тестTestFailNotifyDebounce) - 7.5 Уведомление о падении
qbit_addв ingest через closure (SetFailureNotifier, wiring в serve.go), тестTestIngestQbitErrorNotifies - 7.6 Конвенция логирования: убран неймспейс-префикс
recovery:изmsg - 7.7 Мелочи: дедуп ветки
renderCard, диагностика вtorrentAge,time.Unix(...).UTC(), актуализирован комментарийterminalStates