Inquir Compute · job queue

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 — не 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.

Queue operational ownership comparison
Ownership areaBullMQ + RedisAWS SQS + LambdaInquir Pipelines
Queue infrastructureRedis cluster (you provision)SQS queue (AWS managed)None—built into pipeline
Worker processBullMQ workers (you run)Lambda consumer (you configure)Pipeline steps (platform runs)
Retry configPer-job in worker codeRedrive policy + DLQPer-step in pipeline config
Run history / observabilityRedis keys (short TTL)CloudWatch + DLQ inspectionExecution records (30-day retention)
Secrets modelSeparate from HTTP routesIAM + Secrets ManagerShared workspace secrets
Delivery guaranteeAt-least-once with RedisAt-least-once with SQSAt-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

1

Написать хендлер консьюмера

Экспортируйте функцию, читающую event.payload и возвращающую структурированный выход. Сделайте её идемпотентной: проверяйте dedupe-ключ до записи эффектов.

2

Enqueue из продюсера

Замените queue.add() или sqs.sendMessage() на global.durable.startNew(jobName, undefined, payload) и сразу вернитесь.

3

Настройте retry и мониторинг

Задайте число попыток и backoff в конфиге шага пайплайна. Следите за историей выполнения для зависших прогонов; оповещайте при превышении порога failure rate.

Продюсер → консьюмер без Redis и SQS

Продюсер валидирует вход и ставит задачу одним вызовом. Хендлер консьюмера работает в изолированном контейнере с управляемым retry.

api/enqueue-export.mjs (producer)
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' }) };
}
jobs/run-export.mjs (consumer)
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 и записи прогонов.