/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.store.kahadb;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.apache.activemq.broker.region.BaseDestination;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.usage.StoreUsage;
import org.apache.activemq.util.IOHelper;
import org.apache.activemq.util.Wait;
import org.junit.After;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jms.BytesMessage;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.*;
public class MKahaDBStoreLimitTest {
private static final Logger LOG = LoggerFactory.getLogger(MKahaDBStoreLimitTest.class);
final ActiveMQQueue queueA = new ActiveMQQueue("Q.A");
final ActiveMQQueue queueB = new ActiveMQQueue("Q.B");
private BrokerService broker;
@After
public void stopBroker() throws Exception {
if (broker != null) {
broker.stop();
broker.waitUntilStopped();
}
}
private BrokerService createBroker(MultiKahaDBPersistenceAdapter persistenceAdapter) throws Exception {
broker = new BrokerService();
broker.setPersistenceAdapter(persistenceAdapter);
broker.setUseJmx(false);
broker.setAdvisorySupport(false);
broker.setSchedulerSupport(false);
broker.setPersistenceAdapter(persistenceAdapter);
broker.setDeleteAllMessagesOnStartup(true);
return broker;
}
@Test
public void testPerDestUsage() throws Exception {
// setup multi-kaha adapter
MultiKahaDBPersistenceAdapter persistenceAdapter = new MultiKahaDBPersistenceAdapter();
KahaDBPersistenceAdapter kahaStore = new KahaDBPersistenceAdapter();
kahaStore.setJournalMaxFileLength(1024 * 5);
kahaStore.setCleanupInterval(1000);
// set up a store per destination
FilteredKahaDBPersistenceAdapter filtered = new FilteredKahaDBPersistenceAdapter();
StoreUsage storeUsage = new StoreUsage();
storeUsage.setPercentLimit(10);
storeUsage.setTotal(1024*1024*10);
filtered.setUsage(storeUsage);
filtered.setPersistenceAdapter(kahaStore);
filtered.setPerDestination(true);
List<FilteredKahaDBPersistenceAdapter> stores = new ArrayList<>();
stores.add(filtered);
persistenceAdapter.setFilteredPersistenceAdapters(stores);
createBroker(persistenceAdapter).start();
produceMessages(queueA, 20);
produceMessages(queueB, 0);
LOG.info("Store global u: " + broker.getSystemUsage().getStoreUsage().getUsage() + ", %:" + broker.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue("some usage", broker.getSystemUsage().getStoreUsage().getUsage() > 0);
BaseDestination baseDestinationA = (BaseDestination) broker.getRegionBroker().getDestinationMap().get(queueA);
BaseDestination baseDestinationB = (BaseDestination) broker.getRegionBroker().getDestinationMap().get(queueB);
LOG.info("Store A u: " + baseDestinationA.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue(baseDestinationA.getSystemUsage().getStoreUsage().getUsage() > 0);
produceMessages(queueB, 40);
assertTrue(baseDestinationB.getSystemUsage().getStoreUsage().getUsage() > 0);
assertTrue(baseDestinationB.getSystemUsage().getStoreUsage().getUsage() > baseDestinationA.getSystemUsage().getStoreUsage().getUsage());
LOG.info("Store B u: " + baseDestinationB.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationB.getSystemUsage().getStoreUsage().getPercentUsage());
LOG.info("Store global u: " + broker.getSystemUsage().getStoreUsage().getUsage() + ", %:" + broker.getSystemUsage().getStoreUsage().getPercentUsage());
consume(queueA);
consume(queueB);
LOG.info("Store global u: " + broker.getSystemUsage().getStoreUsage().getUsage() + ", %:" + broker.getSystemUsage().getStoreUsage().getPercentUsage());
LOG.info("Store A u: " + baseDestinationA.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage());
LOG.info("Store B u: " + baseDestinationB.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationB.getSystemUsage().getStoreUsage().getPercentUsage());
}
@Test
public void testExplicitAdapter() throws Exception {
MultiKahaDBPersistenceAdapter persistenceAdapter = new MultiKahaDBPersistenceAdapter();
KahaDBPersistenceAdapter kahaStore = new KahaDBPersistenceAdapter();
kahaStore.setJournalMaxFileLength(1024*25);
FilteredKahaDBPersistenceAdapter filtered = new FilteredKahaDBPersistenceAdapter();
StoreUsage storeUsage = new StoreUsage();
storeUsage.setPercentLimit(50);
storeUsage.setTotal(512*1024);
filtered.setUsage(storeUsage);
filtered.setDestination(queueA);
filtered.setPersistenceAdapter(kahaStore);
List<FilteredKahaDBPersistenceAdapter> stores = new ArrayList<>();
stores.add(filtered);
persistenceAdapter.setFilteredPersistenceAdapters(stores);
BrokerService brokerService = createBroker(persistenceAdapter);
brokerService.getSystemUsage().getStoreUsage().setTotal(1024*1024);
brokerService.start();
produceMessages(queueA, 20);
LOG.info("Store global u: " + broker.getSystemUsage().getStoreUsage().getUsage() + ", %:" + broker.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue("some usage", broker.getSystemUsage().getStoreUsage().getUsage() > 0);
BaseDestination baseDestinationA = (BaseDestination) broker.getRegionBroker().getDestinationMap().get(queueA);
LOG.info("Store A u: " + baseDestinationA.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue("limited store has more % usage than parent", baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage() > broker.getSystemUsage().getStoreUsage().getPercentUsage());
}
@Test
public void testExplicitAdapterBlockingProducer() throws Exception {
MultiKahaDBPersistenceAdapter persistenceAdapter = new MultiKahaDBPersistenceAdapter();
KahaDBPersistenceAdapter kahaStore = new KahaDBPersistenceAdapter();
kahaStore.setJournalMaxFileLength(1024*8);
kahaStore.setIndexDirectory(new File(IOHelper.getDefaultDataDirectory()));
FilteredKahaDBPersistenceAdapter filtered = new FilteredKahaDBPersistenceAdapter();
StoreUsage storeUsage = new StoreUsage();
storeUsage.setLimit(40*1024);
filtered.setUsage(storeUsage);
filtered.setDestination(queueA);
filtered.setPersistenceAdapter(kahaStore);
List<FilteredKahaDBPersistenceAdapter> stores = new ArrayList<>();
stores.add(filtered);
persistenceAdapter.setFilteredPersistenceAdapters(stores);
BrokerService brokerService = createBroker(persistenceAdapter);
brokerService.start();
final AtomicBoolean done = new AtomicBoolean();
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Runnable() {
@Override
public void run() {
try {
produceMessages(queueA, 20);
done.set(true);
} catch (Exception ignored) {
}
}
});
assertTrue("some messages got to dest", Wait.waitFor(new Wait.Condition() {
@Override
public boolean isSatisified() throws Exception {
BaseDestination baseDestinationA = (BaseDestination) broker.getRegionBroker().getDestinationMap().get(queueA);
return baseDestinationA != null && baseDestinationA.getDestinationStatistics().getMessages().getCount() > 4l;
}
}));
BaseDestination baseDestinationA = (BaseDestination) broker.getRegionBroker().getDestinationMap().get(queueA);
// loop till producer stalled
long enqueues = 0l;
do {
enqueues = baseDestinationA.getDestinationStatistics().getEnqueues().getCount();
LOG.info("Dest Enqueues: " + enqueues);
TimeUnit.MILLISECONDS.sleep(500);
} while (enqueues != baseDestinationA.getDestinationStatistics().getEnqueues().getCount());
assertFalse("expect producer to block", done.get());
LOG.info("Store global u: " + broker.getSystemUsage().getStoreUsage().getUsage() + ", %:" + broker.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue("some usage", broker.getSystemUsage().getStoreUsage().getUsage() > 0);
LOG.info("Store A u: " + baseDestinationA.getSystemUsage().getStoreUsage().getUsage() + ", %: " + baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage());
assertTrue("limited store has more % usage than parent", baseDestinationA.getSystemUsage().getStoreUsage().getPercentUsage() > broker.getSystemUsage().getStoreUsage().getPercentUsage());
executor.shutdownNow();
}
private void consume(Destination queue) throws Exception {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?create=false");
Connection connection = cf.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(queue);
for (int i = 0; i < 5; ++i) {
assertNotNull("message[" + i + "]", consumer.receive(4000));
}
connection.close();
}
private void produceMessages(Destination queue, int count) throws Exception {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?create=false");
Connection connection = cf.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
BytesMessage bytesMessage = session.createBytesMessage();
bytesMessage.writeBytes(new byte[1*1024]);
for (int i = 0; i < count; ++i) {
producer.send(bytesMessage);
}
connection.close();
}
}