- порядок беклога и роадмапа назначен слоями: проверки, которым можно верить → долги входа → владелец записи и контракт API → конвейер под тестами → приложение и возможности поверх; у каждого движения записана причина; - заведены восемь задач под пункты «Завершения», которых не закрывала ни одна запись, — цель any-audio-source была без задач вовсе; - у четырёх задач сняты критерии, требовавшие того, что делает задача ниже по очереди; исправлены ссылки на несуществующий repo/sqlite и на отменённую разведку об очереди.
4.4 KiB
🐞 Убирать записанный файл, когда приём отказал на середине
- Тип: fix
- Категория: Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
- Зачем: Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
- Теги: review-2026-08-11, goal:upload-reliability
Приём пишет файл на диск, потом спрашивает у источника метаданных длительность,
потом заводит запись в учёте и задачу. Уборка при отказе есть только на
последнем шаге: отказ fileRepo.Create зовёт os.Remove, а отказы записи на
диск и чтения метаданных возвращают ошибку, оставляя файл лежать.
Сопоставить такой файл не с чем: записи в files под него нет, задачи нет,
имя — случайный идентификатор. На сервере это data/files, где лежат голосовые
сообщения живых людей, а проект по паспорту «не хранит записи как архив».
Найдено проходом review-specs ревью дизайна change
2026-08-11-fix-http-handler-tests; отчёт триажа —
openspec/changes/archive/2026-08-11-fix-http-handler-tests/review/triage.md.
Вынесено сюда решением человека на контрольной точке.
Воспроизведение
Асимметрия видна в самом коде:
sed -n '110,160p' internal/service/transcribe.go
Ветка fileRepo.Create (строки 152–157) содержит os.Remove(storageFilePath) с
комментарием «Удаляем файл если не удалось создать запись в БД». Ветки
io.Copy, dst.Close и metaviewer.GetInfo (строки 116–133) возвращают ошибку
без уборки, хотя файл к этому моменту уже создан.
Прогоном: TestCreateTranscribeJob_MetaViewerFailure в
internal/controller/http/transcribe_test.go доводит приём до этой ветки —
после него в каталоге хранения лежит файл, а countJobs равен нулю.
Затрагивает
internal/service/transcribe.go, функцияcreateTranscribeJob— три ветки отказа между созданием файла и заведением записи в учёте;internal/controller/http/transcribe_test.go— случай отказа метаданных получает проверку на отсутствие файла;- раскладка
data/filesне меняется: имена и формат прежние.
Критерии приёмки
- Отказ чтения метаданных не оставляет файла в каталоге хранения. Оракул — тест на подставном источнике, возвращающем ошибку: каталог пуст после запроса.
- Отказ записи на диск на середине не оставляет частично записанного файла. Оракул — тест с источником данных, обрывающимся на середине чтения.
- Уборка сама не заслоняет исходную ошибку: отправитель по-прежнему получает отказ, а причина уезжает в журнал. Оракул — тот же тест, код ответа и запись в перехваченном журнале.
Рамки
Уборка файлов, осиротевших до этой правки, сюда не входит: их надо найти сверкой каталога с учётом, и это отдельная работа с боевыми данными.