ARTEMIS-3067 - track application properties in memory estimate and check for modification after potential filter execution
This commit is contained in:
parent
03b0fcd2c2
commit
d186d20406
|
@ -490,6 +490,7 @@ public abstract class AMQPMessage extends RefCountMessage implements org.apache.
|
||||||
protected ApplicationProperties lazyDecodeApplicationProperties(ReadableBuffer data) {
|
protected ApplicationProperties lazyDecodeApplicationProperties(ReadableBuffer data) {
|
||||||
if (applicationProperties == null && applicationPropertiesPosition != VALUE_NOT_PRESENT) {
|
if (applicationProperties == null && applicationPropertiesPosition != VALUE_NOT_PRESENT) {
|
||||||
applicationProperties = scanForMessageSection(data, applicationPropertiesPosition, ApplicationProperties.class);
|
applicationProperties = scanForMessageSection(data, applicationPropertiesPosition, ApplicationProperties.class);
|
||||||
|
memoryEstimate = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return applicationProperties;
|
return applicationProperties;
|
||||||
|
|
|
@ -124,12 +124,24 @@ public class AMQPStandardMessage extends AMQPMessage {
|
||||||
@Override
|
@Override
|
||||||
public int getMemoryEstimate() {
|
public int getMemoryEstimate() {
|
||||||
if (memoryEstimate == -1) {
|
if (memoryEstimate == -1) {
|
||||||
memoryEstimate = memoryOffset + (data != null ? data.capacity() : 0);
|
memoryEstimate = memoryOffset + (data != null ? data.capacity() + unmarshalledApplicationPropertiesMemoryEstimateFromData() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoryEstimate;
|
return memoryEstimate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int unmarshalledApplicationPropertiesMemoryEstimateFromData() {
|
||||||
|
if (applicationProperties != null) {
|
||||||
|
// they have been unmarshalled, estimate memory usage based on their encoded size
|
||||||
|
if (remainingBodyPosition != VALUE_NOT_PRESENT) {
|
||||||
|
return remainingBodyPosition - applicationPropertiesPosition;
|
||||||
|
} else {
|
||||||
|
return data.capacity() - applicationPropertiesPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void persist(ActiveMQBuffer targetRecord) {
|
public void persist(ActiveMQBuffer targetRecord) {
|
||||||
ensureDataIsValid();
|
ensureDataIsValid();
|
||||||
|
|
|
@ -269,6 +269,19 @@ public class AMQPMessageTest {
|
||||||
assertNotEquals(estimate, decoded.getMemoryEstimate());
|
assertNotEquals(estimate, decoded.getMemoryEstimate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetMemoryEstimateWithDecodedApplicationProperties() {
|
||||||
|
AMQPStandardMessage decoded = new AMQPStandardMessage(0, encodedProtonMessage, new TypedProperties(), null);
|
||||||
|
|
||||||
|
AMQPStandardMessage decodedWithApplicationPropertiesUnmarshalled =
|
||||||
|
new AMQPStandardMessage(0, encodeMessage(createProtonMessage()), new TypedProperties(), null);
|
||||||
|
|
||||||
|
assertEquals(decodedWithApplicationPropertiesUnmarshalled.getStringProperty(TEST_APPLICATION_PROPERTY_KEY), TEST_APPLICATION_PROPERTY_VALUE);
|
||||||
|
|
||||||
|
assertNotEquals(decodedWithApplicationPropertiesUnmarshalled.getMemoryEstimate(), decoded.getMemoryEstimate());
|
||||||
|
}
|
||||||
|
|
||||||
//----- Test Connection ID access -----------------------------------------//
|
//----- Test Connection ID access -----------------------------------------//
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2935,6 +2935,9 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
|
|
||||||
MessageReference ref;
|
MessageReference ref;
|
||||||
|
|
||||||
|
// filter evaluation or transformation may cause properties to be lazyDecoded, we need to reflect that
|
||||||
|
int existingMemoryEstimate = 0;
|
||||||
|
|
||||||
Consumer handledconsumer = null;
|
Consumer handledconsumer = null;
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
|
@ -2970,6 +2973,7 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
ref = holder.iter.next();
|
ref = holder.iter.next();
|
||||||
} else {
|
} else {
|
||||||
ref = null;
|
ref = null;
|
||||||
|
existingMemoryEstimate = 0;
|
||||||
}
|
}
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
noDelivery++;
|
noDelivery++;
|
||||||
|
@ -2988,6 +2992,7 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
logger.trace("Queue " + this.getName() + " is delivering reference " + ref);
|
logger.trace("Queue " + this.getName() + " is delivering reference " + ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
existingMemoryEstimate = ref.getMessageMemoryEstimate();
|
||||||
final SimpleString groupID = extractGroupID(ref);
|
final SimpleString groupID = extractGroupID(ref);
|
||||||
groupConsumer = getGroupConsumer(groupID);
|
groupConsumer = getGroupConsumer(groupID);
|
||||||
|
|
||||||
|
@ -3062,6 +3067,10 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
if (handledconsumer != null) {
|
if (handledconsumer != null) {
|
||||||
proceedDeliver(handledconsumer, ref);
|
proceedDeliver(handledconsumer, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (existingMemoryEstimate > 0 ) {
|
||||||
|
accountForChangeInMemoryEstimate(ref, existingMemoryEstimate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -3685,8 +3694,13 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
consumer = groupConsumer;
|
consumer = groupConsumer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filter evaluation may cause properties to be lazyDecoded
|
||||||
|
final int existingMemoryEstimate = ref.getMessageMemoryEstimate();
|
||||||
|
|
||||||
HandleStatus status = handle(ref, consumer);
|
HandleStatus status = handle(ref, consumer);
|
||||||
|
|
||||||
|
accountForChangeInMemoryEstimate(ref, existingMemoryEstimate);
|
||||||
|
|
||||||
if (status == HandleStatus.HANDLED) {
|
if (status == HandleStatus.HANDLED) {
|
||||||
final MessageReference reference;
|
final MessageReference reference;
|
||||||
if (redistributor == null) {
|
if (redistributor == null) {
|
||||||
|
@ -3716,6 +3730,16 @@ public class QueueImpl extends CriticalComponentImpl implements Queue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void accountForChangeInMemoryEstimate(final MessageReference ref, final int existingMemoryEstimate) {
|
||||||
|
final int delta = ref.getMessageMemoryEstimate() - existingMemoryEstimate;
|
||||||
|
if (delta > 0) {
|
||||||
|
PagingStore pageStore = ref.getOwner();
|
||||||
|
if (pageStore != null) {
|
||||||
|
pageStore.addSize(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Consumer getGroupConsumer(SimpleString groupID) {
|
private Consumer getGroupConsumer(SimpleString groupID) {
|
||||||
Consumer groupConsumer = null;
|
Consumer groupConsumer = null;
|
||||||
if (exclusive) {
|
if (exclusive) {
|
||||||
|
|
|
@ -118,4 +118,86 @@ public class AmqpPagingTest extends AmqpClientTestSupport {
|
||||||
connection.close();
|
connection.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test(timeout = 60000)
|
||||||
|
public void testSizeCalculationsForApplicationProperties() throws Exception {
|
||||||
|
final int MSG_SIZE = 1000;
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
for (int i = 0; i < MSG_SIZE; i++) {
|
||||||
|
builder.append('0');
|
||||||
|
}
|
||||||
|
final String data = builder.toString();
|
||||||
|
final int MSG_COUNT = 1;
|
||||||
|
|
||||||
|
AmqpClient client = createAmqpClient();
|
||||||
|
AmqpConnection connection = addConnection(client.connect());
|
||||||
|
AmqpSession session = connection.createSession();
|
||||||
|
|
||||||
|
AmqpSender sender = session.createSender(getQueueName(), true);
|
||||||
|
|
||||||
|
// use selector expression that references a property to force decode of application properties
|
||||||
|
AmqpReceiver receiver = session.createReceiver(getQueueName(), "myData IS NOT NULL");
|
||||||
|
receiver.setPresettle(true);
|
||||||
|
receiver.flow(10);
|
||||||
|
Assert.assertNull("somehow the queue had messages from a previous test", receiver.receiveNoWait());
|
||||||
|
receiver.flow(0);
|
||||||
|
|
||||||
|
AmqpMessage message = new AmqpMessage();
|
||||||
|
message.setText(data);
|
||||||
|
|
||||||
|
// large message property also
|
||||||
|
message.setApplicationProperty("myData", data);
|
||||||
|
|
||||||
|
if (durable != null) {
|
||||||
|
message.setDurable(durable);
|
||||||
|
}
|
||||||
|
sender.send(message);
|
||||||
|
|
||||||
|
PagingStore pagingStore = server.getPagingManager().getPageStore(SimpleString.toSimpleString(getQueueName()));
|
||||||
|
|
||||||
|
// verify page usage reflects data + 2*application properties (encoded and decoded)
|
||||||
|
assertTrue(Wait.waitFor(() -> {
|
||||||
|
return pagingStore.getAddressSize() > 3000;
|
||||||
|
}));
|
||||||
|
|
||||||
|
receiver.flow(MSG_COUNT);
|
||||||
|
AmqpMessage receive = receiver.receive(10, TimeUnit.MINUTES);
|
||||||
|
assertNotNull("Not received anything after receive", receive);
|
||||||
|
receive.accept();
|
||||||
|
|
||||||
|
assertTrue(Wait.waitFor(() -> {
|
||||||
|
return pagingStore.getAddressSize() == 0;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// send another with duplicate id property, to force early decode
|
||||||
|
message = new AmqpMessage();
|
||||||
|
message.setText(data);
|
||||||
|
|
||||||
|
// ensures application properties are referenced
|
||||||
|
message.setApplicationProperty("_AMQ_DUPL_ID", "1");
|
||||||
|
|
||||||
|
// large message property also
|
||||||
|
message.setApplicationProperty("myData", data);
|
||||||
|
|
||||||
|
if (durable != null) {
|
||||||
|
message.setDurable(durable);
|
||||||
|
}
|
||||||
|
sender.send(message);
|
||||||
|
|
||||||
|
sender.close();
|
||||||
|
|
||||||
|
// verify page usage reflects data + 2*application properties (encoded and decoded)
|
||||||
|
assertTrue(Wait.waitFor(() -> {
|
||||||
|
return pagingStore.getAddressSize() > 3000;
|
||||||
|
}));
|
||||||
|
|
||||||
|
receiver.flow(MSG_COUNT);
|
||||||
|
receive = receiver.receive(10, TimeUnit.MINUTES);
|
||||||
|
assertNotNull("Not received anything after receive", receive);
|
||||||
|
receive.accept();
|
||||||
|
|
||||||
|
receiver.close();
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue