Serverless job queue: уберите Redis и worker-флот
BullMQ требует Redis, worker-процесс и стратегию retry. SQS требует IAM, Lambda-консьюмер и конфиг dead-letter. Пайплайны Inquir дают семантику продюсер/консьюмер — поставить задачу, выполнить в изолированном контейнере, повторить при сбое — без провизии и эксплуатации очередной инфраструктуры.
Обновлено: 2026-06-23
Кратко
Суть ответа
Serverless job queue: уберите Redis и worker-флот. Inquir — не durable message-очередь с гарантированной доставкой, порядком или встроенным dead-letter. Это пайплайны и durable-функции, которые снимают операционную нагрузку с запуска очереди: retry на шаг, история прогона на шаг, изолированные контейнеры — вы пишете логику задач, а не инфраструктуру очереди.
Когда подходит и когда нет
- Нужны enqueue + retry + история прогонов без эксплуатации Redis, SQS или worker-флота
- Фоновые задачи должны делить секреты и наблюдаемость с HTTP API
На что обратить внимание
- In-memory очередь внутри API-процесса теряет задачи при рестарте и не масштабируется горизонтально без дублей. Worker-процесс — самый хрупкий компонент: он должен работать, корректно дрейнировать при деплое и переживать сбои без потери задач.
- Вызов SQS из каждого хендлера размазывает IAM-ключи, retry-логику и управление ключами идемпотентности по кодовой базе. Каждый новый тип задачи пишет один и тот же boilerplate.
Ситуация: нагрузка и где обычно ломается
Что реально стоит эксплуатировать продакшен-очередь задач
- Бэкенд очереди: Redis, SQS или RabbitMQ — провизия, мониторинг, критический путь для каждой задачи
- Worker-флот: постоянные процессы, масштабирование, безопасный drain при деплое
- Retry и backoff: экспоненциальная выдержка, лимит попыток, ручная инспекция после исчерпания
- Наблюдаемость: отдельный дашборд для глубины очереди, зависших задач и причин сбоев
Очереди решают реальные задачи: отделяют продюсера от консьюмера, сглаживают пики, повторяют flaky-работу. Но сама очередь становится инфраструктурой, которую вы эксплуатируете до первой бизнес-задачи.
Компромиссы
Почему DIY-очереди добавляют операционный долг
In-memory очередь внутри API-процесса теряет задачи при рестарте и не масштабируется горизонтально без дублей. Worker-процесс — самый хрупкий компонент: он должен работать, корректно дрейнировать при деплое и переживать сбои без потери задач.
Вызов SQS из каждого хендлера размазывает IAM-ключи, retry-логику и управление ключами идемпотентности по кодовой базе. Каждый новый тип задачи пишет один и тот же boilerplate.
Как Inquir помогает в этом сценарии
Пайплайны как продюсер/консьюмер без операционной нагрузки очереди
Inquir — не durable message-очередь с гарантированной доставкой, порядком или встроенным dead-letter. Это пайплайны и durable-функции, которые снимают операционную нагрузку с запуска очереди: retry на шаг, история прогона на шаг, изолированные контейнеры — вы пишете логику задач, а не инфраструктуру очереди.
Продюсер вызывает global.durable.startNew(jobName, undefined, payload) из любой функции. Консьюмер — хендлер шага пайплайна. Без Redis connection string, без systemd-юнита воркера, без скрипта drain при деплое.
Сравнение
Queue operational ownership comparison
What you own and operate for each approach—not a feature comparison, an operational one.
| Ownership area | BullMQ + Redis | AWS SQS + Lambda | Inquir Pipelines |
|---|---|---|---|
| Queue infrastructure | Redis cluster (you provision) | SQS queue (AWS managed) | None—built into pipeline |
| Worker process | BullMQ workers (you run) | Lambda consumer (you configure) | Pipeline steps (platform runs) |
| Retry config | Per-job in worker code | Redrive policy + DLQ | Per-step in pipeline config |
| Run history / observability | Redis keys (short TTL) | CloudWatch + DLQ inspection | Execution records (30-day retention) |
| Secrets model | Separate from HTTP routes | IAM + Secrets Manager | Shared workspace secrets |
| Delivery guarantee | At-least-once with Redis | At-least-once with SQS | At-least-once (no guaranteed ordering) |
Что вы получаете на платформе
Паттерны job queue в Inquir
Enqueue из любого продюсера
HTTP 202, проверенный вебхук, cron или шаг другого пайплайна — один вызов enqueue (global.durable.startNew) независимо от точки входа.
Retry на шаг с backoff
Число попыток и задержка на шаг пайплайна. Упавшие шаги повторяются без перезапуска уже завершённых — как job retry options без отдельной retry-очереди.
История прогона как запись выполнения
Каждый enqueue создаёт запись: входной payload, выходы шагов, длительность, число retry и причина сбоя. Инспектируется из консоли без запросов к Redis или проверки глубины DLQ.
Fan-out без второй очереди
Шаг задачи вызывает global.durable.startNew() N раз для параллельных дочерних пайплайнов. Никакого отдельного queue-продукта или конфига topic fanout.
Что сделать дальше, по шагам
Как заменить BullMQ или SQS пайплайнами Inquir
Написать хендлер консьюмера
Экспортируйте функцию, читающую event.payload и возвращающую структурированный выход. Сделайте её идемпотентной: проверяйте dedupe-ключ до записи эффектов.
Enqueue из продюсера
Замените queue.add() или sqs.sendMessage() на global.durable.startNew(jobName, undefined, payload) и сразу вернитесь.
Настройте retry и мониторинг
Задайте число попыток и backoff в конфиге шага пайплайна. Следите за историей выполнения для зависших прогонов; оповещайте при превышении порога failure rate.
Пример кода
Продюсер → консьюмер без Redis и SQS
Продюсер валидирует вход и ставит задачу одним вызовом. Хендлер консьюмера работает в изолированном контейнере с управляемым retry.
export async function handler(event) { const { exportId, format } = JSON.parse(event.body || '{}'); if (!exportId) return { statusCode: 400, body: JSON.stringify({ error: 'exportId required' }) }; // Replace: queue.add('run-export', { exportId, format }) const { instanceId: jobId } = await global.durable.startNew('run-export', undefined, { exportId, format: format ?? 'csv' }); return { statusCode: 202, body: JSON.stringify({ jobId, status: 'queued' }) }; }
export async function handler(event) { const { exportId, format } = event.payload ?? {}; // Idempotency: skip if already exported const existing = await db.findExport(exportId); if (existing) return { exportId, fileUrl: existing.url, skipped: true }; const rows = await fetchExportRows(exportId); const fileUrl = await writeExport(rows, format); await db.saveExport(exportId, fileUrl); await notifyExportReady(exportId, fileUrl); return { exportId, fileUrl, rowCount: rows.length }; }
Когда подходит и когда нет
Когда пайплайны Inquir заменяют job queue
Когда это уместно
- Нужны enqueue + retry + история прогонов без эксплуатации Redis, SQS или worker-флота
- Фоновые задачи должны делить секреты и наблюдаемость с HTTP API
Когда лучше не трогать
- Субмиллисекундная latency с жёстким FIFO на миллионах задач в секунду — нужен отдельный message broker для такой пропускной способности и гарантий порядка
Вопросы и ответы
Вопросы и ответы
Guarantees Inquir exactly-once доставку?
Нет. Вызовы пайплайнов — at-least-once. Делайте хендлеры консьюмера идемпотентными: проверяйте dedupe-ключ (exportId, jobId) до записи эффектов, чтобы повторы были безопасны.
Есть ли dead-letter очередь?
Отдельной DLQ-инфраструктуры нет. Когда шаг исчерпывает политику retry, прогон пайплайна помечается failed с полными логами шагов в истории выполнения. Оповещайте по failure rate; replay из сохранённого payload вручную или запускайте новый прогон.
Можно ставить задачи из Python или Go?
global.durable.startNew() — метод Node.js SDK. Из Python или Go выполните POST на URL триггера пайплайна с тем же payload — trigger API доступен по HTTP из любого рантайма.
Как это сравнивается с BullMQ конкретно?
BullMQ запускает задачи в worker-процессах, читающих Redis-очередь — вы эксплуатируете Redis, масштабируете воркеры, управляете DLQ. Шаги пайплайна Inquir работают в изолированных контейнерах под управлением платформы. Вы пишете хендлер консьюмера; платформа берёт на себя планирование, retry и записи прогонов.