Open
Description
Description
By default, AMQP doesn't guarantee message delivery to the queue. There are 2 ways of ensuring delivery:
- using Publisher Confirms (specifically using RabbitMQ: https://www.rabbitmq.com/confirms.html#publisher-confirms). It is already implemented as
confirm_timeout
option. - using transactions
Currently there is no option to use transactions with Symfony Messenger to ensure message deliverability. Using this option wouldn't even have a big effect on performance, since all the messages are published one-by-one by this component.
Example
Introducing simple transport attribute, like transactional
:
email:
dsn: '%env(resolve:MESSENGER_DSN)%'
options:
transactional: true
exchange:
name: some-name
queues:
some-name: ~
Which could be implemented with a simple change to Symfony\Component\Messenger\Bridge\Amqp\Transport\Connection
method publishOnExchange
.
Before:
private function publishOnExchange(\AMQPExchange $exchange, string $body, string $routingKey = null, array $headers = [], AmqpStamp $amqpStamp = null)
{
$attributes = $amqpStamp ? $amqpStamp->getAttributes() : [];
$attributes['headers'] = array_merge($attributes['headers'] ?? [], $headers);
$attributes['delivery_mode'] ??= 2;
$attributes['timestamp'] ??= time();
$this->lastActivityTime = time();
$exchange->publish(
$body,
$routingKey,
$amqpStamp ? $amqpStamp->getFlags() : \AMQP_NOPARAM,
$attributes
);
if ('' !== ($this->connectionOptions['confirm_timeout'] ?? '')) {
$this->channel()->waitForConfirm((float) $this->connectionOptions['confirm_timeout']);
}
}
After:
private function publishOnExchange(\AMQPExchange $exchange, string $body, string $routingKey = null, array $headers = [], AmqpStamp $amqpStamp = null)
{
$attributes = $amqpStamp ? $amqpStamp->getAttributes() : [];
$attributes['headers'] = array_merge($attributes['headers'] ?? [], $headers);
$attributes['delivery_mode'] ??= 2;
$attributes['timestamp'] ??= time();
$transactional = $this->connectionOptions['transactional'] ??= false;
$this->lastActivityTime = time();
if ($transactional) {
$exchange->getChannel()->startTransaction();
}
$exchange->publish(
$body,
$routingKey,
$amqpStamp ? $amqpStamp->getFlags() : \AMQP_NOPARAM,
$attributes
);
if ($transactional) {
$exchange->getChannel()->commitTransaction();
}
if ('' !== ($this->connectionOptions['confirm_timeout'] ?? '')) {
$this->channel()->waitForConfirm((float) $this->connectionOptions['confirm_timeout']);
}
}