Goal
Offload work to a named queue so it runs asynchronously with built-in retries, concurrency control, and optional FIFO ordering. Target functions receive data normally — no handler changes required.Queues use the
Enqueue trigger action. If you are new to trigger actions, read Trigger Actions first to understand the difference between synchronous, Void, and Enqueue invocations.Steps
Define named queues in config
Declare one or more named queues under You can define as many named queues as your system requires. Each queue name is referenced when enqueuing work.
queue_configs in your iii-config.yaml. Each queue has independent retry, concurrency, and ordering settings.iii-config.yaml
See the Queue module reference for every field, type, and default value.
Enqueue work via trigger action
From any function, enqueue a job by calling The target function (
trigger() with TriggerAction.Enqueue and the target queue name. The caller does not wait for the job to be processed — it receives an acknowledgement (messageReceiptId) once the engine accepts the job.- Node / TypeScript
- Python
- Rust
orders::process-payment in this example) receives the payload as its input — it does not need to know it was invoked via a queue.Unlike
TriggerAction.Void() which is fire-and-forget, Enqueue validates the queue exists and (for FIFO) checks the message_group_field. The messageReceiptId lets you correlate enqueue operations with DLQ entries or retry events. See Trigger Actions for a detailed comparison.Handle the enqueue result
The enqueue call can fail synchronously if the queue name is unknown or FIFO validation fails. Always handle the result.Common rejection reasons:
- Node / TypeScript
- Python
- Rust
- The queue name does not exist in
queue_configs - A FIFO queue’s
message_group_fieldis missing ornullin the payload
Use FIFO queues for ordered processing
When processing order matters — for example, financial transactions for the same account — use a FIFO queue. Set The payload must contain the field named by
type: fifo and specify message_group_field, the field in your payload whose value determines the ordering group. Jobs sharing the same group value are processed strictly in order.iii-config.yaml (excerpt)
message_group_field, and its value must be non-null. The engine rejects enqueue requests that violate this.- Node / TypeScript
- Python
- Rust
Configure retries and backoff
Every named queue retries failed jobs automatically. Configure
After all retries are exhausted, the job moves to a dead-letter queue (DLQ) where it is preserved for inspection or manual reprocessing.
max_retries (total delivery attempts before the job moves to the dead-letter queue) and backoff_ms (base delay between retries). Backoff is exponential:| Attempt | backoff_ms: 1000 | backoff_ms: 2000 |
|---|---|---|
| 1 | 1 000 ms | 2 000 ms |
| 2 | 2 000 ms | 4 000 ms |
| 3 | 4 000 ms | 8 000 ms |
| 4 | 8 000 ms | 16 000 ms |
| 5 | 16 000 ms | 32 000 ms |
iii-config.yaml (excerpt)
See Manage Failed Triggers for DLQ configuration, inspection, and redrive.
Control concurrency
The
concurrency field sets the maximum number of jobs the engine processes simultaneously from a single queue. This applies per-engine-instance.iii-config.yaml (excerpt)
- Standard queues: the engine pulls up to
concurrencyjobs simultaneously. - FIFO queues: the engine processes one job at a time (prefetch=1) to preserve ordering, regardless of the
concurrencyvalue.
Standard vs FIFO Queues
The two queue types solve fundamentally different problems. Standard queues maximize throughput. FIFO queues guarantee ordering.| Dimension | Standard | FIFO |
|---|---|---|
| Processing model | Up to concurrency jobs in parallel | One job at a time (prefetch=1) |
| Ordering | No guarantees — jobs may complete in any order | Strictly ordered within a message group |
message_group_field | Not required | Required — must be present and non-null in every payload |
| Throughput | High — scales with concurrency | Lower — trades throughput for ordering |
| Use cases | Email sends, image processing, notifications | Payments, ledger entries, state machines |
| Retries | Retried independently, other jobs continue | Retried inline — blocks the queue until success or DLQ |
Standard queue flow
Jobs are dequeued and processed concurrently. Each job is independent.FIFO queue flow
Jobs within the same message group are processed one at a time, strictly in order.Retry and dead-letter flow
When a job fails, the engine retries it with exponential backoff. After all retries exhaust, the job moves to the DLQ.Real-World Scenarios
Scenario 1: E-Commerce Order Pipeline
An order API must respond fast. Payment processing is critical and must happen in order per transaction. Email confirmation should be reliable. Analytics is best-effort. Queue configuration:iii-config.yaml
- Node / TypeScript
- Python
- Rust
Scenario 2: Bulk Email Delivery with Rate Limiting
A marketing system sends thousands of emails. The SMTP provider has a rate limit. A standard queue with low concurrency prevents overloading the provider while retrying transient SMTP failures. Queue configuration:- Node / TypeScript
- Python
- Rust
process-order.ts
- Node / TypeScript
- Python
- Rust
4. Use FIFO queues for ordered processing
When order matters (e.g. payment transactions for the same account), use a FIFO queue. Settype: fifo and specify message_group_field — the field in your job data whose value determines the ordering group. Jobs with the same group value are processed strictly in order. The field named by message_group_field must be present and non-null in every job payload — the engine rejects enqueue requests where the field is missing or null.
iii-config.yaml (excerpt)
- Node / TypeScript
- Python
- Rust
concurrency: 3, at most three emails are in-flight at any time. Failed sends retry with exponential backoff (5s, 10s, 20s, 40s, 80s), protecting the SMTP provider from overload.
Scenario 3: Financial Transaction Ledger
A banking system processes account transactions. Transactions for the same account must be applied in order to prevent balance inconsistencies. Different accounts can process in parallel. Queue configuration:iii-config.yaml (excerpt)
- Node / TypeScript
- Python
- Rust
ledger queue is FIFO with message_group_field: account_id, the deposit for acct_A always completes before the withdrawal. Without FIFO ordering, the withdrawal could execute first and fail with “Insufficient funds” even though the deposit was submitted first.
Choosing an Adapter
The queue adapter determines where messages are stored and how they are distributed. Your choice depends on your deployment topology.| Scenario | Recommended Adapter | Why |
|---|---|---|
| Local development | BuiltinQueueAdapter (in_memory) | Zero dependencies, fast iteration |
| Single-instance production | BuiltinQueueAdapter (file_based) | Durable across restarts, no external infra |
| Multi-instance production | RabbitMQAdapter | Distributes messages across engine instances |
See the Queue module reference for adapter configuration and the adapter comparison table for a feature matrix.
When using the RabbitMQ adapter, iii creates exchanges and queues using a predictable naming convention. For a queue named
payment, the main queue is iii.__fn_queue::payment, the retry queue is iii.__fn_queue::payment::retry.queue, and the DLQ is iii.__fn_queue::payment::dlq.queue. See Dead Letter Queues for the full resource map. For the design rationale behind this topology, see Queue Architecture.Queue Config Reference
| Field | Type | Default | Description |
|---|---|---|---|
max_retries | u32 | 3 | Maximum delivery attempts before routing to DLQ |
concurrency | u32 | 10 | Maximum concurrent workers for this queue (standard only) |
type | string | "standard" | "standard" for concurrent processing; "fifo" for ordered processing |
message_group_field | string | — | Required for FIFO — the JSON field in the payload used for ordering groups (must be non-null) |
backoff_ms | u64 | 1000 | Base retry backoff in milliseconds. Applied exponentially: backoff_ms × 2^(attempt - 1) |
poll_interval_ms | u64 | 100 | Worker poll interval in milliseconds |
Next Steps
Trigger Actions
Understand synchronous, Void, and Enqueue invocation modes
Dead Letter Queues
Handle and redrive failed queue messages
Queue Module Reference
Full configuration reference for queues and adapters
Queue Architecture
Design rationale behind retry, dead-lettering, and multi-resource topology