doTestNoDuplicateAfterCacheFullAndAcked(512);
}
public void doTestNoDuplicateAfterCacheFullAndAcked(final int auditDepth) throws Exception {
final PersistenceAdapter persistenceAdapter = brokerService.getPersistenceAdapter();
final MessageStore queueMessageStore =
persistenceAdapter.createQueueMessageStore(destination);
final ConnectionContext contextNotInTx = new ConnectionContext();
final ConsumerInfo consumerInfo = new ConsumerInfo();
final DestinationStatistics destinationStatistics = new DestinationStatistics();
consumerInfo.setExclusive(true);
final Queue queue = new Queue(brokerService, destination,
queueMessageStore, destinationStatistics, brokerService.getTaskRunnerFactory());
// a workaround for this issue
// queue.setUseCache(false);
queue.systemUsage.getMemoryUsage().setLimit(1024 * 1024 * 10);
queue.setMaxAuditDepth(auditDepth);
queue.initialize();
queue.start();
ProducerBrokerExchange producerExchange = new ProducerBrokerExchange();
ProducerInfo producerInfo = new ProducerInfo();
ProducerState producerState = new ProducerState(producerInfo);
producerExchange.setProducerState(producerState);
producerExchange.setConnectionContext(contextNotInTx);
final CountDownLatch receivedLatch = new CountDownLatch(count);
final AtomicLong ackedCount = new AtomicLong(0);
final AtomicLong enqueueCounter = new AtomicLong(0);
final Vector<String> errors = new Vector<String>();
// populate the queue store, exceed memory limit so that cache is disabled
for (int i = 0; i < count; i++) {
Message message = getMessage(i);
queue.send(producerExchange, message);
}
assertEquals("store count is correct", count, queueMessageStore.getMessageCount());
// pull from store in small windows
Subscription subscription = new Subscription() {
public void add(MessageReference node) throws Exception {
if (enqueueCounter.get() != node.getMessageId().getProducerSequenceId()) {
errors.add("Not in sequence at: " + enqueueCounter.get() + ", received: "
+ node.getMessageId().getProducerSequenceId());
}
assertEquals("is in order", enqueueCounter.get(), node
.getMessageId().getProducerSequenceId());
receivedLatch.countDown();
enqueueCounter.incrementAndGet();
node.decrementReferenceCount();
}
public void add(ConnectionContext context, Destination destination)
throws Exception {
}
public int countBeforeFull() {
if (isFull()) {
return 0;
} else {
return fullWindow - (int) (enqueueCounter.get() - ackedCount.get());
}
}
public void destroy() {
};
public void gc() {
}
public ConsumerInfo getConsumerInfo() {
return consumerInfo;
}
public ConnectionContext getContext() {
return null;
}
public long getDequeueCounter() {
return 0;
}
public long getDispatchedCounter() {
return 0;
}
public int getDispatchedQueueSize() {
return 0;
}
public long getEnqueueCounter() {
return 0;
}
public int getInFlightSize() {
return 0;
}
public int getInFlightUsage() {
return 0;
}
public ObjectName getObjectName() {
return null;
}
public int getPendingQueueSize() {
return 0;
}
public int getPrefetchSize() {
return 0;
}
public String getSelector() {
return null;
}
public boolean isBrowser() {
return false;
}
public boolean isFull() {
return (enqueueCounter.get() - ackedCount.get()) >= fullWindow;
}
public boolean isHighWaterMark() {
return false;
}
public boolean isLowWaterMark() {
return false;
}
public boolean isRecoveryRequired() {
return false;
}
public boolean isSlave() {
return false;
}
public boolean matches(MessageReference node,
MessageEvaluationContext context) throws IOException {
return true;
}
public boolean matches(ActiveMQDestination destination) {
return true;
}
public void processMessageDispatchNotification(
MessageDispatchNotification mdn) throws Exception {
}
public Response pullMessage(ConnectionContext context,
MessagePull pull) throws Exception {
return null;
}
public List<MessageReference> remove(ConnectionContext context,
Destination destination) throws Exception {
return null;
}
public void setObjectName(ObjectName objectName) {
}
public void setSelector(String selector)
throws InvalidSelectorException,
UnsupportedOperationException {
}
public void updateConsumerPrefetch(int newPrefetch) {
}
public boolean addRecoveredMessage(ConnectionContext context,
MessageReference message) throws Exception {
return false;
}
public ActiveMQDestination getActiveMQDestination() {
return destination;
}
public void acknowledge(ConnectionContext context, MessageAck ack)
throws Exception {
}
public int getCursorMemoryHighWaterMark(){
return 0;
}
public void setCursorMemoryHighWaterMark(
int cursorMemoryHighWaterMark) {
}
};
queue.addSubscription(contextNotInTx, subscription);
int removeIndex = 0;
do {
// Simulate periodic acks in small but recent windows
long receivedCount = enqueueCounter.get();
if (receivedCount > ackStartIndex) {
if (receivedCount >= removeIndex + ackWindow) {
for (int j = 0; j < ackBatchSize; j++, removeIndex++) {
ackedCount.incrementAndGet();
MessageAck ack = new MessageAck();
ack.setLastMessageId(new MessageId(mesageIdRoot
+ removeIndex));
ack.setMessageCount(1);
queue.removeMessage(contextNotInTx, subscription,
new IndirectMessageReference(
getMessage(removeIndex)), ack);
queue.wakeup();
}
if (removeIndex % 1000 == 0) {
LOG.info("acked: " + removeIndex);
persistenceAdapter.checkpoint(true);
}
}
}
} while (!receivedLatch.await(0, TimeUnit.MILLISECONDS) && errors.isEmpty());
assertTrue("There are no errors: " + errors, errors.isEmpty());
assertEquals(count, enqueueCounter.get());
assertEquals("store count is correct", count - removeIndex,
queueMessageStore.getMessageCount());
}