← Все статьи

Почему BullMQ вместо конвертации файлов прямо в API

Конвертация в HTTP-запросе vs очередь на Redis: p95 упал с 800 мс до 3,8 мс, пропускная способность выросла в 4,8 раза — и цена сложности.

Содержание

Коротко

Приложение для конвертации и шаринга файлов сначала делало всю работу внутри HTTP-запроса. С ростом размеров файлов пользователи ждали сотни миллисекунд там, где ответ мог прийти сразу. Автор вынес тяжёлую обработку в BullMQ и Redis — и получил измеримый выигрыш по задержке и пропускной способности.

Что произошло

Первая версия была простой: загрузка → конвертация в том же процессе, что обрабатывает API → ответ с результатом. На маленьких файлах это работало. Когда объёмы выросли, соединение держалось открытым, пока сервер занят CPU и диском — хотя клиенту в этот момент нужен был лишь факт «принято в работу».

Новый поток: загрузка → задача в BullMQ → мгновенный ответ API → воркер конвертирует → обновление статуса → пользователь получает файл. Redis хранит очередь; воркеры масштабируются по параллелизму; повторы и обработка сбоев встроены в модель очереди.

Автор прогнал нагрузочный тест с 20 параллельными запросами. p95 задержки API упал примерно в 212 раз (с 800 мс до 3,8 мс). Пропускная способность выросла в 4,8 раза (с 1,3 до 6,2 запросов в секунду). Ускорилась не сама конвертация — изменилась архитектура: тяжёлая работа ушла с критического пути запроса.

Почему это важно

Синхронная обработка в API кажется проще, пока нагрузка низкая. Но любой долгий I/O или CPU в обработчике запроса бьёт по таймаутам, пулу соединений и UX. Паттерн «принять быстро, обработать отдельно» — базовый для файлов, видео, отчётов, рассылок.

Цена асинхронности честная: нужны Redis, процессы-воркеры, мониторинг очереди и отложенная согласованность — пользователь не получает файл в том же ответе, что загрузку. Если бизнес требует мгновенного результата в одном HTTP-цикле, синхронный путь может остаться оправданным. Для конвертации и шаринга это редко так.

На практике

  1. Отделите приём файла от тяжёлой обработки — API возвращает jobId и статус.
  2. Выберите очередь с повторами и очередью «мёртвых» задач (BullMQ на Redis — распространённый вариант в Node.js).
  3. Масштабируйте воркеры, а не только реплики API — задачи, нагружающие CPU, живут там.
  4. Отдавайте клиенту опрос статуса, webhook или SSE для состояния «готово».
  5. Замеряйте p95/p99 API отдельно от времени полной обработки задачи.
  6. Документируйте компромисс: проще код vs инфраструктура и задержка до результата.

Итог

История на Dev.to — короткое, но цифрами подкреплённое напоминание: часто выигрыш даёт не «ускорение функции», а вынос работы из HTTP-запроса. BullMQ + Redis окупились для файлового сервиса; ваш порог может быть другим, но принцип универсален.