Magento’s message queue framework moves slow work off the request path: bulk API operations, asynchronous stock updates, export generation. Behind it sits RabbitMQ (or a database fallback), and behind every “the bulk endpoint is broken” ticket sits a consumer that is not running. Here is how the system fits together and how to run it reliably.
The Moving Parts
- Publisher: code that drops a message on a topic -
product_action_attribute.update,async.operations.all - Queue: RabbitMQ exchange/queue pair holding messages until consumed
- Consumer: a long-running PHP process (
bin/magento queue:consumers:start <name>) pulling messages and executing handlers consumers_runnercron job: Magento’s built-in supervisor that starts consumers if they are not running
Declare the wiring in three module files: communication.xml (topics), queue_topology.xml (bindings), queue_consumer.xml (consumer config).
RabbitMQ vs Database Queue
The database queue works without extra infrastructure and is fine for light asynchronous use. RabbitMQ earns its keep when volume or ordering matters: bulk API at scale, MSI with multiple sources, heavy import/export. On Adobe Commerce Cloud, RabbitMQ is provisioned; on-premise, a small dedicated node with default settings handles most stores.
Running Consumers Properly
The consumers_runner cron starts missing consumers - but defaults are timid. Production tuning:
<!-- env.php -->
'cron_consumers_runner' => [
'cron_run' => true,
'max_messages' => 5000,
'consumers' => ['product_action_attribute.update', 'exportProcessor']
],
max_messagesbounds consumer lifetime: the consumer restarts after N messages, releasing leaked memory. Set it - an unbounded consumer is a slow memory leakconsumersrestricts which consumers cron manages; anything not listed needs its own supervision (systemd unit is the clean answer)
For real supervision, systemd beats cron: Restart=always, log capture, and systemctl status for instant health checks.
The Failures to Watch
- Consumer not running: the bulk API “accepts” requests that never process. Monitor queue depth (
rabbitmqctl list_queues messages) and alert on growth - Poison messages: a handler that fatals on a specific payload can crash-loop its consumer.
max_messageslimits the blast radius; the error log names the payload class - Duplicate processing: design handlers idempotently - messages can be redelivered after a crash. “Create invoice” is not idempotent; “ensure invoice exists for order X” is
- Stale code: long-running consumers hold the code they started with. Restart consumers on every deploy - this is the most-missed step in Magento deployment runbooks
Observability
RabbitMQ’s management UI shows queue depth, consumers and rates at a glance - expose it on an internal port and screenshot it into your runbook. Pair with alerts: queue depth above threshold for 10 minutes, or consumer count below expected.
Queues turn “the request timed out” into “the job will complete shortly” - a better product and a calmer operations life. They ask for supervision and idempotent handlers in return. Provide both and the queue layer is some of the most reliable infrastructure Magento has.