6.1 KiB
🐞 Различать отказ, который стоит повторить, и приговор записи
- Тип: fix
- Категория: Очередь — Сроки ожидания идут вперёд: пока их нет, «медленно отвечает» от «упало» неотличимо, и часть отказов классифицировать нечем.
- Зачем: Отказ приведения останавливает запись с первой попытки, и предел в пять отказов не работает никогда: разовый сбой ffmpeg останавливает запись приговором, хотя повтор обработал бы её успешно.
Спека pipeline заводит требование «Число отказов ограничивает повторы шага» с
пределом в пять, но какие отказы этот предел вообще расходуют, она не решает. На
деле шаг делит их сам и делит грубо: отказ конвертации, отказ операции у
провайдера и отсутствие файла останавливают запись приговором с первой
попытки, а до счётчика доходит только отказ хранилища. Значит нарастающая пауза
и пять попыток не работают ровно на тех отказах, которые случаются чаще всего.
Разница наблюдаема: переполненный диск, временно недоступный ffmpeg и разовый
сбой у провайдера сегодня неотличимы от негодной записи, и человек получает
«попробуйте ещё раз» вместо расшифровки, которую дал бы второй заход.
Найдено ревью задачи record-centric-model 2026-08-14; поведение унаследовано от
прежней модели, где отказ переводил задачу в конечное состояние. Само по себе
деление на приговор и повтор — решение, а не разведка: разбирать надо перечень
отказов, а не внешний мир.
Воспроизведение
- Подставить конвертер, отказывающий один раз, и завести запись.
- Прогнать шаг конвейера.
Видно: запись остановлена признаком с причиной «приговор шага», число отказов — единица, паузы нет. Ожидалось: запись возвращается в работу с нарастающей паузой и останавливается только на шестом отказе.
То же на шаге отправки: отказ операции у провайдера останавливает запись, не израсходовав ни одной попытки.
Затрагивает
openspec/specs/pipeline/spec.md— требование о числе отказов и разделPurpose, где вопрос назван неразобранным;internal/service/transcribe.go—failStep,scheduleRetryи ветки отказа в шагах приведения, отправки, опроса и завершения;internal/entity/audio_record.go— перечень причин остановки, если делению потребуется новая;docs/architecture.md, таблица внешних зависимостей — столбцы «падает» и «отдаёт мусор» описывают сегодняшнее деление.
Критерии приёмки
- Разовый отказ приведения возвращает запись в работу, а не останавливает её. Оракул — тест: конвертер отказывает один раз, следующий прогон доводит запись до рубежа приведения.
- Запись, отказывающая на каждой попытке, всё же останавливается по исчерпании предела. Оракул — тест: конвертер отказывает всегда, запись останавливается на шестом захвате с причиной «отказы исчерпаны», а не с первого.
- Отказ, о котором известно, что повтор его не устранит, останавливает запись сразу. Оракул — тест: запись без файла источника останавливается уже на первом заходе, и причина остановки — приговор шага.
- Спека называет, какие отказы расходуют предел, а какие выносят приговор.
Оракул —
openspec validate --strictна изменении плюс сценарий требования, различающий оба исхода.
Рамки
Перечень отказов, которые считать приговором, ограничить теми, что сервис видит
сегодня: негодная запись, отказ операции у провайдера, отсутствие файла.
Классификацию отказов внешних сервисов по кодам не заводить — сроки ожидания
ставит external-call-timeouts, и до неё «медленно» от «упало» неотличимо.
Числа предела и паузы не трогать: они записаны в docs/database.md и решением
владельца не пересматривались.