Options for configuring the queue plugin.
adapter: QueueAdapterType
The adapter type to use (default 'memory').
name: string
Instance name for multi-instance support.
url: string
Connection URL (used when adapter is 'redis' or 'rabbitmq').
client: IRedisQueueClient | IAmqpQueueConnection
Injected client (bypasses lazy import).
defaultMaxAttempts: number
Default max attempts for jobs (default 3).
pollIntervalMs: number
Poll interval for worker loop (default 1000ms).
prefix: string
Queue name prefix for RabbitMQ adapter (default 'he.queue').
deadLetterTtlMs: number
How long a dead-lettered job's payload is retained, in milliseconds.
Omitted (the default) keeps today's behaviour: the payload is retained
indefinitely "for debugging", so the jobs hash grows without bound for the
lifetime of the deployment (X8-4). Applied only by the Redis adapter, and
only when the injected client exposes expire.
Enforced PER PAYLOAD: each dead-letter sweeps the dead set — scored by
dead-letter time — and deletes every entry older than this, so a queue that
keeps failing still drops its oldest payloads. Setting it also relocates a
dead job's payload from queue:<name>:jobs, which holds every queued job's
payload for that name, into queue:<name>:dead:jobs.
The retention it delivers is a bound rather than a deadline: AT LEAST this long, and AT MOST this long past the LAST dead-letter on the queue — so a payload that dies just before a short burst can live for just under twice this value. The sweep runs only when a dead-letter arrives, and the key-level backstop beside it carries one deadline for a shared key, which must be the newest or it would take newer payloads with it. It errs late deliberately: dropping a payload early discards the debugging data this option exists to keep.
sqs: import("../adapters/sqs-queue.ts").SqsQueueOptions
SQS-specific options (required when adapter is 'sqs').
processors: readonly QueueProcessorEntry[]
Processors registered declaratively, as an alternative to calling
queue.process(name, processor, options) imperatively after start().
Each entry — instance or RegistryFactory — produces one process()
call, so a processor can be declared where the plugin is composed instead
of after the application has started.
Instance entries register during the plugin's register() phase,
identical to the imperative timing.
The one exception is an array that also carries a FACTORY: the whole
array then registers in onInit, in DECLARED order, because
process() is last-wins on a job name and registering instances first
would let a leading factory beat a trailing instance. Factory entries are resolved in the
onInit phase — the first at which the registry holds every capability —
so a factory can build its processor from a resolved capability. A
factory that throws rejects start() with an error naming
QueuePlugin({ processors }) and the entry's index in THIS declared
array, not its position among the factories.
Registering two entries under one job name keeps the service's existing
last-wins behaviour: exactly what two imperative process() calls with
the same name do — decided by DECLARED order, so a trailing entry beats a
leading one whichever arm each uses.
behaviors: readonly (IIngressBehavior | RegistryFactory<IIngressBehavior>)[]
Ingress behaviours wrapped around every processor — the queue arm of the
transport-neutral behaviour chain shared with the websocket, scheduler,
and messaging plugins (IIngressBehavior in @setu-ts/common).
Each behaviour observes an IngressContext carrying kind: 'queue', the
job name as name, the delivered IJob as payload, and attempt
equal to IJob.attempts, and runs in declared order ahead of the
processor. A behaviour that returns without calling next()
short-circuits: the processor never sees the job and the job is
acknowledged. A behaviour that throws follows the processor's own failure
path — requeue with backoff, and ProcessOptions.onFailed plus the
dead-letter on the final attempt. Every processor registration is
wrapped, imperative process() calls included, so a mixed application
cannot leave a handler unchained.
With no behaviours configured, dispatch is byte-identical to the pre-chain behaviour: the processor is handed the job directly, with no chain allocated.
When an entry is a FACTORY, dispatch is HELD until onInit has resolved
the whole chain, so nothing reaches a handler through a partial one. That
gate covers every registration — this plugin's declared entries and any a
later plugin makes imperatively through the resolved capability — which
is why no registration's timing has to change. It is released once and
costs nothing thereafter.
Instance entries are handed to the service at register(); factory
entries are resolved in the onInit phase and a throwing factory rejects
start() naming QueuePlugin({ behaviors }) and the entry's index in
THIS declared array.