Никита Митусов Обсудить задачу

gptml

Сервис генерации на тысячи заявок в день. Написал воркеры, которые разбирают очередь круглосуточно, и нашёл, почему всё вставало: база в одном файле на 108 МБ.

  1. база начинает копиться в один файл, очистки нет
  2. первая правка скрипта
  3. второй воркер сдан
  4. третий воркер: работают все дорожки
  5. пакет из 14 пунктов
  6. диск 76%. Первое предупреждение
  7. ночью сервис лежит, подняли
  8. вечная загрузка: три причины сразу
  9. диск 85%. Второе предупреждение
  10. сайт упал, поднят в тот же день. Третье предупреждение
  11. выполненные заявки массово уходят в ошибочные
  12. 121 ошибка за день, текст ошибки пуст у всех
28 апреля, начало файламежду точками оценка, форма кривой условная1 сентября, замер
108 МБ

61 237 записей в одном JSON-файле, замер 1 сентября. Снаружи это выглядело как сбой воркера.

$ cat README

Платный сервис генерации картинок, видео и озвучки. Заявка встаёт в очередь на сайте, воркер её забирает, передаёт во внешний сервис и возвращает файл. Линия идёт круглосуточно, тысячи заявок в день, и при простое у владельцев сразу копятся жалобы клиентов.

роль
Воркеры, бэкенд и админка, выкладка на сервер, разбор аварий и ёмкости
кто ещё
Основу сайта сделал прошлый программист. Партнёры ставили задачи, описывали сценарии ошибок и тестировали под нагрузкой, большую часть багов нашли они
когда
май – сентябрь 2026
стек
Python, Next.js, TypeScript, pm2, cron, внешний API генераций
сайт
ai.gptml.ru

$ cat дорожки.conf

Три воркера, четыре дорожки, 36 сборок

Первый воркер довёл из чужого скрипта, второй и третий написал. 21 мая отключённая модель продолжала уходить в работу. Я показал, что отбор живёт на сервере, а не в воркере, и попросил доступ. С этого дня работаю со всей системой.

Третий воркер. Потоки по ТЗ партнёров, таймауты из конфига
дорожкапотокитаймаут
фото 2 300 с
видео 1 2 600 с
видео 2 1 720 с
апскейл 2 600 с
PENDINGPROCESSINGSUCCESSERROR

Заявка больше не висит. Сбой закрывается понятным текстом, и воркер идёт дальше. У каждой модели свои сценарии отказа, партнёры описали их сами.

Ошибка генерации. Время ожидания сервера истекло. Попробуйте снова

Виноват не тот, на кого думают

Шесть жалоб за четыре месяца. Выберите команду, и терминал покажет, что было на самом деле.

31 августа – 1 сентября «воркер сломался»

жалоба
Выполненные заявки массово уходят в ошибочные. Результат отправлен, а в кабинете 20–30 минут висит генерация, пишет партнёр.
след 1
У всех 121 ошибочной заявки за день пустой текст ошибки. Внешние сервисы отказывают с текстом, значит, это таймаут на сайте.
след 2
Лог залит ошибкой блокировки отложенных результатов, хотя функция выключена. Механизм просто не успевает взять блокировку.
след 3
У сервиса генерации результат есть, до сервера он не дошёл. Воркер работал честно, сервер не принимал его «готово» вовремя.
причина
База генераций это один JSON-файл на 108 МБ и 61 237 записей, копится с 28 апреля без очистки. Каждое сохранение переписывает его целиком, и сервер замирает на секунды.
итог
Исправил в тот же день. Гарантию дал точную: от этой причины да, от внешних сюрпризов нет. Дальше написал уборщика.

13 сентября «логи чистятся через час»

жалоба
Пользователи массово пишут, что история генераций исчезает через несколько часов, у кого-то через час.
проверил
Сами за часы генерации не удаляются, это видно по логам сервера.
причина
Хранение 3 дня, при нехватке места уборщик срезает до 2. В субботу в 16:30 порог сработал, и разом ушло около 4,5 тыс. генераций. А 11 сентября кто-то вручную удалил около 16 ГБ.
итог
Система работала как задумано, но порог считался на меньший объём. Считали 9–14 ГБ в сутки, в пятницу набежал 21 ГБ.

21–23 июля «режим «оба» не работает»

жалоба
Партнёр пишет, что заявки не идут в воркеры и API вместе.
проверил
21 июля прошёл всю цепочку на проде: переключение, 10 минут в режиме «оба», живая заявка. Вывод: режим просто не был включён, все 1 309 заявок ушли воркерам.
поворот
23 июля партнёр пришёл снова и оказался прав. Первая проверка проблему не поймала, поймала нагрузка.
причина
Запись счётчика очереди перезаписывала выбранный режим обратно на «воркеры».
итог
Блокировка параллельных записей, 27 тестов, 80 параллельных распределений. Попробовать можно на следующем экране.

16–20 июля «вечная загрузка»

жалоба
Результат готов, а у пользователя крутится загрузка, помогает только очистка кеша. Потом перестаёт работать кнопка входа.
причина
Три вещи сразу. Страница входа кешировалась на год и ссылалась на уже удалённый JS-файл. В хранилище генераций шла гонка записей, 360 ошибок в логах. Истёкшая сессия оставляла бесконечный опрос.
сделал
Вход больше не кешируется, старый файл восстановлен, чтобы никому не чистить кеш. Запись стала последовательной и атомарной. Сессия ведёт на свежую страницу входа.
итог
Партнёр написал, что жалоб нет и стало гораздо лучше. Через день у двух человек проявилось снова, он сам назвал это нечастым сценарием.

