Files
transcriber/tasks/items/orphan-file-on-failed-intake.md
T
av 5501384cdc tasks: упразднены цели и роадмап, объявлена стадия build
- 11 записей типа goal закрыты с причиной, называющей задачи-наследники;
  ROADMAP.md удалён, индекс остался один — BACKLOG.md;
- 33 записи переписаны: ссылка «Двигает пункты N «Завершения» цели» уступила
  место прямому утверждению — без целей номера пунктов вели в никуда;
- шапка BACKLOG.md размечена парой <!-- стадия -->, порядок строк теперь
  объявлен зависимостью, а не важностью.
2026-08-13 16:00:14 +03:00

4.3 KiB

🐞 Убирать записанный файл, когда приём отказал на середине

  • Тип: fix
  • Категория: Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
  • Зачем: Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
  • Теги: review-2026-08-11

Приём пишет файл на диск, потом спрашивает у источника метаданных длительность, потом заводит запись в учёте и задачу. Уборка при отказе есть только на последнем шаге: отказ 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 не меняется: имена и формат прежние.

Критерии приёмки

  • Отказ чтения метаданных не оставляет файла в каталоге хранения. Оракул — тест на подставном источнике, возвращающем ошибку: каталог пуст после запроса.
  • Отказ записи на диск на середине не оставляет частично записанного файла. Оракул — тест с источником данных, обрывающимся на середине чтения.
  • Уборка сама не заслоняет исходную ошибку: отправитель по-прежнему получает отказ, а причина уезжает в журнал. Оракул — тот же тест, код ответа и запись в перехваченном журнале.

Рамки

Уборка файлов, осиротевших до этой правки, сюда не входит: их надо найти сверкой каталога с учётом, и это отдельная работа с боевыми данными.