Skip to main content

@siglo/bullmq Adapter Guide

@siglo/bullmq provides a high-throughput, Redis-backed job queue consumer and worker processor adapter for Siglo workflows. It bridges BullMQ workers with the Siglo execution engine, offering payload unwrapping, distributed idempotency deduplication, timeout enforcement, tenant namespace partitioning, and graceful failure handling.

1. Overview & Architecture

When executing distributed workflows across worker clusters, BullMQ distributes asynchronous jobs backed by Redis. The @siglo/bullmq adapter wraps workflow execution within a standard BullMQ worker processor function ((job: Job) => Promise<any>).
Note on Guarantees: Processing operates with at-least-once delivery semantics. On job delivery, the entire workflow executes from the beginning; it does not automatically resume at the last completed step. Claims and cached results do not make external side-effects atomic with job completion. A worker crash after an external effect (e.g., charge or API call) but before claim completion leaves an in-progress claim until TTL expiry, which can repeat the external effect on retry. Workflows must ensure external effects are idempotent or supply provider-level deduplication keys.

2. Installation & Requirements

Package Dependencies

Install @siglo/bullmq alongside Siglo. BullMQ and ioredis are declared as optional peer dependencies so you control the exact version:
Peer dependency matrix:
  • Siglo: * (v1.0.0+)
  • bullmq: ^5.0.0
  • ioredis: ^5.0.0 || ^6.0.0

3. API Reference

createBullMQProcessor(workflow, options)

Creates a BullMQ-compliant worker processor function:

Parameters:

  • workflow (Flow<TInput, TOutput>): The compiled Siglo workflow instance to execute.
  • options (SigloBullMQOptions<TJob, TInput, TOutput>): Optional configuration dictionary.

4. Configuration Options (SigloBullMQOptions)


5. Execution Lifecycle & Data Flow

  1. Job Identification & Idempotency Key:
    • The adapter generates an idempotency key: bullmq:job:${job.id || String(Date.now())}.
    • If idempotencyStore is provided, the key is atomically claimed:
      • 'completed': Returns cached result immediately via store.getResult(key) without re-running the workflow.
      • 'in-progress': Throws Error('Job ... is already in-progress concurrently'), signaling BullMQ to avoid concurrent race conditions.
      • 'claimed': Proceeds to workflow execution.
  2. Input Mapping:
    • If options.input is defined, it is invoked with job.
    • Otherwise, job.data || {} is passed directly as the initial workflow input parameter.
  3. Namespace Resolution:
    • If options.namespace is a function, options.namespace(job) is called.
    • If options.namespace is a static string, it is used.
    • Otherwise, the adapter inspects job.data?.namespace.
  4. Workflow Execution & Context Unwrapping:
    • The workflow executes with { timeout: options.timeout, namespace }.
    • Results are automatically unwrapped (unwrapContext), removing internal engine properties (keys prefixed with $) and resolving getter accessor functions into standard JSON-serializable primitives.
  5. Completion & Failure Handling:
    • Success: If an idempotency store is configured, store.complete(idempKey, cleanResult) is called, and options.output (if provided) shapes the return value.
    • Failure: If an error is thrown, store.fail(idempKey, err) is called to release the in-flight lock, enabling BullMQ retry strategies. The error is re-thrown to trigger BullMQ failure handling.

6. Complete Production Examples

Basic Media Transcoder Worker


Distributed Idempotent Worker with @siglo/redis

In high-concurrency environments with Redis clusters, duplicate job events or worker restarts should never cause duplicate processing:

7. Graceful Shutdown & Connection Draining

Ensure that ongoing workflow executions finish gracefully when the host process terminates (e.g., during Kubernetes pod evictions):