Switching to a List to track dispatched messages in a TopicSubscription
to be consistent with a PrefetchSubscription and to prevent an error
in case acks come back out of order.
This commit is contained in:
Christopher L. Shannon (cshannon) 2015-08-06 17:12:18 +00:00
parent 9827427f46
commit 2b7bb6f81b
1 changed files with 42 additions and 39 deletions

View File

@ -17,11 +17,9 @@
package org.apache.activemq.broker.region; package org.apache.activemq.broker.region;
import java.io.IOException; import java.io.IOException;
import java.util.Comparator; import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.NavigableMap; import java.util.List;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -76,19 +74,9 @@ public class TopicSubscription extends AbstractSubscription {
protected boolean active = false; protected boolean active = false;
protected boolean discarding = false; protected boolean discarding = false;
//Used for inflight message size calculations
/** protected final Object dispatchLock = new Object();
* This Map is used to keep track of messages that have been dispatched in sorted order to protected final List<MessageReference> dispatched = new ArrayList<MessageReference>();
* optimize message acknowledgement
*/
private NavigableMap<MessageId, MessageReference> dispatched = new ConcurrentSkipListMap<>(
new Comparator<MessageId>() {
@Override
public int compare(MessageId m1, MessageId m2) {
return m1 == null ? (m2 == null ? 0 : -1) : (m2 == null ? 1
: Long.compare(m1.getBrokerSequenceId(), m2.getBrokerSequenceId()));
}
});
public TopicSubscription(Broker broker,ConnectionContext context, ConsumerInfo info, SystemUsage usageManager) throws Exception { public TopicSubscription(Broker broker,ConnectionContext context, ConsumerInfo info, SystemUsage usageManager) throws Exception {
super(broker, context, info); super(broker, context, info);
@ -267,11 +255,13 @@ public class TopicSubscription extends AbstractSubscription {
MessageReference node = matched.next(); MessageReference node = matched.next();
node.decrementReferenceCount(); node.decrementReferenceCount();
if (node.getMessageId().equals(mdn.getMessageId())) { if (node.getMessageId().equals(mdn.getMessageId())) {
synchronized(dispatchLock) {
matched.remove(); matched.remove();
getSubscriptionStatistics().getDispatched().increment(); getSubscriptionStatistics().getDispatched().increment();
dispatched.put(node.getMessageId(), node); dispatched.add(node);
getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize()); getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize());
node.decrementReferenceCount(); node.decrementReferenceCount();
}
break; break;
} }
} }
@ -403,23 +393,31 @@ public class TopicSubscription extends AbstractSubscription {
} }
/** /**
* Update the inflight statistics on message ack. Since a message ack could be a range, * Update the inflight statistics on message ack.
* we need to grab a subtree of the dispatched map to acknowledge messages. Finding the
* subMap is an O(log n) operation.
* @param ack * @param ack
*/ */
private void updateInflightMessageSizeOnAck(final MessageAck ack) { private void updateInflightMessageSizeOnAck(final MessageAck ack) {
if (ack.getFirstMessageId() != null) { synchronized(dispatchLock) {
NavigableMap<MessageId, MessageReference> acked = dispatched boolean inAckRange = false;
.subMap(ack.getFirstMessageId(), true, ack.getLastMessageId(), true); List<MessageReference> removeList = new ArrayList<MessageReference>();
Iterator<MessageId> i = acked.keySet().iterator(); for (final MessageReference node : dispatched) {
while (i.hasNext()) { MessageId messageId = node.getMessageId();
getSubscriptionStatistics().getInflightMessageSize().addSize(-acked.get(i.next()).getSize()); if (ack.getFirstMessageId() == null
i.remove(); || ack.getFirstMessageId().equals(messageId)) {
inAckRange = true;
}
if (inAckRange) {
removeList.add(node);
if (ack.getLastMessageId().equals(messageId)) {
break;
}
}
}
for (final MessageReference node : removeList) {
dispatched.remove(node);
getSubscriptionStatistics().getInflightMessageSize().addSize(-node.getSize());
} }
} else {
getSubscriptionStatistics().getInflightMessageSize().addSize(-dispatched.get(ack.getLastMessageId()).getSize());
dispatched.remove(ack.getLastMessageId());
} }
} }
@ -645,9 +643,12 @@ public class TopicSubscription extends AbstractSubscription {
md.setConsumerId(info.getConsumerId()); md.setConsumerId(info.getConsumerId());
if (node != null) { if (node != null) {
md.setDestination(((Destination)node.getRegionDestination()).getActiveMQDestination()); md.setDestination(((Destination)node.getRegionDestination()).getActiveMQDestination());
synchronized(dispatchLock) {
getSubscriptionStatistics().getDispatched().increment(); getSubscriptionStatistics().getDispatched().increment();
dispatched.put(node.getMessageId(), node); dispatched.add(node);
getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize()); getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize());
}
// Keep track if this subscription is receiving messages from a single destination. // Keep track if this subscription is receiving messages from a single destination.
if (singleDestination) { if (singleDestination) {
if (destination == null) { if (destination == null) {
@ -729,8 +730,10 @@ public class TopicSubscription extends AbstractSubscription {
} }
} }
setSlowConsumer(false); setSlowConsumer(false);
synchronized(dispatchLock) {
dispatched.clear(); dispatched.clear();
} }
}
@Override @Override
public int getPrefetchSize() { public int getPrefetchSize() {