ARTEMIS-5142 support never expiring incoming messages

This commit is contained in:
Justin Bertram 2024-11-01 11:19:22 -05:00 committed by Robbie Gemmell
parent e22f0ecba2
commit a7a70d6595
14 changed files with 728 additions and 32 deletions

View File

@ -304,6 +304,11 @@ public final class AddressSettingsInfo {
} }
private long maxExpiryDelay; private long maxExpiryDelay;
static {
META_BEAN.add(Boolean.class, "noExpiry", (o, p) -> o.noExpiry = p, o -> o.noExpiry);
}
private boolean noExpiry;
static { static {
META_BEAN.add(Boolean.class, "enableMetrics", (o, p) -> o.enableMetrics = p, o -> o.enableMetrics); META_BEAN.add(Boolean.class, "enableMetrics", (o, p) -> o.enableMetrics = p, o -> o.enableMetrics);
} }
@ -556,6 +561,10 @@ public final class AddressSettingsInfo {
return maxExpiryDelay; return maxExpiryDelay;
} }
public boolean isNoExpiry() {
return noExpiry;
}
public boolean isEnableMetrics() { public boolean isEnableMetrics() {
return enableMetrics; return enableMetrics;
} }

View File

@ -233,6 +233,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
private static final String MAX_EXPIRY_DELAY_NODE_NAME = "max-expiry-delay"; private static final String MAX_EXPIRY_DELAY_NODE_NAME = "max-expiry-delay";
private static final String NO_EXPIRY_NODE_NAME = "no-expiry";
private static final String REDELIVERY_DELAY_NODE_NAME = "redelivery-delay"; private static final String REDELIVERY_DELAY_NODE_NAME = "redelivery-delay";
private static final String REDELIVERY_DELAY_MULTIPLIER_NODE_NAME = "redelivery-delay-multiplier"; private static final String REDELIVERY_DELAY_MULTIPLIER_NODE_NAME = "redelivery-delay-multiplier";
@ -1331,6 +1333,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
addressSettings.setMinExpiryDelay(XMLUtil.parseLong(child)); addressSettings.setMinExpiryDelay(XMLUtil.parseLong(child));
} else if (MAX_EXPIRY_DELAY_NODE_NAME.equalsIgnoreCase(name)) { } else if (MAX_EXPIRY_DELAY_NODE_NAME.equalsIgnoreCase(name)) {
addressSettings.setMaxExpiryDelay(XMLUtil.parseLong(child)); addressSettings.setMaxExpiryDelay(XMLUtil.parseLong(child));
} else if (NO_EXPIRY_NODE_NAME.equalsIgnoreCase(name)) {
addressSettings.setNoExpiry(XMLUtil.parseBoolean(child));
} else if (REDELIVERY_DELAY_NODE_NAME.equalsIgnoreCase(name)) { } else if (REDELIVERY_DELAY_NODE_NAME.equalsIgnoreCase(name)) {
addressSettings.setRedeliveryDelay(XMLUtil.parseLong(child)); addressSettings.setRedeliveryDelay(XMLUtil.parseLong(child));
} else if (REDELIVERY_DELAY_MULTIPLIER_NODE_NAME.equalsIgnoreCase(name)) { } else if (REDELIVERY_DELAY_MULTIPLIER_NODE_NAME.equalsIgnoreCase(name)) {

View File

@ -1355,28 +1355,44 @@ public class PostOfficeImpl implements PostOffice, NotificationListener, Binding
return status; return status;
} }
// HORNETQ-1029 protected static void applyExpiryDelay(Message message, AddressSettings settings) {
private static void applyExpiryDelay(Message message, AddressSettings settings) {
long expirationOverride = settings.getExpiryDelay(); long expirationOverride = settings.getExpiryDelay();
// A -1 <expiry-delay> means don't do anything if (settings.isNoExpiry()) {
if (expirationOverride >= 0) { if (message.getExpiration() != 0) {
// only override the expiration on messages where the expiration hasn't been set by the user message.setExpiration(0);
message.reencode();
}
} else if (expirationOverride >= 0) {
// A -1 <expiry-delay> means don't do anything
if (message.getExpiration() == 0) { if (message.getExpiration() == 0) {
message.setExpiration(System.currentTimeMillis() + expirationOverride); // only override the expiration on messages where the expiration hasn't been set by the user
setExpiration(message, expirationOverride);
} }
} else { } else {
long minExpiration = settings.getMinExpiryDelay(); long minExpiration = settings.getMinExpiryDelay();
long maxExpiration = settings.getMaxExpiryDelay(); long maxExpiration = settings.getMaxExpiryDelay();
if (maxExpiration != AddressSettings.DEFAULT_MAX_EXPIRY_DELAY && (message.getExpiration() == 0 || message.getExpiration() > (System.currentTimeMillis() + maxExpiration))) { if (message.getExpiration() == 0) {
message.setExpiration(System.currentTimeMillis() + maxExpiration); // if the incoming message has NO expiration then apply the max if set and if not set then apply the min if set
if (maxExpiration != AddressSettings.DEFAULT_MAX_EXPIRY_DELAY) {
setExpiration(message, maxExpiration);
} else if (minExpiration != AddressSettings.DEFAULT_MIN_EXPIRY_DELAY) {
setExpiration(message, minExpiration);
}
} else if (maxExpiration != AddressSettings.DEFAULT_MAX_EXPIRY_DELAY && message.getExpiration() > (System.currentTimeMillis() + maxExpiration)) {
setExpiration(message, maxExpiration);
} else if (minExpiration != AddressSettings.DEFAULT_MIN_EXPIRY_DELAY && message.getExpiration() < (System.currentTimeMillis() + minExpiration)) { } else if (minExpiration != AddressSettings.DEFAULT_MIN_EXPIRY_DELAY && message.getExpiration() < (System.currentTimeMillis() + minExpiration)) {
message.setExpiration(System.currentTimeMillis() + minExpiration); setExpiration(message, minExpiration);
} }
} }
} }
private static void setExpiration(Message m, long expiration) {
m.setExpiration(System.currentTimeMillis() + expiration);
m.reencode();
}
@Override @Override
public MessageReference reload(final Message message, final Queue queue, final Transaction tx) throws Exception { public MessageReference reload(final Message message, final Queue queue, final Transaction tx) throws Exception {

View File

@ -117,6 +117,8 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
public static final long DEFAULT_MAX_EXPIRY_DELAY = -1; public static final long DEFAULT_MAX_EXPIRY_DELAY = -1;
public static final boolean DEFAULT_NO_EXPIRY = false;
public static final boolean DEFAULT_SEND_TO_DLA_ON_NO_ROUTE = false; public static final boolean DEFAULT_SEND_TO_DLA_ON_NO_ROUTE = false;
public static final long DEFAULT_SLOW_CONSUMER_THRESHOLD = -1; public static final long DEFAULT_SLOW_CONSUMER_THRESHOLD = -1;
@ -266,6 +268,11 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
} }
private Long maxExpiryDelay = null; private Long maxExpiryDelay = null;
static {
metaBean.add(Boolean.class, "noExpiry", (t, p) -> t.noExpiry = p, t -> t.noExpiry);
}
private Boolean noExpiry = null;
static { static {
metaBean.add(Boolean.class, "defaultLastValueQueue", (t, p) -> t.defaultLastValueQueue = p, t -> t.defaultLastValueQueue); metaBean.add(Boolean.class, "defaultLastValueQueue", (t, p) -> t.defaultLastValueQueue = p, t -> t.defaultLastValueQueue);
} }
@ -1050,6 +1057,15 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
return this; return this;
} }
public Boolean isNoExpiry() {
return noExpiry != null ? noExpiry : AddressSettings.DEFAULT_NO_EXPIRY;
}
public AddressSettings setNoExpiry(final Boolean noExpiry) {
this.noExpiry = noExpiry;
return this;
}
public boolean isSendToDLAOnNoRoute() { public boolean isSendToDLAOnNoRoute() {
return sendToDLAOnNoRoute != null ? sendToDLAOnNoRoute : AddressSettings.DEFAULT_SEND_TO_DLA_ON_NO_ROUTE; return sendToDLAOnNoRoute != null ? sendToDLAOnNoRoute : AddressSettings.DEFAULT_SEND_TO_DLA_ON_NO_ROUTE;
} }
@ -1694,6 +1710,8 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
return false; return false;
if (!Objects.equals(maxExpiryDelay, that.maxExpiryDelay)) if (!Objects.equals(maxExpiryDelay, that.maxExpiryDelay))
return false; return false;
if (!Objects.equals(noExpiry, that.noExpiry))
return false;
if (!Objects.equals(defaultLastValueQueue, that.defaultLastValueQueue)) if (!Objects.equals(defaultLastValueQueue, that.defaultLastValueQueue))
return false; return false;
if (!Objects.equals(defaultLastValueKey, that.defaultLastValueKey)) if (!Objects.equals(defaultLastValueKey, that.defaultLastValueKey))
@ -1830,6 +1848,7 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
result = 31 * result + (expiryDelay != null ? expiryDelay.hashCode() : 0); result = 31 * result + (expiryDelay != null ? expiryDelay.hashCode() : 0);
result = 31 * result + (minExpiryDelay != null ? minExpiryDelay.hashCode() : 0); result = 31 * result + (minExpiryDelay != null ? minExpiryDelay.hashCode() : 0);
result = 31 * result + (maxExpiryDelay != null ? maxExpiryDelay.hashCode() : 0); result = 31 * result + (maxExpiryDelay != null ? maxExpiryDelay.hashCode() : 0);
result = 31 * result + (noExpiry != null ? noExpiry.hashCode() : 0);
result = 31 * result + (defaultLastValueQueue != null ? defaultLastValueQueue.hashCode() : 0); result = 31 * result + (defaultLastValueQueue != null ? defaultLastValueQueue.hashCode() : 0);
result = 31 * result + (defaultLastValueKey != null ? defaultLastValueKey.hashCode() : 0); result = 31 * result + (defaultLastValueKey != null ? defaultLastValueKey.hashCode() : 0);
result = 31 * result + (defaultNonDestructive != null ? defaultNonDestructive.hashCode() : 0); result = 31 * result + (defaultNonDestructive != null ? defaultNonDestructive.hashCode() : 0);
@ -1889,7 +1908,7 @@ public class AddressSettings implements Mergeable<AddressSettings>, Serializable
@Override @Override
public String toString() { public String toString() {
return "AddressSettings{" + "addressFullMessagePolicy=" + addressFullMessagePolicy + ", maxSizeBytes=" + maxSizeBytes + ", maxReadPageBytes=" + maxReadPageBytes + ", maxReadPageMessages=" + maxReadPageMessages + ", prefetchPageBytes=" + prefetchPageBytes + ", prefetchPageMessages=" + prefetchPageMessages + ", pageLimitBytes=" + pageLimitBytes + ", pageLimitMessages=" + pageLimitMessages + ", pageFullMessagePolicy=" + pageFullMessagePolicy + ", maxSizeMessages=" + maxSizeMessages + ", pageSizeBytes=" + pageSizeBytes + ", pageMaxCache=" + pageCacheMaxSize + ", dropMessagesWhenFull=" + dropMessagesWhenFull + ", maxDeliveryAttempts=" + maxDeliveryAttempts + ", messageCounterHistoryDayLimit=" + messageCounterHistoryDayLimit + ", redeliveryDelay=" + redeliveryDelay + ", redeliveryMultiplier=" + redeliveryMultiplier + ", redeliveryCollisionAvoidanceFactor=" + redeliveryCollisionAvoidanceFactor + ", maxRedeliveryDelay=" + maxRedeliveryDelay + ", deadLetterAddress=" + deadLetterAddress + ", expiryAddress=" + expiryAddress + ", expiryDelay=" + expiryDelay + ", minExpiryDelay=" + minExpiryDelay + ", maxExpiryDelay=" + maxExpiryDelay + ", defaultLastValueQueue=" + defaultLastValueQueue + ", defaultLastValueKey=" + defaultLastValueKey + ", defaultNonDestructive=" + defaultNonDestructive + ", defaultExclusiveQueue=" + defaultExclusiveQueue + ", defaultGroupRebalance=" + defaultGroupRebalance + ", defaultGroupRebalancePauseDispatch=" + defaultGroupRebalancePauseDispatch + ", defaultGroupBuckets=" + defaultGroupBuckets + ", defaultGroupFirstKey=" + defaultGroupFirstKey + ", redistributionDelay=" + redistributionDelay + ", sendToDLAOnNoRoute=" + sendToDLAOnNoRoute + ", slowConsumerThreshold=" + slowConsumerThreshold + ", slowConsumerThresholdMeasurementUnit=" + slowConsumerThresholdMeasurementUnit + ", slowConsumerCheckPeriod=" + slowConsumerCheckPeriod + ", slowConsumerPolicy=" + slowConsumerPolicy + ", autoCreateJmsQueues=" + autoCreateJmsQueues + ", autoDeleteJmsQueues=" + autoDeleteJmsQueues + ", autoCreateJmsTopics=" + autoCreateJmsTopics + ", autoDeleteJmsTopics=" + autoDeleteJmsTopics + ", autoCreateQueues=" + autoCreateQueues + ", autoDeleteQueues=" + autoDeleteQueues + ", autoDeleteCreatedQueues=" + autoDeleteCreatedQueues + ", autoDeleteQueuesDelay=" + autoDeleteQueuesDelay + ", autoDeleteQueuesSkipUsageCheck=" + autoDeleteQueuesSkipUsageCheck + ", autoDeleteQueuesMessageCount=" + autoDeleteQueuesMessageCount + ", defaultRingSize=" + defaultRingSize + ", retroactiveMessageCount=" + retroactiveMessageCount + ", configDeleteQueues=" + configDeleteQueues + ", autoCreateAddresses=" + autoCreateAddresses + ", autoDeleteAddresses=" + autoDeleteAddresses + ", autoDeleteAddressesDelay=" + autoDeleteAddressesDelay + ", autoDeleteAddressesSkipUsageCheck=" + autoDeleteAddressesSkipUsageCheck + ", configDeleteAddresses=" + configDeleteAddresses + ", configDeleteDiverts=" + configDeleteDiverts + ", managementBrowsePageSize=" + managementBrowsePageSize + ", maxSizeBytesRejectThreshold=" + maxSizeBytesRejectThreshold + ", defaultMaxConsumers=" + defaultMaxConsumers + ", defaultPurgeOnNoConsumers=" + defaultPurgeOnNoConsumers + ", defaultConsumersBeforeDispatch=" + defaultConsumersBeforeDispatch + ", defaultDelayBeforeDispatch=" + defaultDelayBeforeDispatch + ", defaultQueueRoutingType=" + defaultQueueRoutingType + ", defaultAddressRoutingType=" + defaultAddressRoutingType + ", defaultConsumerWindowSize=" + defaultConsumerWindowSize + ", autoCreateDeadLetterResources=" + autoCreateDeadLetterResources + ", deadLetterQueuePrefix=" + deadLetterQueuePrefix + ", deadLetterQueueSuffix=" + deadLetterQueueSuffix + ", autoCreateExpiryResources=" + autoCreateExpiryResources + ", expiryQueuePrefix=" + expiryQueuePrefix + ", expiryQueueSuffix=" + expiryQueueSuffix + ", enableMetrics=" + enableMetrics + ", managementMessageAttributeSizeLimit=" + managementMessageAttributeSizeLimit + ", enableIngressTimestamp=" + enableIngressTimestamp + ", idCacheSize=" + idCacheSize + ", queuePrefetch=" + queuePrefetch + ", initialQueueBufferSize=" + initialQueueBufferSize return "AddressSettings{" + "addressFullMessagePolicy=" + addressFullMessagePolicy + ", maxSizeBytes=" + maxSizeBytes + ", maxReadPageBytes=" + maxReadPageBytes + ", maxReadPageMessages=" + maxReadPageMessages + ", prefetchPageBytes=" + prefetchPageBytes + ", prefetchPageMessages=" + prefetchPageMessages + ", pageLimitBytes=" + pageLimitBytes + ", pageLimitMessages=" + pageLimitMessages + ", pageFullMessagePolicy=" + pageFullMessagePolicy + ", maxSizeMessages=" + maxSizeMessages + ", pageSizeBytes=" + pageSizeBytes + ", pageMaxCache=" + pageCacheMaxSize + ", dropMessagesWhenFull=" + dropMessagesWhenFull + ", maxDeliveryAttempts=" + maxDeliveryAttempts + ", messageCounterHistoryDayLimit=" + messageCounterHistoryDayLimit + ", redeliveryDelay=" + redeliveryDelay + ", redeliveryMultiplier=" + redeliveryMultiplier + ", redeliveryCollisionAvoidanceFactor=" + redeliveryCollisionAvoidanceFactor + ", maxRedeliveryDelay=" + maxRedeliveryDelay + ", deadLetterAddress=" + deadLetterAddress + ", expiryAddress=" + expiryAddress + ", expiryDelay=" + expiryDelay + ", minExpiryDelay=" + minExpiryDelay + ", maxExpiryDelay=" + maxExpiryDelay + ", noExpiry=" + noExpiry + ", defaultLastValueQueue=" + defaultLastValueQueue + ", defaultLastValueKey=" + defaultLastValueKey + ", defaultNonDestructive=" + defaultNonDestructive + ", defaultExclusiveQueue=" + defaultExclusiveQueue + ", defaultGroupRebalance=" + defaultGroupRebalance + ", defaultGroupRebalancePauseDispatch=" + defaultGroupRebalancePauseDispatch + ", defaultGroupBuckets=" + defaultGroupBuckets + ", defaultGroupFirstKey=" + defaultGroupFirstKey + ", redistributionDelay=" + redistributionDelay + ", sendToDLAOnNoRoute=" + sendToDLAOnNoRoute + ", slowConsumerThreshold=" + slowConsumerThreshold + ", slowConsumerThresholdMeasurementUnit=" + slowConsumerThresholdMeasurementUnit + ", slowConsumerCheckPeriod=" + slowConsumerCheckPeriod + ", slowConsumerPolicy=" + slowConsumerPolicy + ", autoCreateJmsQueues=" + autoCreateJmsQueues + ", autoDeleteJmsQueues=" + autoDeleteJmsQueues + ", autoCreateJmsTopics=" + autoCreateJmsTopics + ", autoDeleteJmsTopics=" + autoDeleteJmsTopics + ", autoCreateQueues=" + autoCreateQueues + ", autoDeleteQueues=" + autoDeleteQueues + ", autoDeleteCreatedQueues=" + autoDeleteCreatedQueues + ", autoDeleteQueuesDelay=" + autoDeleteQueuesDelay + ", autoDeleteQueuesSkipUsageCheck=" + autoDeleteQueuesSkipUsageCheck + ", autoDeleteQueuesMessageCount=" + autoDeleteQueuesMessageCount + ", defaultRingSize=" + defaultRingSize + ", retroactiveMessageCount=" + retroactiveMessageCount + ", configDeleteQueues=" + configDeleteQueues + ", autoCreateAddresses=" + autoCreateAddresses + ", autoDeleteAddresses=" + autoDeleteAddresses + ", autoDeleteAddressesDelay=" + autoDeleteAddressesDelay + ", autoDeleteAddressesSkipUsageCheck=" + autoDeleteAddressesSkipUsageCheck + ", configDeleteAddresses=" + configDeleteAddresses + ", configDeleteDiverts=" + configDeleteDiverts + ", managementBrowsePageSize=" + managementBrowsePageSize + ", maxSizeBytesRejectThreshold=" + maxSizeBytesRejectThreshold + ", defaultMaxConsumers=" + defaultMaxConsumers + ", defaultPurgeOnNoConsumers=" + defaultPurgeOnNoConsumers + ", defaultConsumersBeforeDispatch=" + defaultConsumersBeforeDispatch + ", defaultDelayBeforeDispatch=" + defaultDelayBeforeDispatch + ", defaultQueueRoutingType=" + defaultQueueRoutingType + ", defaultAddressRoutingType=" + defaultAddressRoutingType + ", defaultConsumerWindowSize=" + defaultConsumerWindowSize + ", autoCreateDeadLetterResources=" + autoCreateDeadLetterResources + ", deadLetterQueuePrefix=" + deadLetterQueuePrefix + ", deadLetterQueueSuffix=" + deadLetterQueueSuffix + ", autoCreateExpiryResources=" + autoCreateExpiryResources + ", expiryQueuePrefix=" + expiryQueuePrefix + ", expiryQueueSuffix=" + expiryQueueSuffix + ", enableMetrics=" + enableMetrics + ", managementMessageAttributeSizeLimit=" + managementMessageAttributeSizeLimit + ", enableIngressTimestamp=" + enableIngressTimestamp + ", idCacheSize=" + idCacheSize + ", queuePrefetch=" + queuePrefetch + ", initialQueueBufferSize=" + initialQueueBufferSize
+ '}'; + '}';
} }
} }

