закрыта задача bulk-delete-page, заведены три задачи из урожая ревью
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# 🐞 Не держать общий замок воркера во время обращения к qBittorrent при удалении
|
||||
|
||||
- **Тип:** fix
|
||||
- **Категория:** Инфраструктура
|
||||
- **Зачем:** на время удаления встаёт весь фон: опрос, сверка и команды остальных транспортов ждут замок до 30 с на загрузку, пачкой — минутами; замерено на ревью bulk-delete-page
|
||||
|
||||
`Worker.Delete` берёт `w.mu` и держит его через сетевой вызов `qbt.Delete`, хотя
|
||||
правило пакета обратное и записано рядом: медленные вызовы идут **вне** замка
|
||||
(`internal/worker/worker.go`, `Poll` зовёт `qbt.Torrents` до `Lock`). Замок при
|
||||
этом один на весь воркер, а не на задачу — комментарий пакета и
|
||||
`docs/architecture.md` называют его per-download, и это тоже неверно.
|
||||
|
||||
## Воспроизведение
|
||||
|
||||
1. Заставить qBittorrent отвечать на `torrents/delete` медленно (подставной
|
||||
клиент с задержкой; в жизни — снос раздачи с большим числом файлов на
|
||||
загруженном диске).
|
||||
2. Во время вызова дёрнуть через тот же воркер любую команду по **другой**
|
||||
загрузке.
|
||||
|
||||
Видно вместо ожидаемого: команда ждёт весь чужой сетевой вызов. Замер ревью: при
|
||||
задержке `qbt.Delete` 300 мс конкурентный `Cancel` по неизвестному
|
||||
идентификатору заблокирован на `280.450631ms`. Таймаут клиента qBittorrent —
|
||||
зашитые 30 с (поля в `[qbittorrent]` нет), поэтому верхняя граница на одну
|
||||
загрузку — 30 с, а на пачку группового удаления — минуты, в течение которых не
|
||||
идут ни опрос, ни сверка, ни уведомления.
|
||||
|
||||
Отчёт ревью с оракулами —
|
||||
`openspec/changes/archive/2026-08-10-bulk-delete-page/review/report.md`,
|
||||
находка 3.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `Worker.Delete` (`internal/worker/review.go`) — границы критической секции;
|
||||
- `w.mu` и комментарий пакета `internal/worker` — «per-download» неверно;
|
||||
- `docs/architecture.md`, строка «Переходы состояний» в единых точках — тот же
|
||||
неверный факт;
|
||||
- потолок времени на проход группового удаления (`httpapi.bulkBudget`) и
|
||||
требование о нём в спеке `web-ui` — при сужении замка они теряют основание;
|
||||
- поле таймаута в секции `[qbittorrent]` конфига, которого сегодня нет.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Команда по одной загрузке не ждёт сетевого вызова по другой (оракул: тест
|
||||
`internal/worker` с подставным клиентом, блокирующимся внутри `Delete`, —
|
||||
`w.mu.TryLock()` из теста возвращает `true`).
|
||||
- Состояние после сетевого вызова перечитывается, и удаление не переходит в
|
||||
`deleted`, если задача успела уйти из допустимого состояния (оракул: тест на
|
||||
смену состояния между шагами).
|
||||
- Факт про замок исправлен в комментарии пакета и в `docs/architecture.md`
|
||||
(оракул: `task gate`, шаг `canon`).
|
||||
- Решено и записано, остаётся ли потолок времени на проход группового удаления
|
||||
(оракул: дельта-спека и `openspec validate --strict`).
|
||||
|
||||
## Рамки
|
||||
|
||||
Трогает операцию, которая сносит файлы: разбор идемпотентности при повторе
|
||||
обязателен. Уборка собственного торрента после отмены (`processCatched`) — уже
|
||||
устроена нужным образом и служит образцом, менять её не надо.
|
||||
Reference in New Issue
Block a user