mirror of https://github.com/apache/activemq.git
fix regression of https://issues.apache.org/activemq/browse/AMQ-2870 from refactoring of fix for https://issues.apache.org/activemq/browse/AMQ-2985 - unmatched acks are now stored as negative sequence ids so that we can differenciate between a durables that have acked messages after receipt and those that have acked because of unmatched. Unmatched messages are now deleted in the normal way
git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1027644 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
3b51a692be
commit
6fd292d885
|
@ -718,6 +718,17 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// an ack for an unmatched message is stored as a negative sequence id
|
||||||
|
// if sub has been getting unmatched acks, we need to reset
|
||||||
|
protected Long resetForSelectors(SubscriptionInfo info, Long position) {
|
||||||
|
if (info.getSelector() != null) {
|
||||||
|
if (position < NOT_ACKED) {
|
||||||
|
position = NOT_ACKED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMessageCount(String clientId, String subscriptionName) throws IOException {
|
public int getMessageCount(String clientId, String subscriptionName) throws IOException {
|
||||||
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
||||||
final SubscriptionInfo info = lookupSubscription(clientId, subscriptionName);
|
final SubscriptionInfo info = lookupSubscription(clientId, subscriptionName);
|
||||||
|
@ -731,6 +742,7 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
// The subscription might not exist.
|
// The subscription might not exist.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
cursorPos = resetForSelectors(info, cursorPos);
|
||||||
|
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
try {
|
try {
|
||||||
|
@ -740,7 +752,7 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
selectorExpression = SelectorParser.parse(selector);
|
selectorExpression = SelectorParser.parse(selector);
|
||||||
}
|
}
|
||||||
sd.orderIndex.resetCursorPosition();
|
sd.orderIndex.resetCursorPosition();
|
||||||
sd.orderIndex.setBatch(tx, cursorPos);
|
sd.orderIndex.setBatch(tx, extractSequenceId(cursorPos));
|
||||||
for (Iterator<Entry<Long, MessageKeys>> iterator = sd.orderIndex.iterator(tx); iterator
|
for (Iterator<Entry<Long, MessageKeys>> iterator = sd.orderIndex.iterator(tx); iterator
|
||||||
.hasNext();) {
|
.hasNext();) {
|
||||||
Entry<Long, MessageKeys> entry = iterator.next();
|
Entry<Long, MessageKeys> entry = iterator.next();
|
||||||
|
@ -769,13 +781,15 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
public void recoverSubscription(String clientId, String subscriptionName, final MessageRecoveryListener listener)
|
public void recoverSubscription(String clientId, String subscriptionName, final MessageRecoveryListener listener)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
||||||
|
final SubscriptionInfo info = lookupSubscription(clientId, subscriptionName);
|
||||||
indexLock.writeLock().lock();
|
indexLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
pageFile.tx().execute(new Transaction.Closure<Exception>() {
|
pageFile.tx().execute(new Transaction.Closure<Exception>() {
|
||||||
public void execute(Transaction tx) throws Exception {
|
public void execute(Transaction tx) throws Exception {
|
||||||
StoredDestination sd = getStoredDestination(dest, tx);
|
StoredDestination sd = getStoredDestination(dest, tx);
|
||||||
Long cursorPos = sd.subscriptionAcks.get(tx, subscriptionKey);
|
Long cursorPos = sd.subscriptionAcks.get(tx, subscriptionKey);
|
||||||
sd.orderIndex.setBatch(tx, cursorPos);
|
cursorPos = resetForSelectors(info, cursorPos);
|
||||||
|
sd.orderIndex.setBatch(tx, extractSequenceId(cursorPos));
|
||||||
for (Iterator<Entry<Long, MessageKeys>> iterator = sd.orderIndex.iterator(tx); iterator
|
for (Iterator<Entry<Long, MessageKeys>> iterator = sd.orderIndex.iterator(tx); iterator
|
||||||
.hasNext();) {
|
.hasNext();) {
|
||||||
Entry<Long, MessageKeys> entry = iterator.next();
|
Entry<Long, MessageKeys> entry = iterator.next();
|
||||||
|
@ -792,6 +806,7 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
public void recoverNextMessages(String clientId, String subscriptionName, final int maxReturned,
|
public void recoverNextMessages(String clientId, String subscriptionName, final int maxReturned,
|
||||||
final MessageRecoveryListener listener) throws Exception {
|
final MessageRecoveryListener listener) throws Exception {
|
||||||
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
final String subscriptionKey = subscriptionKey(clientId, subscriptionName);
|
||||||
|
final SubscriptionInfo info = lookupSubscription(clientId, subscriptionName);
|
||||||
indexLock.writeLock().lock();
|
indexLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
pageFile.tx().execute(new Transaction.Closure<Exception>() {
|
pageFile.tx().execute(new Transaction.Closure<Exception>() {
|
||||||
|
@ -800,8 +815,9 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
|
||||||
sd.orderIndex.resetCursorPosition();
|
sd.orderIndex.resetCursorPosition();
|
||||||
MessageOrderCursor moc = sd.subscriptionCursors.get(subscriptionKey);
|
MessageOrderCursor moc = sd.subscriptionCursors.get(subscriptionKey);
|
||||||
if (moc == null) {
|
if (moc == null) {
|
||||||
long pos = sd.subscriptionAcks.get(tx, subscriptionKey);
|
Long pos = sd.subscriptionAcks.get(tx, subscriptionKey);
|
||||||
sd.orderIndex.setBatch(tx, pos);
|
pos = resetForSelectors(info, pos);
|
||||||
|
sd.orderIndex.setBatch(tx, extractSequenceId(pos));
|
||||||
moc = sd.orderIndex.cursor;
|
moc = sd.orderIndex.cursor;
|
||||||
} else {
|
} else {
|
||||||
sd.orderIndex.cursor.sync(moc);
|
sd.orderIndex.cursor.sync(moc);
|
||||||
|
|
|
@ -98,6 +98,8 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
static final int CLOSED_STATE = 1;
|
static final int CLOSED_STATE = 1;
|
||||||
static final int OPEN_STATE = 2;
|
static final int OPEN_STATE = 2;
|
||||||
static final long NOT_ACKED = -1;
|
static final long NOT_ACKED = -1;
|
||||||
|
static final long UNMATCHED_SEQ = -2;
|
||||||
|
|
||||||
static final int VERSION = 2;
|
static final int VERSION = 2;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1018,6 +1020,13 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
metadata.producerSequenceIdTracker.isDuplicate(command.getMessageId());
|
metadata.producerSequenceIdTracker.isDuplicate(command.getMessageId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Long extractSequenceId(Long prev) {
|
||||||
|
if (prev < NOT_ACKED) {
|
||||||
|
prev = Math.abs(prev) + UNMATCHED_SEQ;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
void updateIndex(Transaction tx, KahaRemoveMessageCommand command, Location ackLocation) throws IOException {
|
void updateIndex(Transaction tx, KahaRemoveMessageCommand command, Location ackLocation) throws IOException {
|
||||||
StoredDestination sd = getStoredDestination(command.getDestination(), tx);
|
StoredDestination sd = getStoredDestination(command.getDestination(), tx);
|
||||||
if (!command.hasSubscriptionKey()) {
|
if (!command.hasSubscriptionKey()) {
|
||||||
|
@ -1039,13 +1048,16 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
// Make sure it's a valid message id...
|
// Make sure it's a valid message id...
|
||||||
if (sequence != null) {
|
if (sequence != null) {
|
||||||
String subscriptionKey = command.getSubscriptionKey();
|
String subscriptionKey = command.getSubscriptionKey();
|
||||||
Long prev = sd.subscriptionAcks.put(tx, subscriptionKey, sequence);
|
Long ackSequenceToStore = sequence;
|
||||||
|
|
||||||
if (command.getAck() == UNMATCHED) {
|
if (command.getAck() == UNMATCHED) {
|
||||||
sd.subscriptionAcks.put(tx, subscriptionKey, prev);
|
// store negative sequence to indicate that it was unmatched
|
||||||
|
ackSequenceToStore = new Long(UNMATCHED_SEQ - sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Long prev = sd.subscriptionAcks.put(tx, subscriptionKey, ackSequenceToStore);
|
||||||
|
|
||||||
// The following method handles deleting un-referenced messages.
|
// The following method handles deleting un-referenced messages.
|
||||||
removeAckLocation(tx, sd, subscriptionKey, prev);
|
removeAckLocation(tx, sd, subscriptionKey, extractSequenceId(prev));
|
||||||
|
|
||||||
// Add it to the new location set.
|
// Add it to the new location set.
|
||||||
addAckLocation(sd, sequence, subscriptionKey);
|
addAckLocation(sd, sequence, subscriptionKey);
|
||||||
|
@ -1116,7 +1128,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
sd.subscriptions.remove(tx, subscriptionKey);
|
sd.subscriptions.remove(tx, subscriptionKey);
|
||||||
Long prev = sd.subscriptionAcks.remove(tx, subscriptionKey);
|
Long prev = sd.subscriptionAcks.remove(tx, subscriptionKey);
|
||||||
if( prev!=null ) {
|
if( prev!=null ) {
|
||||||
removeAckLocation(tx, sd, subscriptionKey, prev);
|
removeAckLocation(tx, sd, subscriptionKey, extractSequenceId(prev));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1468,7 +1480,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
|
||||||
|
|
||||||
for (Iterator<Entry<String, Long>> iterator = rc.subscriptionAcks.iterator(tx); iterator.hasNext();) {
|
for (Iterator<Entry<String, Long>> iterator = rc.subscriptionAcks.iterator(tx); iterator.hasNext();) {
|
||||||
Entry<String, Long> entry = iterator.next();
|
Entry<String, Long> entry = iterator.next();
|
||||||
addAckLocation(rc, entry.getValue(), entry.getKey());
|
addAckLocation(rc, extractSequenceId(entry.getValue()), entry.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc.orderIndex.nextMessageId == 0) {
|
if (rc.orderIndex.nextMessageId == 0) {
|
||||||
|
|
|
@ -91,14 +91,14 @@ public class DurableSubscriptionOfflineTest extends org.apache.activemq.TestSupp
|
||||||
broker.stop();
|
broker.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initCombosForTestOfflineSubscription() throws Exception {
|
public void x_initCombosForTestConsumeOnlyMatchedMessages() throws Exception {
|
||||||
this.addCombinationValues("defaultPersistenceAdapter",
|
this.addCombinationValues("defaultPersistenceAdapter",
|
||||||
new Object[]{ PersistenceAdapterChoice.KahaDB, PersistenceAdapterChoice.JDBC});
|
new Object[]{ PersistenceAdapterChoice.KahaDB, PersistenceAdapterChoice.JDBC});
|
||||||
this.addCombinationValues("usePrioritySupport",
|
this.addCombinationValues("usePrioritySupport",
|
||||||
new Object[]{ Boolean.TRUE, Boolean.FALSE});
|
new Object[]{ Boolean.TRUE, Boolean.FALSE});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOfflineSubscription() throws Exception {
|
public void testConsumeOnlyMatchedMessages() throws Exception {
|
||||||
// create durable subscription
|
// create durable subscription
|
||||||
Connection con = createConnection();
|
Connection con = createConnection();
|
||||||
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
@ -140,7 +140,7 @@ public class DurableSubscriptionOfflineTest extends org.apache.activemq.TestSupp
|
||||||
assertEquals(sent, listener.count);
|
assertEquals(sent, listener.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOfflineSubscription2() throws Exception {
|
public void testConsumeAllMatchedMessages() throws Exception {
|
||||||
// create durable subscription
|
// create durable subscription
|
||||||
Connection con = createConnection();
|
Connection con = createConnection();
|
||||||
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
@ -181,7 +181,7 @@ public class DurableSubscriptionOfflineTest extends org.apache.activemq.TestSupp
|
||||||
assertEquals(sent, listener.count);
|
assertEquals(sent, listener.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOfflineSubscription3() throws Exception {
|
public void testVerifyAllConsumedAreAcked() throws Exception {
|
||||||
// create durable subscription
|
// create durable subscription
|
||||||
Connection con = createConnection();
|
Connection con = createConnection();
|
||||||
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
@ -237,7 +237,7 @@ public class DurableSubscriptionOfflineTest extends org.apache.activemq.TestSupp
|
||||||
assertEquals(0, listener.count);
|
assertEquals(0, listener.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOfflineSubscription4() throws Exception {
|
public void testTwoOfflineSubscriptionCanConsume() throws Exception {
|
||||||
// create durable subscription 1
|
// create durable subscription 1
|
||||||
Connection con = createConnection("cliId1");
|
Connection con = createConnection("cliId1");
|
||||||
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
||||||
|
|
Loading…
Reference in New Issue