View File

@ -3871,6 +3871,14 @@
</xsd:annotation> </xsd:annotation>
</xsd:element> </xsd:element>
<xsd:element name="no-expiry" type="xsd:boolean" default="false" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Overrides the expiration time for all messages so that they never expire.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="expiry-delay" type="xsd:long" default="-1" maxOccurs="1" minOccurs="0"> <xsd:element name="expiry-delay" type="xsd:long" default="-1" maxOccurs="1" minOccurs="0">
<xsd:annotation> <xsd:annotation>
<xsd:documentation> <xsd:documentation>

View File

@ -506,6 +506,7 @@ public class FileConfigurationTest extends AbstractConfigurationTestBase {
assertEquals(1L, (long) conf.getAddressSettings().get("a1").getExpiryDelay()); assertEquals(1L, (long) conf.getAddressSettings().get("a1").getExpiryDelay());
assertEquals(2L, (long) conf.getAddressSettings().get("a1").getMinExpiryDelay()); assertEquals(2L, (long) conf.getAddressSettings().get("a1").getMinExpiryDelay());
assertEquals(3L, (long) conf.getAddressSettings().get("a1").getMaxExpiryDelay()); assertEquals(3L, (long) conf.getAddressSettings().get("a1").getMaxExpiryDelay());
assertTrue(conf.getAddressSettings().get("a1").isNoExpiry());
assertEquals(AddressSettings.DEFAULT_AUTO_CREATE_EXPIRY_RESOURCES, conf.getAddressSettings().get("a1").isAutoCreateExpiryResources()); assertEquals(AddressSettings.DEFAULT_AUTO_CREATE_EXPIRY_RESOURCES, conf.getAddressSettings().get("a1").isAutoCreateExpiryResources());
assertEquals(AddressSettings.DEFAULT_EXPIRY_QUEUE_PREFIX, conf.getAddressSettings().get("a1").getExpiryQueuePrefix()); assertEquals(AddressSettings.DEFAULT_EXPIRY_QUEUE_PREFIX, conf.getAddressSettings().get("a1").getExpiryQueuePrefix());
assertEquals(AddressSettings.DEFAULT_EXPIRY_QUEUE_SUFFIX, conf.getAddressSettings().get("a1").getExpiryQueueSuffix()); assertEquals(AddressSettings.DEFAULT_EXPIRY_QUEUE_SUFFIX, conf.getAddressSettings().get("a1").getExpiryQueueSuffix());
@ -547,6 +548,7 @@ public class FileConfigurationTest extends AbstractConfigurationTestBase {
assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getExpiryDelay()); assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getExpiryDelay());
assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getMinExpiryDelay()); assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getMinExpiryDelay());
assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getMaxExpiryDelay()); assertEquals(-1L, (long) conf.getAddressSettings().get("a2").getMaxExpiryDelay());
assertFalse(conf.getAddressSettings().get("a2").isNoExpiry());
assertTrue(conf.getAddressSettings().get("a2").isAutoCreateDeadLetterResources()); assertTrue(conf.getAddressSettings().get("a2").isAutoCreateDeadLetterResources());
assertEquals("", conf.getAddressSettings().get("a2").getExpiryQueuePrefix().toString()); assertEquals("", conf.getAddressSettings().get("a2").getExpiryQueuePrefix().toString());
assertEquals(".EXP", conf.getAddressSettings().get("a2").getExpiryQueueSuffix().toString()); assertEquals(".EXP", conf.getAddressSettings().get("a2").getExpiryQueueSuffix().toString());

