ARTEMIS-2082 Reset buffer valid flag after re-encoding the message

Once a re-encode of the message is done the buffer is not being marked
as valid and so subsequent checks on the buffer are all assuming the
message data is not valid and re-encoding over and over.  This can lead
to poor performance in some cases and corrupted data in others.
This commit is contained in:
Timothy Bish 2018-09-11 15:42:24 -04:00
parent 4507d7783f
commit e065e3e960
3 changed files with 191 additions and 9 deletions

View File

@ -694,7 +694,8 @@ public class AMQPMessage extends RefCountMessage {
getProtonMessage().encode(new NettyWritable(buffer));
byte[] bytes = new byte[buffer.writerIndex()];
buffer.readBytes(bytes);
this.data = ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(bytes));
data = ReadableBuffer.ByteBufferReader.wrap(ByteBuffer.wrap(bytes));
bufferValid = true;
} finally {
buffer.release();
}

View File

@ -16,9 +16,9 @@
*/
package org.apache.activemq.artemis.tests.integration.amqp;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.tests.util.Wait;
import org.apache.activemq.transport.amqp.client.AmqpClient;
@ -339,11 +339,10 @@ public class AmqpExpiredMessageTest extends AmqpClientTestSupport {
message.setDurable(true);
message.setText("Test-Message");
message.setDeliveryAnnotation("shouldDisappear", 1);
message.setAbsoluteExpiryTime(System.currentTimeMillis() + 1000);
message.setAbsoluteExpiryTime(System.currentTimeMillis() + 250);
sender.send(message);
org.apache.activemq.artemis.core.server.Queue dlq = server.locateQueue(SimpleString.toSimpleString(getDeadLetterAddress()));
Queue dlq = getProxyToQueue(getDeadLetterAddress());
assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlq.getMessageCount() > 0, 5000, 500));
connection.close();
@ -361,11 +360,61 @@ public class AmqpExpiredMessageTest extends AmqpClientTestSupport {
receiver.flow(20);
message = receiver.receive(5, TimeUnit.SECONDS);
Assert.assertNotNull(message);
Assert.assertEquals(getQueueName(), message.getMessageAnnotation(org.apache.activemq.artemis.api.core.Message.HDR_ORIGINAL_ADDRESS.toString()));
Assert.assertNull(message.getDeliveryAnnotation("shouldDisappear"));
Assert.assertNull(receiver.receiveNoWait());
assertNotNull(message);
assertEquals(getQueueName(), message.getMessageAnnotation(org.apache.activemq.artemis.api.core.Message.HDR_ORIGINAL_ADDRESS.toString()));
assertNull(message.getDeliveryAnnotation("shouldDisappear"));
assertNull(receiver.receiveNoWait());
} finally {
connection.close();
}
}
@Test(timeout = 60000)
public void testDLQdMessageCanBeRedeliveredMultipleTimes() throws Throwable {
AmqpClient client = createAmqpClient();
AmqpConnection connection = client.connect();
try {
AmqpSession session = connection.createSession();
AmqpSender sender = session.createSender(getQueueName());
AmqpMessage message = new AmqpMessage();
message.setDurable(true);
message.setTimeToLive(250);
message.setText("Test-Message");
message.setMessageId(UUID.randomUUID().toString());
message.setApplicationProperty("key", "value");
sender.send(message);
Queue dlqView = getProxyToQueue(getDeadLetterAddress());
assertTrue("Message not movied to DLQ", Wait.waitFor(() -> dlqView.getMessageCount() > 0, 5000, 200));
// Read and Modify the message for redelivery repeatedly
AmqpReceiver receiver = session.createReceiver(getDeadLetterAddress());
receiver.flow(20);
message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(0, message.getWrappedMessage().getDeliveryCount());
message.modified(true, false);
message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(1, message.getWrappedMessage().getDeliveryCount());
message.modified(true, false);
message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(2, message.getWrappedMessage().getDeliveryCount());
message.modified(true, false);
message = receiver.receive(5, TimeUnit.SECONDS);
assertNotNull(message);
assertEquals(3, message.getWrappedMessage().getDeliveryCount());
} finally {
connection.close();
}

View File

@ -0,0 +1,132 @@
/*
* 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.amqp;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.junit.Wait;
import org.junit.Test;
public class JMSTransactedRedeliveryBugTest extends JMSClientTestSupport {
private static final String INITIAL_QUEUE_NAME = "InitialQueue";
private static final String FINAL_QUEUE_NAME = "FinalQueue";
private static final SimpleString INITIAL_QUEUE_SS = new SimpleString(INITIAL_QUEUE_NAME);
private static final SimpleString FINAL_QUEUE_SS = new SimpleString(FINAL_QUEUE_NAME);
@Override
protected void addConfiguration(ActiveMQServer server) {
server.getAddressSettingsRepository().addMatch(INITIAL_QUEUE_NAME, new AddressSettings().setExpiryAddress(FINAL_QUEUE_SS));
}
@Override
protected void createAddressAndQueues(ActiveMQServer server) throws Exception {
super.createAddressAndQueues(server);
server.addAddressInfo(new AddressInfo(INITIAL_QUEUE_SS, RoutingType.ANYCAST));
server.createQueue(INITIAL_QUEUE_SS, RoutingType.ANYCAST, INITIAL_QUEUE_SS, null, true, false, -1, false, true);
server.addAddressInfo(new AddressInfo(FINAL_QUEUE_SS, RoutingType.ANYCAST));
server.createQueue(FINAL_QUEUE_SS, RoutingType.ANYCAST, FINAL_QUEUE_SS, null, true, false, -1, false, true);
}
@Override
protected String getJmsConnectionURIOptions() {
return "amqp.traceFrames=true";
}
@Test
public void testAMQPProducerAMQPConsumer() throws Exception {
Connection producerConnection = createConnection();
Connection consumerConnection = createConnection();
try {
Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue producerQueue = producerSession.createQueue(INITIAL_QUEUE_NAME);
MessageProducer producer = producerSession.createProducer(producerQueue);
TextMessage sentMessage = producerSession.createTextMessage();
sentMessage.setStringProperty("something", "KEY");
sentMessage.setText("how are you");
producer.send(sentMessage, DeliveryMode.PERSISTENT, 4, 10);
// Simulate a small pause, else both messages could be consumed if
// consumer is fast enough
Wait.assertTrue("Message should have expired", () -> getProxyToQueue(FINAL_QUEUE_NAME).getMessageCount() == 1);
Session consumerSession = consumerConnection.createSession(true, Session.SESSION_TRANSACTED);
Queue consumerQueue = consumerSession.createQueue(FINAL_QUEUE_NAME);
MessageConsumer consumer = consumerSession.createConsumer(consumerQueue);
Message msg = consumer.receive(1_000);
assertNotNull(msg);
assertEquals("1", msg.getStringProperty("JMSXDeliveryCount"));
assertEquals("KEY", msg.getStringProperty("something"));
assertEquals("how are you", ((TextMessage) msg).getText());
consumerSession.rollback();
msg = consumer.receive(1_000);
assertNotNull(msg);
assertEquals("2", msg.getStringProperty("JMSXDeliveryCount"));
assertEquals("KEY", msg.getStringProperty("something"));
assertEquals("how are you", ((TextMessage) msg).getText());
consumerSession.rollback();
msg = consumer.receive(1_000);
assertNotNull(msg);
assertEquals("3", msg.getStringProperty("JMSXDeliveryCount"));
assertEquals("KEY", msg.getStringProperty("something"));
assertEquals("how are you", ((TextMessage) msg).getText());
consumerSession.rollback();
msg = consumer.receive(1_000);
assertNotNull(msg);
assertEquals("4", msg.getStringProperty("JMSXDeliveryCount"));
assertEquals("KEY", msg.getStringProperty("something"));
assertEquals("how are you", ((TextMessage) msg).getText());
consumerSession.rollback();
msg = consumer.receive(1_000);
assertNotNull(msg);
assertEquals("5", msg.getStringProperty("JMSXDeliveryCount"));
assertEquals("KEY", msg.getStringProperty("something"));
assertEquals("how are you", ((TextMessage) msg).getText());
consumerSession.commit();
consumer.close();
} finally {
producerConnection.close();
consumerConnection.close();
}
}
}