Home/Learn/RabbitMQ/Message Persistence

Message Persistence

Intermediate
Queues

Set delivery_mode=2 (persistent) on messages AND use a durable queue to survive broker restart; fsync overhead makes persistent messages slower than transient ones.

Overview

RabbitMQ message persistence requires two independent settings to work together: the queue must be declared durable (survives broker restart) and each message must have delivery_mode=2 (written to disk before the broker acknowledges the publish). If either is missing — transient message on a durable queue, or persistent message on a non-durable queue — messages are lost on restart. Disk writes are batched for throughput, but this means persistence adds latency compared to in-memory transient queuing. For maximum durability guarantees, combine persistence with publisher confirms: the broker only sends an ack after the message is fsynced to disk. Quorum queues (RabbitMQ 3.8+) are the modern alternative — they replicate to a majority of nodes before acking, providing stronger guarantees than classic durable queues.

Declaring a durable queue with persistent messages

Both the queue durability flag and per-message delivery_mode must be set. Spring AMQP's MessageProperties.PERSISTENT_TEXT_PLAIN is shorthand for delivery_mode=2.

Java — durable queue + persistent message delivery
@Configuration
public class RabbitConfig {

    // durable=true: queue survives broker restart
    // autoDelete=false: not deleted when last consumer disconnects
    @Bean
    public Queue ordersQueue() {
        return QueueBuilder.durable("orders.queue")
                .withArgument("x-queue-type", "quorum") // Recommended: quorum queue
                .build();
    }
}

// Publishing a persistent message
rabbitTemplate.convertAndSend("orders.exchange", "new-order", event, message -> {
    message.getMessageProperties()
           .setDeliveryMode(MessageDeliveryMode.PERSISTENT); // delivery_mode=2
    return message;
});

// Or use the built-in MessagePostProcessor shorthand
rabbitTemplate.convertAndSend("orders.exchange", "new-order",
        event, new MessagePostProcessor() {
    @Override
    public Message postProcessMessage(Message msg) {
        msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        return msg;
    }
});

Publisher confirms for end-to-end durability

Persistence alone does not guarantee delivery — the publish can succeed but the broker can crash before fsyncing. Publisher confirms close this gap: the broker only acks after writing to disk.

Java — publisher confirms with ack/nack callback
@Bean
public CachingConnectionFactory connectionFactory() {
    CachingConnectionFactory cf = new CachingConnectionFactory("localhost");
    cf.setPublisherConfirmType(
        CachingConnectionFactory.ConfirmType.CORRELATED); // async confirms
    return cf;
}

@Bean
public RabbitTemplate rabbitTemplate(CachingConnectionFactory cf) {
    RabbitTemplate template = new RabbitTemplate(cf);

    template.setConfirmCallback((correlationData, ack, cause) -> {
        if (ack) {
            log.info("Message confirmed: {}", correlationData.getId());
        } else {
            log.error("Nack received — requeue or alert: {}", cause);
            // trigger retry logic or dead-letter processing
        }
    });

    template.setMandatory(true);  // trigger returns for unroutable messages
    template.setReturnsCallback(returned ->
        log.error("Message returned unrouted: {}", returned.getMessage()));

    return template;
}

Classic durable vs Quorum queues — when to use which

Classic mirrored queues are deprecated since RabbitMQ 3.9. Quorum queues use Raft consensus for replication, providing stronger durability and are the recommended replacement.

Java — Quorum queue declaration
# Classic durable queue (single node or classic mirrored — deprecated)
# Survives restart but all data on one node; mirroring adds replication lag

# Quorum queue — recommended for production durability
@Bean
public Queue paymentsQueue() {
    return QueueBuilder.durable("payments.queue")
        .quorum()                            // x-queue-type: quorum
        .deliveryLimit(5)                    // max redelivery attempts
        .build();
}
# Quorum queues replicate to (N/2 + 1) nodes before acking
# No message loss on single-node failure
# Requires RabbitMQ 3.8+ with at least 3 nodes for full benefit

# application.properties tuning
spring.rabbitmq.template.default-receive-queue=payments.queue
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.prefetch=10

Key Points to Remember

  • 1Both queue durability AND message delivery_mode=2 are required — one without the other is not persistence.
  • 2Persistent messages add latency because RabbitMQ must fsync before acknowledging; batch fsyncing mitigates this.
  • 3Publisher confirms provide at-least-once delivery: only send after the broker confirms disk write.
  • 4Classic mirrored queues are deprecated since RabbitMQ 3.9; use Quorum queues for replicated persistence.
  • 5Quorum queues require a majority of replicas to be available — plan your cluster size (3 or 5 nodes) accordingly.
  • 6For very high throughput, consider separating persistent critical queues (payments) from transient queues (logs) on different vhosts.

Interview Questions

Sign in to ask Aria
1

What are the two conditions required for a message to survive a RabbitMQ broker restart?

EasyPivotal
2

What is the difference between publisher confirms and transactions in RabbitMQ?

MediumVMware
3

Why are classic mirrored queues deprecated and what should you use instead?

MediumZalando
4

Explain how quorum queues use Raft consensus and what "delivery limit" prevents.

HardAmazon
5

A persistent message was published but lost after a broker crash with no publisher confirms. What went wrong?

MediumNetflix

Ask Aria about Message Persistence

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…