Files
jellybit/tasks/items/background-error-noise.md
T
av bb278e8744 tasks: груминг — верх очереди отдан багам и мелочам
- в «Ядре» первыми стоят metadata-title-sanitize и long-title-to-review,
  за ними живая проверка формы ответа TheTVDB и confidence-гейт
- в «Инфраструктуре» первой стала background-error-noise: единственный
  ready-дефект секции, решение по бэкоффу принято 2026-08-06
- infohash-identity-integrity понижен до research и сдвинут вниз: тело само
  не решает между change и ограничением в документе, взять его нельзя
2026-08-10 08:57:00 +03:00

91 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🐞 Тормозить опрос qBittorrent бэкоффом при недоступности и эскалировать устойчивый сбой
- **Тип:** fix
- **Категория:** Инфраструктура — единственный ready-дефект секции: недоступный qBittorrent опрашивается каждые 5 с и пишет WARN на каждом тике бесконечно; решение (бэкофф до минуты + ERROR на N подряд) принято 2026-08-06, критерии с оракулами прописаны
- **Зачем:** недоступный qBittorrent опрашивается каждые 5 с и даёт WARN на каждом тике: нужен экспоненциальный бэкофф до минутного потолка со сбросом по первому успеху и ERROR на устойчивой деградации
Остаток от задачи «классификация доменных ошибок + конвенции логирования»
(основное реализовано, см. ниже) плюс бэкофф опроса, заказанный 2026-08-06.
Речь о поведении фонового цикла, пока зависимость лежит: с какой частотой он её
дёргает и каким уровнем об этом пишет.
## Что уже сделано (не переоткрывать)
Коммит `f8fb4fa` (Tier A) + коммит этой задачи закрыли:
- **Классификация доменных ошибок:** sentinel `worker.ErrInvalidInput`→400;
обёртки `ErrConflict` в Cancel/Retry/Defer/Undo; `layout.ErrCollision`→409 в
`classifyErr` и ветка в tgbot; `logCmd` относит новые классы в DEBUG.
- **Конвенции:** `logging.md` — команды воркера = доменная граница, таблица
уровней доменных отказов (граница команды vs асинхронная стадия), правило про
`*url.Error`/секреты в URL, канон категории `state transition` (унифицированы
cancel/retry/relink/recovery). `errors.md` — таблица маппинга ошибка→статус,
развилка «транзиентный ответ vs персистентная диагностика» решена как (а):
`error_msg`/`reasons` — операторская поверхность владельца (сырой текст ок,
секреты запрещены; аудит показал, что секреты туда не текут).
- **Мелочи:** reason-коды const-блок; лог-поля `id``download_id`; preview
WARN; комментарий у `parseIgnored`.
## Остаток
**Шум `ext.*` ERROR решено оставить как есть (2026-08-06).** Запись
«зависимость недоступна» на каждом тике — легитимный сигнал транспортного слоя,
и гасится он уровнем сбора логов, а не кодом. Варианты с пониженным уровнем у
`logging.ExtCall` и с дедупом отклонены: первый заводит второе правило уровня
для того же класса вызовов, второй даёт транспортному логгеру память о
состоянии.
Остаются две вещи, и обе стоят на одном счётчике подряд-идущих сбоев тика.
**Бэкофф опроса (решение 2026-08-06).** Пока qBittorrent недоступен, цикл
продолжает дёргать его каждые `poll_interval` (5 с) — недоступную зависимость
незачем опрашивать с рабочей частотой. Интервал растёт экспоненциально от
`poll_interval` до потолка порядка минуты; первый успешный ответ возвращает
рабочий интервал сразу, без ступенчатого спуска. Бэкофф заодно снимает и остроту
шума: записей становится столько же на событие, но событий — единицы в минуту.
**Эскалация уровня.** Сейчас сбой тика — **всегда WARN**, сколько бы тиков
подряд он ни падал. `docs/conventions/logging.md` требует иного: устойчивый сбой
N тиков подряд — это реальная деградация, и она пишется ERROR.
## Воспроизведение
1. Остановить qBittorrent (локально, не на umbar).
2. Смотреть лог воркера в течение нескольких минут поллинга.
3. Наблюдается: запрос к qBittorrent уходит каждые 5 секунд всё время
недоступности, а доменная запись `poll failed` идёт WARN на каждом тике и
остаётся WARN бесконечно.
4. Ожидается: интервал опроса растёт до минутного потолка, а после N
подряд-идущих неудачных тиков уровень поднимается до ERROR — деградация
отличается от разового промаха.
5. Поднять qBittorrent обратно: опрос возвращается к `poll_interval` с первого
успешного ответа.
## Затрагивает
- цикл поллинга воркера (`internal/worker`) — счётчик подряд-идущих сбоев,
текущий интервал тика и его сброс по успеху;
- секция `[worker]` конфига и `config.example.toml` — потолок бэкоффа и порог
эскалации;
- `docs/database.md`, таблица «Настройки с числовым значением» — дом обоих
чисел;
- `docs/architecture.md`, «Характер потока» — там сказано, что фон непрерывный с
периодом поллинга; переменный интервал это уточняет;
- `docs/conventions/logging.md` — правило эскалации уже записано, меняться не
должно; задача приводит код к нему.
## Критерии приёмки
- При подряд-идущих сбоях интервал опроса растёт экспоненциально от
`poll_interval` и упирается в потолок из конфига, дальше не растёт (оракул:
тест цикла с подставным клиентом и управляемыми часами — проверяет
последовательность интервалов).
- Первый успешный ответ возвращает `poll_interval` немедленно (оракул: тот же
тест, сценарий «серия сбоев, успех, сбой» — после успеха интервал рабочий).
- Сбой тика ниже порога пишется WARN, начиная с N-го подряд — ERROR, а успешный
тик сбрасывает счётчик (оракул: тест, считающий уровни записей на сценарии
«сбой, сбой, успех, сбой»).
- Потолок бэкоффа и порог эскалации читаются из конфига и описаны в
`config.example.toml` с единицами и диапазоном (оракул: `task gate`, шаг
канона — сверка с `database.md`).