download-tracking: требование о re-read source_type приведено к коду
- re-read `source_type` перечитывается под блокировкой после тик-снимка, а остаточное окно вывода имени названо известным ограничением с ценой и достоверным маршрутом восстановления (ручной шаг + `Retry`, не самоисцеление) - в `ingest` снята парная ложная гарантия «воркер добавит раздачу файлом», добавлены сценарии на оба окна апгрейда и на недоступные байты `.torrent` - заведён ADR о том, что при разрыве спека↔код двигается тот, чья формулировка сильнее рационали
This commit is contained in:
@@ -137,10 +137,42 @@ Conflict`), и загрузка зациклилась бы на ретраях.
|
||||
magnet-хешу вместо файла система SHALL NOT.
|
||||
|
||||
`source_type` для выбора способа добавления worker SHALL перечитывать **под
|
||||
блокировкой переходов** непосредственно перед добавлением (а не полагаться на
|
||||
снимок, снятый ранее вне блокировки): иначе при точном оверлапе тика с апгрейдом
|
||||
пойманной magnet-задачи до `.torrent` (см. `ingest`) воркер добавил бы magnet из
|
||||
устаревшего снимка, хотя БД уже `torrent`.
|
||||
блокировкой переходов после тик-снимка** — перед подготовкой запроса на
|
||||
добавление, а не полагаясь на снимок списка `catched`, который к моменту
|
||||
обработки загрузки уже устарел (блокировка между снятием списка и обработкой
|
||||
отпускается). Иначе апгрейд пойманной magnet-задачи до `.torrent` (см.
|
||||
`ingest`), легший между снятием списка и обработкой, был бы пропущен, и worker
|
||||
добавил бы magnet из устаревшего снимка, хотя в БД уже `torrent`.
|
||||
|
||||
Если подготовка запроса не удалась (сохранённые байты `.torrent` недоступны),
|
||||
worker `add` выполнять SHALL NOT и SHALL оставлять загрузку в `catched` для
|
||||
повтора на следующем тике; отсечка — `catch_timeout`.
|
||||
|
||||
Между этим re-read и самим `add` остаётся окно: вывод отображаемого имени идёт
|
||||
секунды вне блокировки (см. ниже), а запрос на добавление собран до него и по
|
||||
свежей записи не пересобирается. Апгрейд magnet → `.torrent`, легший ровно в это
|
||||
окно, worker не подхватывает и добавляет magnet-ссылку. Это **известное
|
||||
ограничение с названной ценой**, а не гарантия: сегодня запрос собран до вывода
|
||||
имени и по свежей записи не пересобирается. Закрывать ли окно кодом и каким
|
||||
способом (пересборкой запроса или сравнением одного `source_type` под замком с
|
||||
пропуском тика при расхождении) — открытый вопрос за границами этого
|
||||
требования.
|
||||
|
||||
Ущерб окна ограничен, но не бесплатен, и спека называет его точно. На публичном
|
||||
трекере (или при живом DHT) magnet доберёт метаданные и задача пойдёт штатно —
|
||||
ущерба нет вовсе. На закрытом трекере, ради которого `.torrent` и подавался,
|
||||
magnet без метаданных зависает в `metaDL`, и предохранитель `magnet_timeout`
|
||||
уводит задачу в `failed` (см. «Таймауты-предохранители downloading»). Сама собой
|
||||
задача из этого состояния не восстанавливается: фоновая сверка оживляет только
|
||||
продвинувшийся источник, а `Retry` при живом и здоровом торренте (`metaDL`
|
||||
считается живым) перецепляется к нему и повторный `add` по актуальному
|
||||
`source_type` не делает — см. `state-reconciliation` «Ручной повтор
|
||||
зависшей/упавшей загрузки из транспортов». Восстановление сегодня требует
|
||||
ручного шага: убрать
|
||||
зависшую magnet-раздачу из qBittorrent, после чего `Retry` добавит источник
|
||||
заново по актуальному `source_type` — сохранёнными байтами, которые апгрейд уже
|
||||
записал. Без этого шага повторный приём `.torrent` не помогает: новая загрузка
|
||||
усыновит ту же зависшую раздачу (см. «усыновить» выше).
|
||||
|
||||
Неуспешный `add` (qBittorrent временно отверг/недоступен) SHALL оставлять
|
||||
загрузку в `catched` для повторной попытки на следующем тике; переход в
|
||||
@@ -167,7 +199,9 @@ re-read состояния → `[вне блокировки]` свежий ли
|
||||
вывода имени) worker SHALL под блокировкой переходов перечитать запись и, если
|
||||
она уже НЕ в `catched` (отменена), НЕ вызывать `add` и загрузку в этот тик
|
||||
пропустить. Это сужает окно гонки до промежутка между re-read и записью
|
||||
перехода.
|
||||
перехода. Этот re-read проверяет **только состояние** и запрос на добавление не
|
||||
пересобирает — за `source_type` отвечает более ранний re-read после
|
||||
тик-снимка (см. выше).
|
||||
|
||||
- **Подтверждение отсутствия торрента перед `add`.** Непосредственно перед `add`
|
||||
worker SHALL свежим листингом раздач qBittorrent подтвердить, что раздачи ни с
|
||||
@@ -232,6 +266,37 @@ SHALL логировать на уровне `WARN` с корреляцией п
|
||||
нашей категорией и `rename`, без обращения к magnet-хешу
|
||||
- **AND** загрузка переходит в `downloading`
|
||||
|
||||
#### Scenario: Апгрейд magnet → .torrent до подготовки запроса — добавляется файлом
|
||||
|
||||
- **GIVEN** загрузка попала в снимок списка `catched` с `source_type = magnet`
|
||||
- **WHEN** апгрейд до `.torrent` применяется после снятия снимка, но до re-read
|
||||
записи под блокировкой, и worker обрабатывает эту загрузку
|
||||
- **THEN** worker берёт `source_type = torrent` из перечитанной записи и
|
||||
добавляет раздачу сохранёнными байтами файлом (`torrents`)
|
||||
- **AND** magnet-ссылка из устаревшего снимка не используется
|
||||
|
||||
#### Scenario: Апгрейд magnet → .torrent в окне вывода имени — добавляется magnet
|
||||
|
||||
- **GIVEN** загрузка в `catched` с `source_type = magnet`, запрос на добавление
|
||||
подготовлен, worker выводит отображаемое имя вне блокировки
|
||||
- **WHEN** апгрейд до `.torrent` применяется именно в это окно
|
||||
- **THEN** worker добавляет magnet-ссылку из подготовленного запроса — апгрейд в
|
||||
этом окне не подхватывается (известное ограничение)
|
||||
- **AND** на закрытом трекере раздача зависает в `metaDL` и по `magnet_timeout`
|
||||
задача уходит в `failed`
|
||||
- **AND** восстановление требует ручного шага: убрать зависшую раздачу из
|
||||
qBittorrent, после чего `Retry` добавляет источник заново по актуальному
|
||||
`source_type` — сохранёнными байтами
|
||||
|
||||
#### Scenario: Байты .torrent недоступны — повтор
|
||||
|
||||
- **GIVEN** загрузка в `catched` с `source_type = torrent`, торрента в
|
||||
qBittorrent нет, но сохранённые байты `.torrent` прочитать не удалось
|
||||
- **WHEN** worker готовит запрос на добавление
|
||||
- **THEN** worker `add` НЕ вызывает
|
||||
- **AND** загрузка остаётся в `catched`, попытка повторяется на следующем тике
|
||||
(отсечка — `catch_timeout`)
|
||||
|
||||
#### Scenario: Торрент уже присутствует в qBittorrent — усыновление без add
|
||||
|
||||
- **GIVEN** загрузка в состоянии `catched`, торрент которой уже присутствует в
|
||||
|
||||
@@ -511,11 +511,15 @@ v2-хеш (для v2/гибридного файла, BEP52) SHALL извлек
|
||||
привязав их к этой загрузке, и сменить её `source_type` на `torrent`. Тем
|
||||
самым воркер добавит раздачу файлом, а не magnet-хешем (иначе на закрытом
|
||||
трекере без DHT метаданные не докачаются, а magnet застрянет в metaDL →
|
||||
failed). Апгрейд SHALL применяться ТОЛЬКО пока загрузка в `catched` (воркер
|
||||
источник ещё не добавил); для уже добавленной (`downloading` и далее)
|
||||
загрузки смена `source_type` при дедупе выполняться SHALL NOT — её судьба
|
||||
решается путями retry/сверки, а не приёмом. Апгрейд SHALL быть best-effort:
|
||||
его неуспех приём не прерывает.
|
||||
failed) — **при условии, что апгрейд лёг до того, как воркер подготовил запрос
|
||||
на добавление**. Апгрейд, попавший в узкое окно вывода отображаемого имени,
|
||||
воркер уже не подхватит и добавит magnet: это известное ограничение, названное в
|
||||
`download-tracking` «Добавление пойманной загрузки в qBittorrent» вместе с
|
||||
маршрутом восстановления. Апгрейд SHALL применяться ТОЛЬКО пока загрузка в
|
||||
`catched` (воркер источник ещё не добавил); для уже добавленной (`downloading`
|
||||
и далее) загрузки смена `source_type` при дедупе выполняться SHALL NOT — её
|
||||
судьба решается путями retry/сверки, а не приёмом. Апгрейд SHALL быть
|
||||
best-effort: его неуспех приём не прерывает.
|
||||
|
||||
Из полей `.torrent` система SHALL синтезировать контекст распознавания (имя
|
||||
раздачи, суммарный размер, сигнал по дереву файлов, домен трекера, комментарий)
|
||||
@@ -567,7 +571,19 @@ NOT.
|
||||
- **THEN** новая загрузка не создаётся, возвращается существующая
|
||||
- **AND** байты `.torrent` сохраняются привязанными к ней, а её `source_type`
|
||||
становится `torrent` — в одной транзакции
|
||||
- **AND** воркер добавит раздачу файлом (не по magnet)
|
||||
- **AND** воркер добавит раздачу файлом (не по magnet), если апгрейд лёг до
|
||||
подготовки запроса на добавление — см. `download-tracking` «Добавление
|
||||
пойманной загрузки в qBittorrent»
|
||||
|
||||
#### Scenario: Апгрейд в окне вывода имени — воркер добавит magnet
|
||||
|
||||
- **GIVEN** активная загрузка в `catched` с `source_type = magnet`, для которой
|
||||
воркер уже подготовил запрос на добавление и выводит отображаемое имя
|
||||
- **WHEN** принимается `.torrent` с тем же инфохэшем
|
||||
- **THEN** апгрейд применяется (байты сохранены, `source_type` стал `torrent`)
|
||||
- **AND** гарантии, что воркер добавит раздачу файлом, приём в этом случае не
|
||||
даёт: исход добавления определяет `download-tracking` «Добавление пойманной
|
||||
загрузки в qBittorrent», где это окно и его цена описаны
|
||||
|
||||
#### Scenario: Magnet-задача уже добавлена — апгрейда нет
|
||||
|
||||
|
||||
Reference in New Issue
Block a user