View File

@ -0,0 +1,308 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.postoffice.impl;
import org.apache.activemq.artemis.api.core.Message;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class PostOfficeImplTest {
private static final int EXPIRATION_DELTA = 5000;
@Test
public void testNoExpiryWhenExpirationSetLow() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(1L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true));
Mockito.verify(mockMessage).setExpiration(0);
}
@Test
public void testNoExpiryWhenExpirationSetHigh() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(Long.MAX_VALUE);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true));
Mockito.verify(mockMessage).setExpiration(0);
}
@Test
public void testNoExpiryWhenExpirationNotSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testExpiryDelayWhenExpirationNotSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
final long expiryDelay = 123456L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setExpiryDelay(expiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + expiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testExpiryDelayWhenExpirationSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(1L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setExpiryDelay(9999L));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testMinExpiryDelayWhenExpirationNotSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
final long minExpiryDelay = 123456L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + minExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMinExpiryDelayWhenExpirationSet() {
Message mockMessage = Mockito.mock(Message.class);
long origExpiration = 1234L;
Mockito.when(mockMessage.getExpiration()).thenReturn(origExpiration);
final long minExpiryDelay = 123456L;
assertTrue(minExpiryDelay > origExpiration);
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + minExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMaxExpiryDelayWhenExpirationNotSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
final long maxExpiryDelay = 123456L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + maxExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMaxExpiryDelayWhenExpirationSet() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(Long.MAX_VALUE);
final long maxExpiryDelay = 123456L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + maxExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMinAndMaxExpiryDelayWhenExpirationNotSet() {
Message mockMessage = Mockito.mock(Message.class);
long origExpiration = 0L;
Mockito.when(mockMessage.getExpiration()).thenReturn(origExpiration);
final long minExpiryDelay = 100_000L;
final long maxExpiryDelay = 300_000L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay).setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + maxExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMinAndMaxExpiryDelayWhenExpirationSetInbetween() {
Message mockMessage = Mockito.mock(Message.class);
final long startTime = System.currentTimeMillis();
long origExpiration = startTime + 200_000L;
Mockito.when(mockMessage.getExpiration()).thenReturn(origExpiration);
final long minExpiryDelay = 100_000L;
final long maxExpiryDelay = 300_000L;
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay).setMaxExpiryDelay(maxExpiryDelay));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testMinAndMaxExpiryDelayWhenExpirationSetAbove() {
Message mockMessage = Mockito.mock(Message.class);
final long startTime = System.currentTimeMillis();
long origExpiration = startTime + 400_000L;
Mockito.when(mockMessage.getExpiration()).thenReturn(origExpiration);
final long minExpiryDelay = 100_000L;
final long maxExpiryDelay = 300_000L;
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay).setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + maxExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testMinAndMaxExpiryDelayWhenExpirationSetBelow() {
Message mockMessage = Mockito.mock(Message.class);
final long startTime = System.currentTimeMillis();
long origExpiration = startTime + 50_000;
Mockito.when(mockMessage.getExpiration()).thenReturn(origExpiration);
final long minExpiryDelay = 100_000L;
final long maxExpiryDelay = 300_000L;
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setMinExpiryDelay(minExpiryDelay).setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + minExpiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
private void assertExpirationSetAsExpected(final long expectedExpirationLow, final long expectedExpirationHigh, final Long actualExpirationSet) {
assertNotNull(actualExpirationSet);
assertTrue(actualExpirationSet >= expectedExpirationLow, () -> "Expected set expiration of at least " + expectedExpirationLow + ", but was: " + actualExpirationSet);
assertTrue(actualExpirationSet < expectedExpirationHigh, "Expected set expiration less than " + expectedExpirationHigh + ", but was: " + actualExpirationSet);
}
@Test
public void testPrecedencNoExpiryOverExpiryDelay() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true).setExpiryDelay(10L));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testPrecedencNoExpiryOverMaxExpiryDelay() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true).setMaxExpiryDelay(10L));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testPrecedencNoExpiryOverMinExpiryDelay() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setNoExpiry(true).setMinExpiryDelay(10L));
Mockito.verify(mockMessage, Mockito.never()).setExpiration(Mockito.anyLong());
}
@Test
public void testPrecedencExpiryDelayOverMaxExpiryDelay() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
final long expiryDelay = 1000L;
final long maxExpiryDelay = 999999999L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setExpiryDelay(expiryDelay).setMaxExpiryDelay(maxExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + expiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
@Test
public void testPrecedencExpiryDelayOverMinExpiryDelay() {
Message mockMessage = Mockito.mock(Message.class);
Mockito.when(mockMessage.getExpiration()).thenReturn(0L);
final long expiryDelay = 1000L;
final long minExpiryDelay = 999999999L;
final long startTime = System.currentTimeMillis();
PostOfficeImpl.applyExpiryDelay(mockMessage, new AddressSettings().setExpiryDelay(expiryDelay).setMinExpiryDelay(minExpiryDelay));
final ArgumentCaptor<Long> captor = ArgumentCaptor.forClass(Long.class);
Mockito.verify(mockMessage).setExpiration(captor.capture());
final long expectedExpirationLow = startTime + expiryDelay;
final long expectedExpirationHigh = expectedExpirationLow + EXPIRATION_DELTA; // Allowing a delta
final Long actualExpirationSet = captor.getValue();
assertExpirationSetAsExpected(expectedExpirationLow, expectedExpirationHigh, actualExpirationSet);
}
}

