This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a commit to branch 3.9.x
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/3.9.x by this push:
     new 663fff40bc [FIX] RabbitMQ Allow disabling notification queue auto 
delete in favor of x-expires (#3076)
663fff40bc is described below

commit 663fff40bcba8f6056b5d9ab81a0d872dd405ec0
Author: Benoit TELLIER <[email protected]>
AuthorDate: Tue Jun 30 15:49:50 2026 +0200

    [FIX] RabbitMQ Allow disabling notification queue auto delete in favor of 
x-expires (#3076)
---
 .../backends/rabbitmq/RabbitMQConfiguration.java     | 20 +++++++++++++++++++-
 .../pages/distributed/configure/rabbitmq.adoc        |  8 ++++++++
 .../modules/servers/partials/configure/rabbitmq.adoc |  8 ++++++++
 .../apache/james/events/KeyRegistrationHandler.java  |  3 +--
 .../sample-configuration/rabbitmq.properties         | 10 ++++++++++
 .../rabbitmq.properties                              | 10 ++++++++++
 src/site/xdoc/server/config-rabbitmq.xml             |  8 ++++++++
 7 files changed, 64 insertions(+), 3 deletions(-)

diff --git 
a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConfiguration.java
 
b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConfiguration.java
index 0535a96a6f..645ea32648 100644
--- 
a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConfiguration.java
+++ 
b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConfiguration.java
@@ -310,6 +310,7 @@ public class RabbitMQConfiguration {
     private static final String QUEUE_TTL = "notification.queue.ttl";
     private static final String QUEUE_DELIVERY_LIMIT = 
"quorum.queues.delivery.limit";
     private static final String EVENT_BUS_NOTIFICATION_DURABILITY_ENABLED = 
"event.bus.notification.durability.enabled";
+    private static final String EVENT_BUS_NOTIFICATION_QUEUE_AUTO_DELETE = 
"notification.queue.autoDelete";
     private static final String EVENT_BUS_PUBLISH_CONFIRM_ENABLED = 
"event.bus.publish.confirm.enabled";
     private static final String EVENT_BUS_PROPAGATE_DISPATCH_ERROR = 
"event.bus.propagate.dispatch.error";
     private static final String TASK_QUEUE_CONSUMER_TIMEOUT = 
"task.queue.consumer.timeout";
@@ -412,6 +413,7 @@ public class RabbitMQConfiguration {
         private Optional<Long> queueTTL;
         private Optional<Boolean> eventBusPublishConfirmEnabled;
         private Optional<Boolean> eventBusNotificationDurabilityEnabled;
+        private Optional<Boolean> eventBusNotificationQueueAutoDelete;
         private Optional<Boolean> eventBusPropagateDispatchError;
         private Optional<String> vhost;
         private Optional<Duration> taskQueueConsumerTimeout;
@@ -437,6 +439,7 @@ public class RabbitMQConfiguration {
             this.queueTTL = Optional.empty();
             this.eventBusPublishConfirmEnabled = Optional.empty();
             this.eventBusNotificationDurabilityEnabled = Optional.empty();
+            this.eventBusNotificationQueueAutoDelete = Optional.empty();
             this.vhost = Optional.empty();
             this.taskQueueConsumerTimeout = Optional.empty();
             this.eventBusPropagateDispatchError = Optional.empty();
@@ -522,6 +525,11 @@ public class RabbitMQConfiguration {
             return this;
         }
 
+        public Builder eventBusNotificationQueueAutoDelete(Boolean 
eventBusNotificationQueueAutoDelete) {
+            this.eventBusNotificationQueueAutoDelete = 
Optional.ofNullable(eventBusNotificationQueueAutoDelete);
+            return this;
+        }
+
         public Builder useQuorumQueues(Boolean useQuorumQueues) {
             this.useQuorumQueues = Optional.ofNullable(useQuorumQueues);
             return this;
@@ -587,6 +595,7 @@ public class RabbitMQConfiguration {
                     queueTTL,
                     eventBusPublishConfirmEnabled.orElse(true),
                     eventBusNotificationDurabilityEnabled.orElse(true),
+                    
eventBusNotificationQueueAutoDelete.orElse(Constants.AUTO_DELETE),
                     vhost,
                     
taskQueueConsumerTimeout.orElse(DEFAULT_TASK_QUEUE_CONSUMER_TIMEOUT),
                     eventBusPropagateDispatchError.orElse(true));
@@ -663,6 +672,7 @@ public class RabbitMQConfiguration {
             .hosts(hosts)
             .queueTTL(queueTTL)
             
.eventBusNotificationDurabilityEnabled(configuration.getBoolean(EVENT_BUS_NOTIFICATION_DURABILITY_ENABLED,
 null))
+            
.eventBusNotificationQueueAutoDelete(configuration.getBoolean(EVENT_BUS_NOTIFICATION_QUEUE_AUTO_DELETE,
 null))
             
.eventBusPublishConfirmEnabled(configuration.getBoolean(EVENT_BUS_PUBLISH_CONFIRM_ENABLED,
 null))
             
.eventBusPropagateDispatchError(configuration.getBoolean(EVENT_BUS_PROPAGATE_DISPATCH_ERROR,
 null))
             .vhost(vhost)
@@ -738,6 +748,7 @@ public class RabbitMQConfiguration {
     private final Optional<Long> queueTTL;
     private final boolean eventBusPublishConfirmEnabled;
     private final boolean eventBusNotificationDurabilityEnabled;
+    private final boolean eventBusNotificationQueueAutoDelete;
     private final Optional<String> vhost;
     private final Duration taskQueueConsumerTimeout;
     private final boolean eventBusPropagateDispatchError;
@@ -747,6 +758,7 @@ public class RabbitMQConfiguration {
                                   int networkRecoveryIntervalInMs, Boolean 
useSsl, Boolean useSslManagement, SSLConfiguration sslConfiguration,
                                   boolean useQuorumQueues, Optional<Integer> 
quorumQueueDeliveryLimit, int quorumQueueReplicationFactor, List<Host> hosts, 
Optional<Long> queueTTL,
                                   boolean eventBusPublishConfirmEnabled, 
boolean eventBusNotificationDurabilityEnabled,
+                                  boolean eventBusNotificationQueueAutoDelete,
                                   Optional<String> vhost, Duration 
taskQueueConsumerTimeout, boolean eventBusPropagateDispatchError) {
         this.uri = uri;
         this.managementUri = managementUri;
@@ -768,6 +780,7 @@ public class RabbitMQConfiguration {
         this.queueTTL = queueTTL;
         this.eventBusPublishConfirmEnabled = eventBusPublishConfirmEnabled;
         this.eventBusNotificationDurabilityEnabled = 
eventBusNotificationDurabilityEnabled;
+        this.eventBusNotificationQueueAutoDelete = 
eventBusNotificationQueueAutoDelete;
         this.vhost = vhost;
         this.taskQueueConsumerTimeout = taskQueueConsumerTimeout;
         this.eventBusPropagateDispatchError = eventBusPropagateDispatchError;
@@ -862,6 +875,10 @@ public class RabbitMQConfiguration {
         return eventBusNotificationDurabilityEnabled;
     }
 
+    public boolean isEventBusNotificationQueueAutoDeleteEnabled() {
+        return eventBusNotificationQueueAutoDelete;
+    }
+
     public Optional<String> getVhost() {
         return vhost.or(this::getVhostFromPath);
     }
@@ -916,6 +933,7 @@ public class RabbitMQConfiguration {
                 && Objects.equals(this.queueTTL, that.queueTTL)
                 && Objects.equals(this.eventBusPublishConfirmEnabled, 
that.eventBusPublishConfirmEnabled)
                 && Objects.equals(this.eventBusNotificationDurabilityEnabled, 
that.eventBusNotificationDurabilityEnabled)
+                && Objects.equals(this.eventBusNotificationQueueAutoDelete, 
that.eventBusNotificationQueueAutoDelete)
                 && Objects.equals(this.vhost, that.vhost)
                 && Objects.equals(this.taskQueueConsumerTimeout, 
that.taskQueueConsumerTimeout)
                 && Objects.equals(this.eventBusPropagateDispatchError, 
that.eventBusPropagateDispatchError);
@@ -927,6 +945,6 @@ public class RabbitMQConfiguration {
     public final int hashCode() {
         return Objects.hash(uri, managementUri, maxRetries, minDelayInMs, 
connectionTimeoutInMs, quorumQueueReplicationFactor, quorumQueueDeliveryLimit, 
useQuorumQueues, hosts,
             channelRpcTimeoutInMs, handshakeTimeoutInMs, shutdownTimeoutInMs, 
networkRecoveryIntervalInMs, managementCredentials, useSsl, useSslManagement,
-            sslConfiguration, queueTTL, eventBusPublishConfirmEnabled, 
eventBusNotificationDurabilityEnabled, vhost, taskQueueConsumerTimeout, 
eventBusPropagateDispatchError);
+            sslConfiguration, queueTTL, eventBusPublishConfirmEnabled, 
eventBusNotificationDurabilityEnabled, eventBusNotificationQueueAutoDelete, 
vhost, taskQueueConsumerTimeout, eventBusPropagateDispatchError);
     }
 }
diff --git a/docs/modules/servers/pages/distributed/configure/rabbitmq.adoc 
b/docs/modules/servers/pages/distributed/configure/rabbitmq.adoc
index 3f183ed468..bc98d54f6a 100644
--- a/docs/modules/servers/pages/distributed/configure/rabbitmq.adoc
+++ b/docs/modules/servers/pages/distributed/configure/rabbitmq.adoc
@@ -51,4 +51,12 @@ Tips for choosing metricsEnabled are explained in
 This is used only on queues used to share notification patterns, are exclusive 
to a node. If omitted, it will not add the TTL configure when declaring queues.
 Optional integer, defaults is 3600000.
 
+| notification.queue.autoDelete
+| Whether the per-node notification (key registration) queue is declared as 
auto-delete. Optional boolean, defaults to true
+(historical behaviour). Forced to false when `quorum.queues.enable` is true, 
as quorum queues do not support auto-delete.
+Auto-delete queues are removed by RabbitMQ as soon as their last consumer 
disconnects, including on transient connection
+blips, causing key bindings to fail with 404 NOT_FOUND until the queue is 
redeclared. Setting this to false, combined with
+`notification.queue.ttl` (x-expires), is more robust: the queue survives short 
consumer outages and is only garbage
+collected once it has stayed unused for the TTL, which also cleans up the 
queues of nodes that died abruptly.
+
 |===
diff --git a/docs/modules/servers/partials/configure/rabbitmq.adoc 
b/docs/modules/servers/partials/configure/rabbitmq.adoc
index 689bb17a57..3408aba3a2 100644
--- a/docs/modules/servers/partials/configure/rabbitmq.adoc
+++ b/docs/modules/servers/partials/configure/rabbitmq.adoc
@@ -105,6 +105,14 @@ A coma separated list of hosts, example: 
hosts=ip1:5672,ip2:5672
 | event.bus.notification.durability.enabled
 | Whether or not the queue backing notifications should be durable. Optional 
boolean, defaults to true.
 
+| notification.queue.autoDelete
+| Whether the per-node notification (key registration) queue is declared as 
auto-delete. Optional boolean, defaults to true
+(historical behaviour). Forced to false when `quorum.queues.enable` is true, 
as quorum queues do not support auto-delete.
+Auto-delete queues are removed by RabbitMQ as soon as their last consumer 
disconnects, including on transient connection
+blips, causing key bindings to fail with 404 NOT_FOUND until the queue is 
redeclared. Setting this to false, combined with
+`notification.queue.ttl` (x-expires), is more robust: the queue survives short 
consumer outages and is only garbage
+collected once it has stayed unused for the TTL, which also cleans up the 
queues of nodes that died abruptly.
+
 | event.bus.propagate.dispatch.error
 | Whether to propagate errors back to the callers when eventbus fails to 
dispatch group events to RabbitMQ (then store the failed events in the event 
dead letters).
 Optional boolean, defaults to true.
diff --git 
a/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
 
b/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
index 84ab3b88d3..cb1fe51742 100644
--- 
a/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
+++ 
b/event-bus/distributed/src/main/java/org/apache/james/events/KeyRegistrationHandler.java
@@ -19,7 +19,6 @@
 
 package org.apache.james.events;
 
-import static org.apache.james.backends.rabbitmq.Constants.AUTO_DELETE;
 import static org.apache.james.backends.rabbitmq.Constants.EXCLUSIVE;
 import static org.apache.james.backends.rabbitmq.Constants.evaluateAutoDelete;
 import static org.apache.james.backends.rabbitmq.Constants.evaluateDurable;
@@ -132,7 +131,7 @@ class KeyRegistrationHandler {
             QueueSpecification.queue(registrationQueue.asString())
                 
.durable(evaluateDurable(configuration.isEventBusNotificationDurabilityEnabled(),
 configuration.isQuorumQueuesUsed()))
                 .exclusive(evaluateExclusive(!EXCLUSIVE, 
configuration.isQuorumQueuesUsed()))
-                .autoDelete(evaluateAutoDelete(AUTO_DELETE, 
configuration.isQuorumQueuesUsed()))
+                
.autoDelete(evaluateAutoDelete(configuration.isEventBusNotificationQueueAutoDeleteEnabled(),
 configuration.isQuorumQueuesUsed()))
                 .arguments(builder.build()))
             .timeout(TOPOLOGY_CHANGES_TIMEOUT)
             .map(AMQP.Queue.DeclareOk::getQueue)
diff --git 
a/server/apps/distributed-app/sample-configuration/rabbitmq.properties 
b/server/apps/distributed-app/sample-configuration/rabbitmq.properties
index 9ad1edc38d..a370aeb381 100644
--- a/server/apps/distributed-app/sample-configuration/rabbitmq.properties
+++ b/server/apps/distributed-app/sample-configuration/rabbitmq.properties
@@ -98,3 +98,13 @@ task.consumption.enabled=true
 # This is used only on queues used to share notification patterns, are 
exclusive to a node. If omitted, it will not add the TTL configure when 
declaring queues.
 # Optional integer, defaults is 3600000.
 #notification.queue.ttl=3600000
+
+# Boolean. Whether the per-node notification (key registration) queue is 
declared as auto-delete.
+# Defaults to true, which preserves the historical behaviour. Forced to false 
when quorum.queues.enable=true
+# (quorum queues do not support auto-delete).
+# Auto-delete queues are removed by RabbitMQ as soon as their last consumer 
disconnects, including on transient
+# connection blips: the queue then briefly disappears and key bindings fail 
with 404 NOT_FOUND until it is redeclared.
+# Setting this to false (combined with notification.queue.ttl above, i.e. 
x-expires) is more robust: the queue
+# survives short consumer outages and is only garbage collected once it has 
stayed unused for the TTL, which also
+# cleans up the queues of nodes that died abruptly.
+#notification.queue.autoDelete=false
diff --git 
a/server/apps/postgres-app/sample-configuration-distributed/rabbitmq.properties 
b/server/apps/postgres-app/sample-configuration-distributed/rabbitmq.properties
index 75a562af60..d148e1ee4d 100644
--- 
a/server/apps/postgres-app/sample-configuration-distributed/rabbitmq.properties
+++ 
b/server/apps/postgres-app/sample-configuration-distributed/rabbitmq.properties
@@ -93,3 +93,13 @@ task.consumption.enabled=true
 # This is used only on queues used to share notification patterns, are 
exclusive to a node. If omitted, it will not add the TTL configure when 
declaring queues.
 # Optional integer, defaults is 3600000.
 #notification.queue.ttl=3600000
+
+# Boolean. Whether the per-node notification (key registration) queue is 
declared as auto-delete.
+# Defaults to true, which preserves the historical behaviour. Forced to false 
when quorum.queues.enable=true
+# (quorum queues do not support auto-delete).
+# Auto-delete queues are removed by RabbitMQ as soon as their last consumer 
disconnects, including on transient
+# connection blips: the queue then briefly disappears and key bindings fail 
with 404 NOT_FOUND until it is redeclared.
+# Setting this to false (combined with notification.queue.ttl above, i.e. 
x-expires) is more robust: the queue
+# survives short consumer outages and is only garbage collected once it has 
stayed unused for the TTL, which also
+# cleans up the queues of nodes that died abruptly.
+#notification.queue.autoDelete=false
diff --git a/src/site/xdoc/server/config-rabbitmq.xml 
b/src/site/xdoc/server/config-rabbitmq.xml
index b51689e8db..513d3f7ef2 100644
--- a/src/site/xdoc/server/config-rabbitmq.xml
+++ b/src/site/xdoc/server/config-rabbitmq.xml
@@ -148,6 +148,14 @@
           <dt><strong>event.bus.notification.durability.enabled</strong></dt>
           <dd>Whether or not the queue backing notifications should be 
durable. Optional boolean, defaults to true.</dd>
 
+          <dt><strong>notification.queue.autoDelete</strong></dt>
+          <dd>Whether the per-node notification (key registration) queue is 
declared as auto-delete. Optional boolean, defaults to true
+              (historical behaviour). Forced to false when 
<code>quorum.queues.enable</code> is true, as quorum queues do not support 
auto-delete.
+              Auto-delete queues are removed by RabbitMQ as soon as their last 
consumer disconnects, including on transient connection
+              blips, causing key bindings to fail with 404 NOT_FOUND until the 
queue is redeclared. Setting this to false, combined with
+              <code>notification.queue.ttl</code> (x-expires), is more robust: 
the queue survives short consumer outages and is only garbage
+              collected once it has stayed unused for the TTL, which also 
cleans up the queues of nodes that died abruptly.</dd>
+
           <dt><strong>event.bus.propagate.dispatch.error</strong></dt>
           <dd>Whether to propagate errors back to the callers when eventbus 
fails to dispatch group events to RabbitMQ (then store the failed events in the 
event dead letters).
               Optional boolean, defaults to true.</dd>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to