4–5 июня «заявка закрылась сама»

жалоба
Заявка закрывается ошибкой на пустом месте.
причина
В тексте заявки стояло слово «ошибка», и воркер принимал его за настоящий отказ. Гипотезу подсказал партнёр.
итог
Исправлено. Теперь слово в запросе пользователя заявку не закрывает.

9 июля «мы же уехали со старой базы»

жалоба
Партнёры считают, что перенос базы закончен.
проверил
Делал пакет из 14 пунктов и увидел, что заявки всё ещё идут через старый внешний сервис.
итог
Перенос оказался выполнен наполовину. Нашёл и довёл.

$ режим --пример

Воркер, API, воркер, API

Заявки идут в воркеры, во внешний API или через одну, режим переключается в админке. Включите нагрузку, и увидите, что ломалось 23 июля: на спокойном проде этого не воспроизвести.

режим

Режим: оба. Заявки чередуются.

Пример, настоящих заявок здесь нет. Настоящая починка: блокировка параллельных записей, 27 тестов, 80 параллельных распределений.

*/30 * * * *

Уборщик

Встроенная очистка удаляла результат и постер, а референс не трогала, поэтому при чистке всплывали файлы прошлого месяца. К 7 сентября заработал уборщик: раз в 30 минут, по понедельникам ещё и кеши.

  1. сносит файлы, на которые не ссылается ни одна запись, в трёх каталогах
  2. ждёт 6 часов: свежий файл мог ещё не попасть в базу. В первом прогоне так отложился 291 файл
  3. закрывает заявки в обработке старше суток, иначе они висят в кабинете вечно
  4. режет срок хранения 3 → 2 → 1 день при базе больше 55 МБ или диске выше 85%, пока диск не дойдёт до 75%

$ df -h /

Шесть письменных предупреждений

76%19 ГБ свободно, 31 ГБ генераций

85%порог

82%лог ошибок 361 МБ без ротации

  1. 1/6

    Диск 76%. Советую внешнее хранилище.

  2. 2/6

    Диск 85%.

  3. 3/6

    После падения сайта предлагаю автоочистку и автоподъём.

  4. 4/6

    В разборе аварии: база растёт без очистки, около 3 000 генераций в день.

  5. 5/6

    Лог ошибок 361 МБ без ротации, диск 82%.

  6. 6/6

    Предлагаю отдельное S3-хранилище вместо расширения памяти.

Что будет дальше

3 днярасчёт

Считали 9–14 ГБ файлов в сутки. Под этот объём и настроен порог, хранение 3 дня.

2 днязамер

В пятницу набежал 21 ГБ при диске 79 ГБ. Три дня истории не помещаются. После обрезки диск снова доходит до 85% меньше чем за сутки. Каждая обрезка перезапускает приложение на 2 секунды: 46 ошибок 502, данные и новые заявки не страдают.

1 деньпрогноз

Перестанут вмещаться и 2 дня, история сожмётся до 1 дня. Тогда «через несколько часов» станет почти правдой.

Партнёры решили расширять память. На следующий день я предложил отдельное хранилище, партнёр согласился присмотреться. Внедрения пока нет.

$ проверка --прод

Чужие заявки не трогал

Приёмка пакета из 14 пунктов шла на живом проде с деньгами чужих пользователей. Каждый пункт закрывал доказательством, а свои ошибки называл своими.

  • PASS

    Служебный тег не уходит в запрос

    Запросил у внешнего API параметры своей же задачи и показал чистый текст.

  • PASS

    Заявка от воркера

    В очереди стояли 3 заявки реальных пользователей, перехватывать их я не стал. Проверил чистую функцию сборки: те же байты, никакого риска для клиента.

  • PASS

    Теги в списке генераций

    Смотрел отрисованную страницу. В сыром ответе теги у 9 из 10 заявок, на экране ни у одной.

  • PASS

    Вложение и перегенерация

    Сквозной прогон через настоящий эндпоинт в обоих режимах. Тестовую копию удалил за секунды, чтобы её не взял настоящий воркер.

  • FAIL

    Мой регресс, 11 июля

    Тексты ошибок воркеров пропали. Написал прямо, что это мой фикс статусов, объяснил механизм и показал настоящей ошибкой на проде, что исправлено.

  • FAIL

    Голоса озвучки

    В прошлый раз проверил вживую один голос, и это была моя вина. Прогнал все 17 по живому API: 16 принято, один убран, в каталоге 36 проверенных.

Партнёр подтвердил по времени: с 16:02 заявки пошли чисто.

Воркеры работают круглосуточно и бьют железно, как молоток.

Партнёр, совладелец сервиса, август 2026. Пересказ из переписки. Партнёры и сами сильные тестировщики, качество воркеров это и их заслуга.

$ exit

Настоящая проблема часто не там, где её видит клиент. Мегабайты, записи и число ошибок 502 в отчёте работают лучше объяснений. А про масштаб лучше писать заранее, с цифрами: про внешнее хранилище я говорил ещё в июле.

Нужно похожее? Автоматизация и ИИ: от 8 000 ₽