View File

@ -18,6 +18,7 @@ package org.apache.activemq.artemis.core.settings;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
@ -60,6 +61,7 @@ public class AddressSettingsTest extends ServerTestBase {
assertEquals(AddressSettings.DEFAULT_AUTO_DELETE_ADDRESSES, addressSettings.isAutoDeleteAddresses()); assertEquals(AddressSettings.DEFAULT_AUTO_DELETE_ADDRESSES, addressSettings.isAutoDeleteAddresses());
assertEquals(ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), addressSettings.isDefaultPurgeOnNoConsumers()); assertEquals(ActiveMQDefaultConfiguration.getDefaultPurgeOnNoConsumers(), addressSettings.isDefaultPurgeOnNoConsumers());
assertEquals(Integer.valueOf(ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()), addressSettings.getDefaultMaxConsumers()); assertEquals(Integer.valueOf(ActiveMQDefaultConfiguration.getDefaultMaxQueueConsumers()), addressSettings.getDefaultMaxConsumers());
assertEquals(AddressSettings.DEFAULT_NO_EXPIRY, addressSettings.isNoExpiry());
} }
@Test @Test
@ -93,6 +95,7 @@ public class AddressSettingsTest extends ServerTestBase {
addressSettingsToMerge.setMaxExpiryDelay(777L); addressSettingsToMerge.setMaxExpiryDelay(777L);
addressSettingsToMerge.setIDCacheSize(5); addressSettingsToMerge.setIDCacheSize(5);
addressSettingsToMerge.setInitialQueueBufferSize(256); addressSettingsToMerge.setInitialQueueBufferSize(256);
addressSettingsToMerge.setNoExpiry(true);
if (copy) { if (copy) {
addressSettings = addressSettings.mergeCopy(addressSettingsToMerge); addressSettings = addressSettings.mergeCopy(addressSettingsToMerge);
@ -115,6 +118,7 @@ public class AddressSettingsTest extends ServerTestBase {
assertEquals(Long.valueOf(777), addressSettings.getMaxExpiryDelay()); assertEquals(Long.valueOf(777), addressSettings.getMaxExpiryDelay());
assertEquals(Integer.valueOf(5), addressSettings.getIDCacheSize()); assertEquals(Integer.valueOf(5), addressSettings.getIDCacheSize());
assertEquals(Integer.valueOf(256), addressSettings.getInitialQueueBufferSize()); assertEquals(Integer.valueOf(256), addressSettings.getInitialQueueBufferSize());
assertTrue(addressSettings.isNoExpiry());
} }
@Test @Test
@ -139,6 +143,7 @@ public class AddressSettingsTest extends ServerTestBase {
addressSettingsToMerge.setMessageCounterHistoryDayLimit(1002); addressSettingsToMerge.setMessageCounterHistoryDayLimit(1002);
addressSettingsToMerge.setAddressFullMessagePolicy(AddressFullMessagePolicy.DROP); addressSettingsToMerge.setAddressFullMessagePolicy(AddressFullMessagePolicy.DROP);
addressSettingsToMerge.setMaxSizeBytesRejectThreshold(10 * 1024); addressSettingsToMerge.setMaxSizeBytesRejectThreshold(10 * 1024);
addressSettingsToMerge.setNoExpiry(true);
if (copy) { if (copy) {
addressSettings = addressSettings.mergeCopy(addressSettingsToMerge); addressSettings = addressSettings.mergeCopy(addressSettingsToMerge);
} else { } else {
@ -166,6 +171,7 @@ public class AddressSettingsTest extends ServerTestBase {
assertEquals(addressSettings.getRedeliveryMultiplier(), 2.5, 0.000001); assertEquals(addressSettings.getRedeliveryMultiplier(), 2.5, 0.000001);
assertEquals(AddressFullMessagePolicy.DROP, addressSettings.getAddressFullMessagePolicy()); assertEquals(AddressFullMessagePolicy.DROP, addressSettings.getAddressFullMessagePolicy());
assertEquals(addressSettings.getMaxSizeBytesRejectThreshold(), 10 * 1024); assertEquals(addressSettings.getMaxSizeBytesRejectThreshold(), 10 * 1024);
assertTrue(addressSettings.isNoExpiry());
} }
@Test @Test
@ -236,6 +242,7 @@ public class AddressSettingsTest extends ServerTestBase {
addressSettings.setRedeliveryDelay(1003); addressSettings.setRedeliveryDelay(1003);
addressSettings.setRedeliveryMultiplier(1.0); addressSettings.setRedeliveryMultiplier(1.0);
addressSettings.setAddressFullMessagePolicy(AddressFullMessagePolicy.DROP); addressSettings.setAddressFullMessagePolicy(AddressFullMessagePolicy.DROP);
addressSettings.setNoExpiry(true);
String json = addressSettings.toJSON(); String json = addressSettings.toJSON();
logger.info("Json:: {}", json); logger.info("Json:: {}", json);

View File

@ -577,6 +577,7 @@
<expiry-delay>1</expiry-delay> <expiry-delay>1</expiry-delay>
<min-expiry-delay>2</min-expiry-delay> <min-expiry-delay>2</min-expiry-delay>
<max-expiry-delay>3</max-expiry-delay> <max-expiry-delay>3</max-expiry-delay>
<no-expiry>true</no-expiry>
<redelivery-delay>1</redelivery-delay> <redelivery-delay>1</redelivery-delay>
<redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor> <redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor>
<max-size-bytes>817M</max-size-bytes> <max-size-bytes>817M</max-size-bytes>

View File

@ -21,6 +21,7 @@
<expiry-delay>1</expiry-delay> <expiry-delay>1</expiry-delay>
<min-expiry-delay>2</min-expiry-delay> <min-expiry-delay>2</min-expiry-delay>
<max-expiry-delay>3</max-expiry-delay> <max-expiry-delay>3</max-expiry-delay>
<no-expiry>true</no-expiry>
<redelivery-delay>1</redelivery-delay> <redelivery-delay>1</redelivery-delay>
<redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor> <redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor>
<max-size-bytes>817M</max-size-bytes> <max-size-bytes>817M</max-size-bytes>

View File

@ -21,6 +21,7 @@
<expiry-delay>1</expiry-delay> <expiry-delay>1</expiry-delay>
<min-expiry-delay>2</min-expiry-delay> <min-expiry-delay>2</min-expiry-delay>
<max-expiry-delay>3</max-expiry-delay> <max-expiry-delay>3</max-expiry-delay>
<no-expiry>true</no-expiry>
<redelivery-delay>1</redelivery-delay> <redelivery-delay>1</redelivery-delay>
<redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor> <redelivery-collision-avoidance-factor>0.5</redelivery-collision-avoidance-factor>
<max-size-bytes>817M</max-size-bytes> <max-size-bytes>817M</max-size-bytes>

View File

@ -31,7 +31,10 @@ Here an example of an `address-setting` entry that might be found in the `broker
<auto-create-expiry-resources>false</auto-create-expiry-resources> <auto-create-expiry-resources>false</auto-create-expiry-resources>
<expiry-queue-prefix></expiry-queue-prefix> <expiry-queue-prefix></expiry-queue-prefix>
<expiry-queue-suffix></expiry-queue-suffix> <expiry-queue-suffix></expiry-queue-suffix>
<expiry-delay>123</expiry-delay> <no-expiry>false</no-expiry>
<expiry-delay>-1</expiry-delay>
<min-expiry-delay>-1</min-expiry-delay>
<max-expiry-delay>-1</max-expiry-delay>
<redelivery-delay>5000</redelivery-delay> <redelivery-delay>5000</redelivery-delay>
<redelivery-delay-multiplier>1.0</redelivery-delay-multiplier> <redelivery-delay-multiplier>1.0</redelivery-delay-multiplier>
<redelivery-collision-avoidance-factor>0.0</redelivery-collision-avoidance-factor> <redelivery-collision-avoidance-factor>0.0</redelivery-collision-avoidance-factor>
@ -123,12 +126,24 @@ The suffix used for automatically created expiry queues.
Default is empty. Default is empty.
Read more in the chapter about xref:message-expiry.adoc#message-expiry[message expiry]. Read more in the chapter about xref:message-expiry.adoc#message-expiry[message expiry].
no-expiry::
If `true` this overrides the expiration time for _all_ messages so that they never expire.
The default is `false`.
Read more about xref:message-expiry.adoc#configuring-expiry-delay[message expiry].
expiry-delay:: expiry-delay::
The expiration time that will be used for messages which are using the default expiration time (i.e. 0). The expiration time that will be used for messages which are using the default expiration time (i.e. `0`).
For example, if `expiry-delay` is set to "10" and a message which is using the default expiration time (i.e. 0) arrives then its expiration time of "0" will be changed to "10." However, if a message which is using an expiration time of "20" arrives then its expiration time will remain unchanged. For example, if `expiry-delay` is set to `10` and a message which is using the default expiration time (i.e. `0`) arrives then its expiration time of `0` will be changed to `10`.
Setting `expiry-delay` to "-1" will disable this feature. However, if a message which is using an expiration time of `20` arrives then its expiration time will remain unchanged.
The default is "-1". Setting `expiry-delay` to `-1` will disable this feature.
Read more about xref:message-expiry.adoc#configuring-expiry-addresses[message expiry]. The default is `-1`.
Read more about xref:message-expiry.adoc#configuring-expiry-delay[message expiry].
min-expiry-delay::
max-expiry-delay::
These are applied if the aforementioned `expiry-delay` isn't set.
Unlike `expiry-delay`, they can impact the expiration of a message even if that message is using a non-default expiration time.
There are a xref:message-expiry.adoc#configuring-expiry-delay[handful of rules] which dictate the behavior of these settings.
max-delivery-attempts:: max-delivery-attempts::
defines how many time a cancelled message can be redelivered before sending to the `dead-letter-address`. defines how many time a cancelled message can be redelivered before sending to the `dead-letter-address`.

View File

@ -36,7 +36,40 @@ a Long property containing the _actual expiration time_ of the expired message
== Configuring Expiry Delay == Configuring Expiry Delay
Default expiry delay can be configured in the address-setting configuration: There are multiple address-settings which you can use to modify the expiry delay for incoming messages:
. `no-expiry`
. `expiry-delay`
. `max-expiry-delay` & `min-expiry-delay`
These settings are applied exclusively in this order of precedence. For example, if `no-expiry` is set and `expiry-delay` is also set then `expiry-delay` is ignored completely and `no-expiry` is enforced.
[WARNING]
====
If you set any of these values for the `expiry-address` then messages which expire will have corresponding new expiry delays potentially causing the expired messages to themselves expire and be removed completely from the broker.
====
Let's look at each of these in turn.
=== Never Expire
If you want to force messages to _never_ expire regardless of their existing settings then set `no-expiry` to `true`, e.g.:
[,xml]
----
<!-- messages will never expire -->
<address-setting match="exampleQueue">
<no-expiry>true</no-expiry>
</address-setting>
----
For example, if `no-expiry` is set to `true` and a message which is using an expiration of `10` arrives then its expiration time of `10` will be changed to `0`.
The default is `false`.
=== Modify Default Expiry
To modify the expiry delay on a message using the _default expiration_ (i.e. `0`) set `expiry-delay`, e.g.
[,xml] [,xml]
---- ----
@ -47,14 +80,14 @@ Default expiry delay can be configured in the address-setting configuration:
</address-setting> </address-setting>
---- ----
`expiry-delay` defines the expiration time in milliseconds that will be used for messages which are using the default expiration time (i.e. 0). For example, if `expiry-delay` is set to `10` and a message which is using the default expiration time (i.e. `0`) arrives then its expiration time of `0` will be changed to `10`.
However, if a message which is using an expiration time of `20` arrives then its expiration time will remain unchanged.
For example, if `expiry-delay` is set to "10" and a message which is using the default expiration time (i.e. 10) arrives then its expiration time of "0" will be changed to "10." However, if a message which is using an expiration time of "20" arrives then its expiration time will remain unchanged. This value is measured in milliseconds. The default is `-1` (i.e. disabled).
Setting `expiry-delay` to "-1" will disable this feature.
The default is `-1`. === Enforce an Expiry Range
If `expiry-delay` is _not set_ then minimum and maximum expiry delay values can be configured in the address-setting configuration. To enforce a range of expiry delay values
[,xml] [,xml]
---- ----
@ -67,20 +100,17 @@ If `expiry-delay` is _not set_ then minimum and maximum expiry delay values can
Semantics are as follows: Semantics are as follows:
* Messages _without_ an expiration will be set to `max-expiry-delay`. * Messages _without_ an expiration will be set to `max-expiry-delay`.
If `max-expiry-delay` is not defined then the message will be set to `min-expiry-delay`. ** If `max-expiry-delay` is not defined then the message will be set to `min-expiry-delay`.
If `min-expiry-delay` is not defined then the message will not be changed. ** If `min-expiry-delay` is not defined then the message will not be changed.
* Messages with an expiration _above_ `max-expiry-delay` will be set to `max-expiry-delay` * Messages with an expiration _above_ `max-expiry-delay` will be set to `max-expiry-delay`.
* Messages with an expiration _below_ `min-expiry-delay` will be set to `min-expiry-delay` * Messages with an expiration _below_ `min-expiry-delay` will be set to `min-expiry-delay`.
* Messages with an expiration _within_ `min-expiry-delay` and `max-expiry-delay` range will not be changed * Messages with an expiration _within_ `min-expiry-delay` and `max-expiry-delay` range will not be changed.
* Any value set for `expiry-delay` other than the default (i.e. `-1`) will override the aforementioned min/max settings.
The default for both `min-expiry-delay` and `max-expiry-delay` is `-1` (i.e. disabled). These values are measured in milliseconds. The default for both is `-1` (i.e. disabled).
[WARNING] [WARNING]
==== ====
**If you set expiry-delay, or min/max-expiry-delay, on the expiration target address beware of the following:** Setting a value of `0` for `max-expiry-delay` will cause messages to expire _immediately_.
* Messages will get a new expiration when moved to the expiry queue, rather than being set to 0 as usual, and so may disappear after the new expiration.
==== ====
== Configuring Expiry Addresses == Configuring Expiry Addresses

View File

@ -0,0 +1,275 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.jms.multiprotocol;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import java.lang.invoke.MethodHandles;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JMSMessageExpiryTest extends MultiprotocolJMSClientTestSupport {
protected static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final long EXPIRY_DELAY = 10_000_000L;
@Test
@Timeout(30)
public void testCoreMessageExpiryDelay() throws Exception {
testExpiry(CoreConnection, DelayType.NORMAL, false);
}
@Test
@Timeout(30)
public void testAmqpMessageExpiryDelay() throws Exception {
testExpiry(AMQPConnection, DelayType.NORMAL, false);
}
@Test
@Timeout(30)
public void testOpenWireMessageExpiryDelay() throws Exception {
testExpiry(OpenWireConnection, DelayType.NORMAL, false);
}
@Test
@Timeout(30)
public void testCoreLargeMessageExpiryDelay() throws Exception {
testExpiry(CoreConnection, DelayType.NORMAL, false, true, false);
}
@Test
@Timeout(30)
public void testAmqpLargeMessageExpiryDelay() throws Exception {
testExpiry(AMQPConnection, DelayType.NORMAL, false, true, false);
}
@Test
@Timeout(30)
public void testOpenWireLargeMessageExpiryDelay() throws Exception {
testExpiry(OpenWireConnection, DelayType.NORMAL, false, true, false);
}
@Test
@Timeout(30)
public void testCoreLargeMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(CoreConnection, DelayType.NORMAL, false, true, true);
}
@Test
@Timeout(30)
public void testAmqpLargeMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(AMQPConnection, DelayType.NORMAL, false, true, true);
}
@Test
@Timeout(30)
public void testOpenWireLargeMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(OpenWireConnection, DelayType.NORMAL, false, true, true);
}
@Test
@Timeout(30)
public void testCoreMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(CoreConnection, DelayType.NORMAL, false, false, true);
}
@Test
@Timeout(30)
public void testAmqpMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(AMQPConnection, DelayType.NORMAL, false, false, true);
}
@Test
@Timeout(30)
public void testOpenWireMessageExpiryDelayWithBrokerRestart() throws Exception {
testExpiry(OpenWireConnection, DelayType.NORMAL, false, false, true);
}
@Test
@Timeout(30)
public void testCoreMaxExpiryDelayNoExpiration() throws Exception {
testExpiry(CoreConnection, DelayType.MAX, false);
}
@Test
@Timeout(30)
public void testAmqpMaxExpiryDelayNoExpiration() throws Exception {
testExpiry(AMQPConnection, DelayType.MAX, false);
}
@Test
@Timeout(30)
public void testOpenWireMaxExpiryDelayNoExpiration() throws Exception {
testExpiry(OpenWireConnection, DelayType.MAX, false);
}
@Test
@Timeout(30)
public void testCoreMinExpiryDelayNoExpiration() throws Exception {
testExpiry(CoreConnection, DelayType.MIN, false);
}
@Test
@Timeout(30)
public void testAmqpMinExpiryDelayNoExpiration() throws Exception {
testExpiry(AMQPConnection, DelayType.MIN, false);
}
@Test
@Timeout(30)
public void testOpenWireMinExpiryDelayNoExpiration() throws Exception {
testExpiry(OpenWireConnection, DelayType.MIN, false);
}
@Test
@Timeout(30)
public void testCoreMaxExpiryDelayWithExpiration() throws Exception {
testExpiry(CoreConnection, DelayType.MAX, true);
}
@Test
@Timeout(30)
public void testAmqpMaxExpiryDelayWithExpiration() throws Exception {
testExpiry(AMQPConnection, DelayType.MAX, true);
}
@Test
@Timeout(30)
public void testOpenWireMaxExpiryDelayWithExpiration() throws Exception {
testExpiry(OpenWireConnection, DelayType.MAX, true);
}
@Test
@Timeout(30)
public void testCoreMinExpiryDelayWithExpiration() throws Exception {
testExpiry(CoreConnection, DelayType.MIN, true);
}
@Test
@Timeout(30)
public void testAmqpMinExpiryDelayWithExpiration() throws Exception {
testExpiry(AMQPConnection, DelayType.MIN, true);
}
@Test
@Timeout(30)
public void testOpenWireMinExpiryDelayWithExpiration() throws Exception {
testExpiry(OpenWireConnection, DelayType.MIN, true);
}
@Test
@Timeout(30)
public void testCoreMessageNoExpiry() throws Exception {
testExpiry(CoreConnection, DelayType.NEVER, true);
}
@Test
@Timeout(30)
public void testAmqpMessageNoExpiry() throws Exception {
testExpiry(AMQPConnection, DelayType.NEVER, true);
}
@Test
@Timeout(30)
public void testOpenWireMessageNoExpiry() throws Exception {
testExpiry(OpenWireConnection, DelayType.NEVER, true);
}
private void testExpiry(ConnectionSupplier supplier, DelayType delayType, boolean setTimeToLive) throws Exception {
testExpiry(supplier, delayType, setTimeToLive, false, false);
}
private void testExpiry(ConnectionSupplier supplier, DelayType delayType, boolean setTimeToLive, boolean useLargeMessage, boolean restartBroker) throws Exception {
AddressSettings addressSettings = new AddressSettings();
if (delayType == DelayType.NORMAL) {
addressSettings.setExpiryDelay(EXPIRY_DELAY);
} else if (delayType == DelayType.MIN) {
addressSettings.setMinExpiryDelay(EXPIRY_DELAY);
} else if (delayType == DelayType.MAX) {
addressSettings.setMaxExpiryDelay(EXPIRY_DELAY);
} else if (delayType == DelayType.NEVER) {
addressSettings.setNoExpiry(true);
}
server.getAddressSettingsRepository().addMatch(getQueueName(), addressSettings);
Connection producerConnection = supplier.createConnection();
Session session = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = session.createQueue(getQueueName());
MessageProducer producer = session.createProducer(q);
if (setTimeToLive) {
if (delayType == DelayType.MIN) {
producer.setTimeToLive(EXPIRY_DELAY / 2);
} else if (delayType == DelayType.MAX) {
producer.setTimeToLive(EXPIRY_DELAY * 2);
} else if (delayType == DelayType.NEVER) {
producer.setTimeToLive(EXPIRY_DELAY);
}
}
BytesMessage m = session.createBytesMessage();
if (useLargeMessage) {
m.writeBytes(RandomUtil.randomBytes(server.getConfiguration().getJournalBufferSize_NIO() * 2));
}
long start = System.currentTimeMillis();
producer.send(m);
producerConnection.close();
if (useLargeMessage) {
validateNoFilesOnLargeDir(getLargeMessagesDir(), 1);
}
if (restartBroker) {
server.stop();
server.start();
}
Connection consumerConnection = supplier.createConnection();
session = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
q = session.createQueue(getQueueName());
MessageConsumer consumer = session.createConsumer(q);
consumerConnection.start();
m = (BytesMessage) consumer.receive(1500);
long stop = System.currentTimeMillis();
assertNotNull(m);
consumerConnection.close();
if (delayType == DelayType.NEVER) {
assertEquals(0, m.getJMSExpiration());
} else {
long duration = stop - start;
long delayOnMessage = m.getJMSExpiration() - stop;
assertTrue(delayOnMessage >= (EXPIRY_DELAY - duration));
assertTrue(delayOnMessage <= EXPIRY_DELAY);
}
if (useLargeMessage) {
validateNoFilesOnLargeDir();
}
}
enum DelayType {
NORMAL, MIN, MAX, NEVER;
}
}