resolve issue with kahadb durable subs with selectors after restart, persist the ack locations, kahadb version to 3 with auto upgrade from 1 or 2. https://issues.apache.org/activemq/browse/AMQ-2985

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1036524 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Gary Tully 2010-11-18 16:44:50 +00:00
parent fe3660dfcd
commit 3f0cf98407
8 changed files with 256 additions and 85 deletions

View File

@ -53,8 +53,8 @@ public abstract class AbstractStoreCursor extends AbstractPendingMessageCursor i
public final synchronized void start() throws Exception{ public final synchronized void start() throws Exception{
if (!isStarted()) { if (!isStarted()) {
super.start();
clear(); clear();
super.start();
resetBatch(); resetBatch();
this.size = getStoreSize(); this.size = getStoreSize();
this.storeHasMessages=this.size > 0; this.storeHasMessages=this.size > 0;

View File

@ -928,7 +928,7 @@ public class KahaDBStore extends MessageDatabase implements PersistenceAdapter {
ActiveMQDestination dest = convert(entry.getKey()); ActiveMQDestination dest = convert(entry.getKey());
if (dest.isTopic()) { if (dest.isTopic()) {
StoredDestination loadedStore = getStoredDestination(convert(dest), tx); StoredDestination loadedStore = getStoredDestination(convert(dest), tx);
if (loadedStore.ackPositions.isEmpty()) { if (loadedStore.ackPositions.isEmpty(tx)) {
isEmptyTopic = true; isEmptyTopic = true;
} }
} }

View File

@ -16,6 +16,7 @@
*/ */
package org.apache.activemq.store.kahadb; package org.apache.activemq.store.kahadb;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@ -100,7 +101,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
static final long NOT_ACKED = -1; static final long NOT_ACKED = -1;
static final long UNMATCHED_SEQ = -2; static final long UNMATCHED_SEQ = -2;
static final int VERSION = 2; static final int VERSION = 3;
protected class Metadata { protected class Metadata {
@ -165,9 +166,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} else { } else {
os.writeBoolean(false); os.writeBoolean(false);
} }
if (version > 1) { os.writeInt(VERSION);
os.writeInt(version);
}
} }
} }
@ -255,8 +254,6 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
metadata.destinations.load(tx); metadata.destinations.load(tx);
} }
}); });
pageFile.flush();
// Load up all the destinations since we need to scan all the indexes to figure out which journal files can be deleted. // Load up all the destinations since we need to scan all the indexes to figure out which journal files can be deleted.
// Perhaps we should just keep an index of file // Perhaps we should just keep an index of file
storedDestinations.clear(); storedDestinations.clear();
@ -269,6 +266,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} }
} }
}); });
pageFile.flush();
}finally { }finally {
this.indexLock.writeLock().unlock(); this.indexLock.writeLock().unlock();
} }
@ -985,7 +983,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
// Skip adding the message to the index if this is a topic and there are // Skip adding the message to the index if this is a topic and there are
// no subscriptions. // no subscriptions.
if (sd.subscriptions != null && sd.ackPositions.isEmpty()) { if (sd.subscriptions != null && sd.ackPositions.isEmpty(tx)) {
return; return;
} }
@ -1055,18 +1053,19 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} }
Long prev = sd.subscriptionAcks.put(tx, subscriptionKey, ackSequenceToStore); Long prev = sd.subscriptionAcks.put(tx, subscriptionKey, ackSequenceToStore);
if (prev != null) {
if (ackSequenceToStore != sequence) { if (ackSequenceToStore != sequence) {
// unmatched, need to add ack locations for the intermediate sequences // unmatched, need to add ack locations for the intermediate sequences
for (long matchedGapSequence = extractSequenceId(prev) + 1; matchedGapSequence < sequence; matchedGapSequence++) { for (long matchedGapSequence = extractSequenceId(prev) + 1; matchedGapSequence < sequence; matchedGapSequence++) {
addAckLocation(sd, matchedGapSequence, subscriptionKey); addAckLocation(tx, sd, matchedGapSequence, subscriptionKey);
}
} }
// The following method handles deleting un-referenced messages.
removeAckLocation(tx, sd, subscriptionKey, extractSequenceId(prev));
} }
// The following method handles deleting un-referenced messages.
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(tx, sd, sequence, subscriptionKey);
} }
} }
@ -1107,6 +1106,10 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
sd.subscriptionAcks.clear(tx); sd.subscriptionAcks.clear(tx);
sd.subscriptionAcks.unload(tx); sd.subscriptionAcks.unload(tx);
tx.free(sd.subscriptionAcks.getPageId()); tx.free(sd.subscriptionAcks.getPageId());
sd.ackPositions.clear(tx);
sd.ackPositions.unload(tx);
tx.free(sd.ackPositions.getPageId());
} }
String key = key(command.getDestination()); String key = key(command.getDestination());
@ -1127,7 +1130,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} }
sd.subscriptionAcks.put(tx, subscriptionKey, ackLocation); sd.subscriptionAcks.put(tx, subscriptionKey, ackLocation);
addAckLocation(sd, ackLocation, subscriptionKey); addAckLocation(tx, sd, ackLocation, subscriptionKey);
} else { } else {
// delete the sub... // delete the sub...
String subscriptionKey = command.getSubscriptionKey(); String subscriptionKey = command.getSubscriptionKey();
@ -1326,13 +1329,13 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
BTreeIndex<String, KahaSubscriptionCommand> subscriptions; BTreeIndex<String, KahaSubscriptionCommand> subscriptions;
BTreeIndex<String, Long> subscriptionAcks; BTreeIndex<String, Long> subscriptionAcks;
HashMap<String, MessageOrderCursor> subscriptionCursors; HashMap<String, MessageOrderCursor> subscriptionCursors;
TreeMap<Long, HashSet<String>> ackPositions; BTreeIndex<Long, HashSet<String>> ackPositions;
} }
protected class StoredDestinationMarshaller extends VariableMarshaller<StoredDestination> { protected class StoredDestinationMarshaller extends VariableMarshaller<StoredDestination> {
public StoredDestination readPayload(DataInput dataIn) throws IOException { public StoredDestination readPayload(DataInput dataIn) throws IOException {
StoredDestination value = new StoredDestination(); final StoredDestination value = new StoredDestination();
value.orderIndex.defaultPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong()); value.orderIndex.defaultPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong());
value.locationIndex = new BTreeIndex<Location, Long>(pageFile, dataIn.readLong()); value.locationIndex = new BTreeIndex<Location, Long>(pageFile, dataIn.readLong());
value.messageIdIndex = new BTreeIndex<String, Long>(pageFile, dataIn.readLong()); value.messageIdIndex = new BTreeIndex<String, Long>(pageFile, dataIn.readLong());
@ -1340,11 +1343,40 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
if (dataIn.readBoolean()) { if (dataIn.readBoolean()) {
value.subscriptions = new BTreeIndex<String, KahaSubscriptionCommand>(pageFile, dataIn.readLong()); value.subscriptions = new BTreeIndex<String, KahaSubscriptionCommand>(pageFile, dataIn.readLong());
value.subscriptionAcks = new BTreeIndex<String, Long>(pageFile, dataIn.readLong()); value.subscriptionAcks = new BTreeIndex<String, Long>(pageFile, dataIn.readLong());
if (metadata.version >= 3) {
value.ackPositions = new BTreeIndex<Long, HashSet<String>>(pageFile, dataIn.readLong());
} else {
// upgrade
pageFile.tx().execute(new Transaction.Closure<IOException>() {
public void execute(Transaction tx) throws IOException {
value.ackPositions = new BTreeIndex<Long, HashSet<String>>(pageFile, tx.allocate());
value.ackPositions.setKeyMarshaller(LongMarshaller.INSTANCE);
value.ackPositions.setValueMarshaller(HashSetStringMarshaller.INSTANCE);
value.ackPositions.load(tx);
}
});
}
} }
if (metadata.version >= 2) { if (metadata.version >= 2) {
value.orderIndex.lowPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong()); value.orderIndex.lowPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong());
value.orderIndex.highPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong()); value.orderIndex.highPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, dataIn.readLong());
} else {
// upgrade
pageFile.tx().execute(new Transaction.Closure<IOException>() {
public void execute(Transaction tx) throws IOException {
value.orderIndex.lowPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, tx.allocate());
value.orderIndex.lowPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE);
value.orderIndex.lowPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
value.orderIndex.lowPriorityIndex.load(tx);
value.orderIndex.highPriorityIndex = new BTreeIndex<Long, MessageKeys>(pageFile, tx.allocate());
value.orderIndex.highPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE);
value.orderIndex.highPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
value.orderIndex.highPriorityIndex.load(tx);
}
});
} }
return value; return value;
} }
@ -1356,13 +1388,12 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
dataOut.writeBoolean(true); dataOut.writeBoolean(true);
dataOut.writeLong(value.subscriptions.getPageId()); dataOut.writeLong(value.subscriptions.getPageId());
dataOut.writeLong(value.subscriptionAcks.getPageId()); dataOut.writeLong(value.subscriptionAcks.getPageId());
dataOut.writeLong(value.ackPositions.getPageId());
} else { } else {
dataOut.writeBoolean(false); dataOut.writeBoolean(false);
} }
if (metadata.version >= 2) { dataOut.writeLong(value.orderIndex.lowPriorityIndex.getPageId());
dataOut.writeLong(value.orderIndex.lowPriorityIndex.getPageId()); dataOut.writeLong(value.orderIndex.highPriorityIndex.getPageId());
dataOut.writeLong(value.orderIndex.highPriorityIndex.getPageId());
}
} }
} }
@ -1452,6 +1483,7 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
if (topic) { if (topic) {
rc.subscriptions = new BTreeIndex<String, KahaSubscriptionCommand>(pageFile, tx.allocate()); rc.subscriptions = new BTreeIndex<String, KahaSubscriptionCommand>(pageFile, tx.allocate());
rc.subscriptionAcks = new BTreeIndex<String, Long>(pageFile, tx.allocate()); rc.subscriptionAcks = new BTreeIndex<String, Long>(pageFile, tx.allocate());
rc.ackPositions = new BTreeIndex<Long, HashSet<String>>(pageFile, tx.allocate());
} }
metadata.destinations.put(tx, key, rc); metadata.destinations.put(tx, key, rc);
} }
@ -1481,18 +1513,24 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
rc.subscriptionAcks.setValueMarshaller(LongMarshaller.INSTANCE); rc.subscriptionAcks.setValueMarshaller(LongMarshaller.INSTANCE);
rc.subscriptionAcks.load(tx); rc.subscriptionAcks.load(tx);
rc.ackPositions = new TreeMap<Long, HashSet<String>>(); rc.ackPositions.setKeyMarshaller(LongMarshaller.INSTANCE);
rc.ackPositions.setValueMarshaller(HashSetStringMarshaller.INSTANCE);
rc.ackPositions.load(tx);
rc.subscriptionCursors = new HashMap<String, MessageOrderCursor>(); rc.subscriptionCursors = new HashMap<String, MessageOrderCursor>();
for (Iterator<Entry<String, Long>> iterator = rc.subscriptionAcks.iterator(tx); iterator.hasNext();) { if (metadata.version < 3) {
Entry<String, Long> entry = iterator.next(); // on upgrade need to fill ackLocation
addAckLocation(rc, extractSequenceId(entry.getValue()), entry.getKey()); for (Iterator<Entry<String, Long>> iterator = rc.subscriptionAcks.iterator(tx); iterator.hasNext();) {
Entry<String, Long> entry = iterator.next();
addAckLocation(tx, rc, extractSequenceId(entry.getValue()), entry.getKey());
}
} }
if (rc.orderIndex.nextMessageId == 0) { if (rc.orderIndex.nextMessageId == 0) {
// check for existing durable sub all acked out - pull next seq from acks as messages are gone // check for existing durable sub all acked out - pull next seq from acks as messages are gone
if (!rc.ackPositions.isEmpty()) { if (!rc.ackPositions.isEmpty(tx)) {
Long lastAckedMessageId = rc.ackPositions.lastKey(); Long lastAckedMessageId = rc.ackPositions.getLast(tx).getKey();
if (lastAckedMessageId != NOT_ACKED) { if (lastAckedMessageId != NOT_ACKED) {
rc.orderIndex.nextMessageId = lastAckedMessageId+1; rc.orderIndex.nextMessageId = lastAckedMessageId+1;
} }
@ -1500,6 +1538,11 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} }
} }
if (metadata.version < 3) {
// store again after upgrade
metadata.destinations.put(tx, key, rc);
}
return rc; return rc;
} }
@ -1508,13 +1551,14 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
* @param messageSequence * @param messageSequence
* @param subscriptionKey * @param subscriptionKey
*/ */
private void addAckLocation(StoredDestination sd, Long messageSequence, String subscriptionKey) { private void addAckLocation(Transaction tx, StoredDestination sd, Long messageSequence, String subscriptionKey) throws IOException {
HashSet<String> hs = sd.ackPositions.get(messageSequence); HashSet<String> hs = sd.ackPositions.get(tx, messageSequence);
if (hs == null) { if (hs == null) {
hs = new HashSet<String>(); hs = new HashSet<String>();
sd.ackPositions.put(messageSequence, hs);
} }
hs.add(subscriptionKey); hs.add(subscriptionKey);
// every ack location addition needs to be a btree modification to get it stored
sd.ackPositions.put(tx, messageSequence, hs);
} }
/** /**
@ -1527,12 +1571,12 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
private void removeAckLocation(Transaction tx, StoredDestination sd, String subscriptionKey, Long sequenceId) throws IOException { private void removeAckLocation(Transaction tx, StoredDestination sd, String subscriptionKey, Long sequenceId) throws IOException {
// Remove the sub from the previous location set.. // Remove the sub from the previous location set..
if (sequenceId != null) { if (sequenceId != null) {
HashSet<String> hs = sd.ackPositions.get(sequenceId); HashSet<String> hs = sd.ackPositions.get(tx, sequenceId);
if (hs != null) { if (hs != null) {
hs.remove(subscriptionKey); hs.remove(subscriptionKey);
if (hs.isEmpty()) { if (hs.isEmpty()) {
HashSet<String> firstSet = sd.ackPositions.values().iterator().next(); HashSet<String> firstSet = sd.ackPositions.getFirst(tx).getValue();
sd.ackPositions.remove(sequenceId); sd.ackPositions.remove(tx, sequenceId);
// Did we just empty out the first set in the // Did we just empty out the first set in the
// ordered list of ack locations? Then it's time to // ordered list of ack locations? Then it's time to
@ -1942,15 +1986,12 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
defaultPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE); defaultPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE);
defaultPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE); defaultPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
defaultPriorityIndex.load(tx); defaultPriorityIndex.load(tx);
if (metadata.version >= 2) { lowPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE);
lowPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE); lowPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
lowPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE); lowPriorityIndex.load(tx);
lowPriorityIndex.load(tx); highPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE);
highPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
highPriorityIndex.setKeyMarshaller(LongMarshaller.INSTANCE); highPriorityIndex.load(tx);
highPriorityIndex.setValueMarshaller(MessageKeysMarshaller.INSTANCE);
highPriorityIndex.load(tx);
}
} }
void allocate(Transaction tx) throws IOException { void allocate(Transaction tx) throws IOException {
@ -2193,6 +2234,33 @@ public class MessageDatabase extends ServiceSupport implements BrokerServiceAwar
} }
} }
private static class HashSetStringMarshaller extends VariableMarshaller<HashSet<String>> {
final static HashSetStringMarshaller INSTANCE = new HashSetStringMarshaller();
public void writePayload(HashSet<String> object, DataOutput dataOut) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oout = new ObjectOutputStream(baos);
oout.writeObject(object);
oout.flush();
oout.close();
byte[] data = baos.toByteArray();
dataOut.writeInt(data.length);
dataOut.write(data);
}
public HashSet<String> readPayload(DataInput dataIn) throws IOException {
int dataLen = dataIn.readInt();
byte[] data = new byte[dataLen];
dataIn.readFully(data);
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream oin = new ObjectInputStream(bais);
try {
return (HashSet<String>) oin.readObject();
} catch (ClassNotFoundException cfe) {
IOException ioe = new IOException("Failed to read HashSet<String>: " + cfe);
ioe.initCause(cfe);
throw ioe;
}
}
}
} }

View File

@ -34,6 +34,7 @@ import java.io.FileNotFoundException;
public class KahaDBVersionTest extends TestCase { public class KahaDBVersionTest extends TestCase {
final static File VERSION_1_DB= new File("src/test/resources/org/apache/activemq/store/kahadb/KahaDBVersion1"); final static File VERSION_1_DB= new File("src/test/resources/org/apache/activemq/store/kahadb/KahaDBVersion1");
final static File VERSION_2_DB= new File("src/test/resources/org/apache/activemq/store/kahadb/KahaDBVersion2");
protected BrokerService createBroker(KahaDBPersistenceAdapter kaha) throws Exception { protected BrokerService createBroker(KahaDBPersistenceAdapter kaha) throws Exception {
BrokerService broker = new BrokerService(); BrokerService broker = new BrokerService();
@ -47,7 +48,7 @@ public class KahaDBVersionTest extends TestCase {
public void XtestCreateStore() throws Exception { public void XtestCreateStore() throws Exception {
KahaDBPersistenceAdapter kaha = new KahaDBPersistenceAdapter(); KahaDBPersistenceAdapter kaha = new KahaDBPersistenceAdapter();
File dir = new File("src/test/resources/org/apache/activemq/store/kahadb/KahaDBVersion1"); File dir = new File("src/test/resources/org/apache/activemq/store/kahadb/KahaDBVersionX");
IOHelper.deleteFile(dir); IOHelper.deleteFile(dir);
kaha.setDirectory(dir); kaha.setDirectory(dir);
kaha.setJournalMaxFileLength(1024*1024); kaha.setJournalMaxFileLength(1024*1024);
@ -56,12 +57,19 @@ public class KahaDBVersionTest extends TestCase {
Connection connection = cf.createConnection(); Connection connection = cf.createConnection();
connection.setClientID("test"); connection.setClientID("test");
connection.start(); connection.start();
producerSomeMessages(connection);
connection.close();
broker.stop();
}
private void producerSomeMessages(Connection connection) throws Exception {
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("test.topic"); Topic topic = session.createTopic("test.topic");
Queue queue = session.createQueue("test.queue"); Queue queue = session.createQueue("test.queue");
MessageConsumer consumer = session.createDurableSubscriber(topic,"test"); MessageConsumer consumer = session.createDurableSubscriber(topic,"test");
consumer.close(); consumer.close();
MessageProducer producer = session.createProducer(topic); MessageProducer producer = session.createProducer(topic);
producer.setPriority(9);
for (int i =0; i < 1000; i++) { for (int i =0; i < 1000; i++) {
Message msg = session.createTextMessage("test message:"+i); Message msg = session.createTextMessage("test message:"+i);
producer.send(msg); producer.send(msg);
@ -71,45 +79,56 @@ public class KahaDBVersionTest extends TestCase {
Message msg = session.createTextMessage("test message:"+i); Message msg = session.createTextMessage("test message:"+i);
producer.send(msg); producer.send(msg);
} }
connection.stop();
broker.stop();
} }
public void testVersionConversion() throws Exception{ public void testVersion1Conversion() throws Exception{
doConvertRestartCycle(VERSION_1_DB);
}
public void testVersion2Conversion() throws Exception{
doConvertRestartCycle(VERSION_2_DB);
}
public void doConvertRestartCycle(File existingStore) throws Exception {
File testDir = new File("target/activemq-data/kahadb/versionDB"); File testDir = new File("target/activemq-data/kahadb/versionDB");
IOHelper.deleteFile(testDir); IOHelper.deleteFile(testDir);
IOHelper.copyFile(VERSION_1_DB, testDir); IOHelper.copyFile(existingStore, testDir);
KahaDBPersistenceAdapter kaha = new KahaDBPersistenceAdapter(); // on repeat store will be upgraded
kaha.setDirectory(testDir); for (int repeats = 0; repeats < 3; repeats++) {
kaha.setJournalMaxFileLength(1024*1024); KahaDBPersistenceAdapter kaha = new KahaDBPersistenceAdapter();
BrokerService broker = createBroker(kaha); kaha.setDirectory(testDir);
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost"); kaha.setJournalMaxFileLength(1024 * 1024);
Connection connection = cf.createConnection(); BrokerService broker = createBroker(kaha);
connection.setClientID("test"); ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost");
connection.start(); Connection connection = cf.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); connection.setClientID("test");
Topic topic = session.createTopic("test.topic"); connection.start();
Queue queue = session.createQueue("test.queue"); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer queueConsumer = session.createConsumer(queue); Topic topic = session.createTopic("test.topic");
for (int i = 0; i < 1000; i++) { Queue queue = session.createQueue("test.queue");
TextMessage msg = (TextMessage) queueConsumer.receive(10000);
//System.err.println(msg.getText());
assertNotNull(msg);
}
MessageConsumer topicConsumer = session.createDurableSubscriber(topic,"test");
for (int i = 0; i < 1000; i++) {
TextMessage msg = (TextMessage) topicConsumer.receive(10000);
//System.err.println(msg.getText());
assertNotNull(msg);
}
broker.stop();
if (repeats > 0) {
// upgraded store will be empty so generated some more messages
producerSomeMessages(connection);
}
MessageConsumer queueConsumer = session.createConsumer(queue);
for (int i = 0; i < 1000; i++) {
TextMessage msg = (TextMessage) queueConsumer.receive(10000);
//System.err.println(msg.getText());
assertNotNull(msg);
}
MessageConsumer topicConsumer = session.createDurableSubscriber(topic, "test");
for (int i = 0; i < 1000; i++) {
TextMessage msg = (TextMessage) topicConsumer.receive(10000);
//System.err.println(msg.getText());
assertNotNull(msg);
}
connection.close();
broker.stop();
}
} }
} }

View File

@ -617,6 +617,90 @@ public class DurableSubscriptionOfflineTest extends org.apache.activemq.TestSupp
assertEquals(0, listener.count); assertEquals(0, listener.count);
} }
public void testOfflineSubscriptionWithSelectorAfterRestart() throws Exception {
// create offline subs 1
Connection con = createConnection("offCli1");
Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
session.createDurableSubscriber(topic, "SubsId", "filter = 'true'", true);
session.close();
con.close();
// create offline subs 2
con = createConnection("offCli2");
session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
session.createDurableSubscriber(topic, "SubsId", "filter = 'true'", true);
session.close();
con.close();
// send messages
con = createConnection();
session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(null);
int filtered = 0;
for (int i = 0; i < 10; i++) {
boolean filter = (i %2 == 0); //(int) (Math.random() * 2) >= 1;
if (filter)
filtered++;
Message message = session.createMessage();
message.setStringProperty("filter", filter ? "true" : "false");
producer.send(topic, message);
}
LOG.info("sent: " + filtered);
Thread.sleep(1 * 1000);
session.close();
con.close();
// restart broker
Thread.sleep(3 * 1000);
broker.stop();
createBroker(false /*deleteAllMessages*/);
// send more messages
con = createConnection();
session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(null);
for (int i = 0; i < 10; i++) {
boolean filter = (i %2 == 0); //(int) (Math.random() * 2) >= 1;
if (filter)
filtered++;
Message message = session.createMessage();
message.setStringProperty("filter", filter ? "true" : "false");
producer.send(topic, message);
}
LOG.info("after restart, sent: " + filtered);
Thread.sleep(1 * 1000);
session.close();
con.close();
// test offline subs
con = createConnection("offCli1");
session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createDurableSubscriber(topic, "SubsId", "filter = 'true'", true);
Listener listener = new Listener();
consumer.setMessageListener(listener);
Connection con3 = createConnection("offCli2");
Session session3 = con3.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "SubsId", "filter = 'true'", true);
Listener listener3 = new Listener();
consumer3.setMessageListener(listener3);
Thread.sleep(3 * 1000);
session.close();
con.close();
session3.close();
con3.close();
assertEquals(filtered, listener.count);
assertEquals(filtered, listener3.count);
}
public static class Listener implements MessageListener { public static class Listener implements MessageListener {
int count = 0; int count = 0;