- имя раздачи нормализуется на границе разбора: вырожденное `-` (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки вывода имени - контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и удерживается структурно; три транспорта перестали обещать идентификатор, которого нет, и коррелируют отказ по request_id - scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах воркера — записи об отказе qBittorrent и метабаз получили download_id и infohash; граница разбора bencode записана в docs/research
4.0 KiB
MODIFIED Requirements
Requirement: Корреляция сущностей в логах
Записи журнала, относящиеся к сущности, SHALL содержать её id в атрибуте
<entity>_id (download_id, recognition_id, batch_id, …); работа в
контексте загрузки ведётся через scoped-логгер с download_id. Благодаря
глобальной уникальности ULID поиск по значению id (grep/jq) SHALL находить
все записи журнала, относящиеся к сущности, независимо от имени поля.
Scoped-логгер SHALL передаваться через context, а не доклеиваться к каждой
записи руками. Отсюда обязанность вызывающего, и она ограничена наблюдаемым
исходом: операция, работающая в контексте загрузки и делающая вызов внешнего
сервиса, SHALL положить scoped-логгер этой загрузки в context до такого
вызова — включая команды, пришедшие с транспорта, а не только фоновый цикл
воркера. Однородность формы у команд, внешних вызовов не делающих, это
требование не нормирует: она принадлежит конвенциям кода.
Причина в том, что клиент внешнего сервиса своей доменной сущности не знает и
знать SHALL NOT — он берёт логгер из context. Поэтому вызов внешнего сервиса в
контексте загрузки SHALL давать запись с download_id и, когда он известен,
infohash; добавлять клиенту поля-дубликаты доменных идентификаторов ради этого
SHALL NOT — источник корреляции один.
Перечень клиентов, ведущих записи о внешних вызовах, живёт в
docs/conventions/logging.md и здесь не дублируется. Telegram-клиент таких
записей не ведёт, и уведомление отправляется вне контекста загрузки намеренно
(иначе оно умирало бы вместе с тиком) — это требование его не касается.
Отдельно это важно там, где внешний сервис не сообщает причину отказа: ответ
qBittorrent Fails. на добавление раздачи причины не несёт, и единственное, что
делает такую запись пригодной для разбора, — корреляция с загрузкой.
Scenario: Путь загрузки по логам
- GIVEN загрузка прошла приём, распознавание и раскладку
- WHEN журнал фильтруется по значению её
id - THEN находятся записи всех этапов (ingest, recognition, file-layout)
Scenario: Неуспешное добавление в qBittorrent с пути retry
- GIVEN загрузка в
failedс известным инфохэшем, для которой оператор запросил retry - WHEN qBittorrent отвечает на добавление отказом (
Fails.либо не-200) - THEN запись о вызове внешнего сервиса содержит
download_idиinfohashзагрузки - AND запись находится тем же фильтром по значению id, что и записи фонового пути добавления