I had understood that the JMS semantics specify that only durable subscribers to a topic are guaranteed message delivery for a persistent delivery mode, even in the face of message-broker provider failure. But what does it have to say about non-durable subscribers for persistent messages? What’s the point of sending a message “persistent” when there are no durable subscribers?
Upon looking at the exact wording of the spec, I became a little unsure. So I consulted the Java Message Service book (Richards, Monson-Haefel, and Chappell) for some more discussion around guaranteed messaging, checked into the ActiveMQ source code, and consulted with some of my co-workers.
First, let’s look at what the spec says:
From section 4.10 of the JMS spec:
Most clients should use producers that produce PERSISTENT messages. This insures once-and-only-once message delivery for messages delivered from a queue or a durable subscription.
Pretty clear, right? Using persistent message delivery ensures message delivery for a queue or durable subscription.
From section 6.12:
Unacknowledged messages of a nondurable subscriber should be able to be recovered for the lifetime of that nondurable subscriber.
So now unacked messages of a non-durable subscriber should be able to be recovered? “for the lifetime of that non-durable subscriber” I guess…
But later as part of 6.12:
Only durable subscriptions are reliably able to recover unacknowledged messages.
To ensure delivery, a TopicSubscriber should establish a durable subscription.
Although the spec says very clearly [to the effect] that only queues and durable subscribers can take advantage of the store-and-forward guaranteed delivery, I guess I become confused at the “messages of a non-durable subscriber should be able to be recovered for the lifetime of the non-durable subscriber”
- Does the persistent protocol change for a topic depending on its consumers (where the message is considered the responsibility of the broker only after the broker has persisted the message and sent the producer an ack)?
- Does that mean even in the event of broker failure? Or is broker failure considered and end of the life of the subscription for non-durable subs?
- What happens with ActiveMQ when there is a network of brokers for persistent, non-durable topics? Can messages be missed if a broker in the network fails?
- What are the exact differences between sending a message “persistent” vs “non-persistent” to a topic with non-durable subscribers?
These are two parts that have to be considered for this discussion of guaranteed delivery. The part where the publisher is sending a message to the broker, and when the consumer is receiving a message from the broker. For persistent messages, the protocol is for the sender to send a message and the broker to ack the message only after it has been persisted to a store. On the other hand, the consumer must ack the message after the broker has delivered it to say “hey, I’ll take responsibility of the message now”. Only then will the broker relinquish responsibility and removing it from its store.
Does the protocol change for a topic depending on its consumers?
So for persistent messages sent to a topic (not taking into any account consumers at the moment), does the spec say anything about whether the message is supposed to be stored before the broker sends back its ack? No, it doesn’t. It’s left up to the implementors of the JMS broker in question. In the case of ActiveMQ, if there are ONLY non-durable subscriptions on a topic, it will NOT persist the message.
The synchronous nature of the protocol does not change, i.e., if the message is sent persistent, the session will consider the exchange with the broker to be synchronous, and it will wait for a response from the broker before proceeding — but the broker will not actually persist the message. In ActiveMQ, this changes if there is AT LEAST one durable subscriber. Then the broker will persist the message (per the JMS spec).
Does that mean even in the event of a broker failure?
The lifetime of a non-durable subscription is indeed broken if a broker fails. So a message will not be delivered, even if it’s sent persistent, to a non-durable subscriber in the event of broker failure (or any other termination of the life of that non-durable sub). Additionally, a message will not be redelivered in the face of broker failure for non-durable subscriptions.
What happens in a network of brokers?
Messages can indeed be lost. Consider this network of brokers where A -> B -> C and subscription is demand forwarded from C -> B -> A. So if we have a producer at A producing to a topic “topic.foo” and a non-durable consumer is on broker C consuming from “topic.foo”, if broker B goes down, messages thereafter sent to A will be dropped. The lifetime of the subscription as far as A knows has been terminated.
What are the exact differences between sending a message “persistent” vs “non-persistent” to a topic with non-durable subscribers?
According to the JMS spec:
|How Published||Nondurable Subscriber||Durable Subscriber|
|NON_PERSISTENT||at-most-once (missed if inactive)||at-most-once|
|PERSISTENT||once-and-only-once (missed if inactive)||once-and-only-once|
So for a non-durable subscriber, a non-persistent message will be delivered “at most once” but will be missed if inactive (or broker failure).
For a non-durable subscriber, a persistent message will be delivered “once and only once” but missed if inactive. The “inactive” part of the spec effectively means if there are no durable subscribers to a topic, a message could be lost and there is no guarantee of delivery regardless of whether the message is sent persistent or non-persistent.
Reference: Guaranteed messaging for topics, the JMS spec, and ActiveMQ from our JCG partner Christian Posta at the Christian Posta Software blog.
Get ready to program in a whole new way!
Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.