/* * 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.artemis.tests.integration.cluster.distribution; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.client.ClientConsumer; import org.apache.activemq.artemis.api.core.client.ClientMessage; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl; import org.apache.activemq.artemis.api.core.management.MessageCounterInfo; import org.apache.activemq.artemis.api.core.management.QueueControl; import org.apache.activemq.artemis.api.core.management.ResourceNames; import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; public class ClusteredMessageCounterTest extends ClusterTestBase { private AtomicInteger total = new AtomicInteger(); private AtomicBoolean stopFlag = new AtomicBoolean(); private Timer timer1 = new Timer(); private Timer timer2 = new Timer(); private int numMsg = 1000; private List<MessageCounterInfo> results = new ArrayList<>(); @Override @Before public void setUp() throws Exception { super.setUp(); setupServers(); setupClusters(); total.set(0); stopFlag.set(false); } @Override @After public void tearDown() throws Exception { timer1.cancel(); timer2.cancel(); super.tearDown(); } protected void setupServers() throws Exception { setupServer(0, isFileStorage(), isNetty()); setupServer(1, isFileStorage(), isNetty()); } protected void setupClusters() { setupClusterConnection("cluster0", 0, 1, "queues", MessageLoadBalancingType.ON_DEMAND, 1, isNetty(), false); setupClusterConnection("cluster1", 1, 0, "queues", MessageLoadBalancingType.ON_DEMAND, 1, isNetty(), false); } protected boolean isNetty() { return true; } @Override protected ConfigurationImpl createBasicConfig(final int serverID) { ConfigurationImpl config = super.createBasicConfig(serverID); Map<String, AddressSettings> addrSettingsMap = config.getAddressesSettings(); AddressSettings addrSettings = new AddressSettings(); addrSettings.setMaxSizeBytes(10 * 1024); addrSettings.setPageSizeBytes(5 * 1024); addrSettings.setAddressFullMessagePolicy(AddressFullMessagePolicy.PAGE); addrSettingsMap.put("queues", addrSettings); if (serverID == 1) { config.setMessageCounterEnabled(true); } return config; } @Test public void testNonDurableMessageAddedWithPaging() throws Exception { testMessageAddedWithPaging(false); } @Test public void testDurableMessageAddedWithPaging() throws Exception { testMessageAddedWithPaging(true); } //messages flow from one node to another, in paging mode //check the messageAdded is correct. private void testMessageAddedWithPaging(boolean durable) throws Exception { startServers(0, 1); numMsg = 100; try { setupSessionFactory(0, isNetty()); setupSessionFactory(1, isNetty()); createQueue(0, "queues", "queue0", null, false); createQueue(1, "queues", "queue0", null, false); waitForBindings(1, "queues", 1, 0, true); waitForBindings(0, "queues", 1, 0, false); addConsumer(1, 1, "queue0", null); System.out.println("sending....."); send(0, "queues", numMsg, durable, null); verifyReceiveAllOnSingleConsumer(true, numMsg, 1); QueueControl control = (QueueControl) servers[1].getManagementService().getResource(ResourceNames.QUEUE + "queue0"); //wait up to 30sec to allow the counter get updated long timeout = 30000; while (timeout > 0 && (numMsg != control.getMessagesAdded())) { Thread.sleep(1000); timeout -= 1000; } assertEquals(numMsg, control.getMessagesAdded()); } finally { stopServers(0, 1); } } @Test public void testMessageCounterWithPaging() throws Exception { startServers(0, 1); try { setupSessionFactory(0, isNetty()); setupSessionFactory(1, isNetty()); createQueue(0, "queues", "queue0", null, false); createQueue(1, "queues", "queue0", null, false); waitForBindings(1, "queues", 1, 0, true); waitForBindings(0, "queues", 1, 0, false); System.out.println("sending....."); Thread sendThread = new Thread(new Runnable() { @Override public void run() { try { send(0, "queues", numMsg, true, null); } catch (Exception e) { e.printStackTrace(); } System.out.println("messages sent."); } }); QueueControl control = (QueueControl) servers[1].getManagementService().getResource(ResourceNames.QUEUE + "queue0"); ActiveMQServerControl serverControl = (ActiveMQServerControl) servers[1].getManagementService().getResource(ResourceNames.BROKER); serverControl.setMessageCounterSamplePeriod(300); CountDownLatch resultLatch = new CountDownLatch(40); MessageCounterCollector collector = new MessageCounterCollector(control, resultLatch); timer1.schedule(collector, 0); PeriodicalReceiver receiver = new PeriodicalReceiver(50, 1, 100); timer2.schedule(receiver, 0); sendThread.start(); try { resultLatch.await(120, TimeUnit.SECONDS); } finally { stopFlag.set(true); } sendThread.join(); System.out.println("Results collected: " + results.size()); //checking for (MessageCounterInfo info : results) { assertTrue("countDelta should be positive " + info.getCountDelta() + dumpResults(results), info.getCountDelta() >= 0); } } finally { timer1.cancel(); timer2.cancel(); stopServers(0, 1); } } private String dumpResults(List<MessageCounterInfo> results) { StringBuilder builder = new StringBuilder("\n"); for (int i = 0; i < results.size(); i++) { builder.append("result[" + i + "]: " + results.get(i).getCountDelta() + " " + results.get(i).getCount() + "\n"); } return builder.toString(); } //Periodically read the counter private class MessageCounterCollector extends TimerTask { private QueueControl queueControl; private CountDownLatch resultLatch; MessageCounterCollector(QueueControl queueControl, CountDownLatch resultLatch) { this.queueControl = queueControl; this.resultLatch = resultLatch; } @Override public void run() { if (stopFlag.get()) { return; } try { String result = queueControl.listMessageCounter(); MessageCounterInfo info = MessageCounterInfo.fromJSON(result); if (info.getCountDelta() != 0) { System.out.println("non zero value got ---> " + info.getCountDelta()); } results.add(info); resultLatch.countDown(); if (info.getCountDelta() < 0) { //stop and make the test finish quick stopFlag.set(true); while (resultLatch.getCount() > 0) { resultLatch.countDown(); } } } catch (Exception e) { e.printStackTrace(); } finally { if (!stopFlag.get()) { timer1.schedule(new MessageCounterCollector(this.queueControl, resultLatch), 200); } } } } //Periodically receive a number of messages private class PeriodicalReceiver extends TimerTask { private int batchSize; private int serverID; private long period; PeriodicalReceiver(int batchSize, int serverID, long period) { this.batchSize = batchSize; this.serverID = serverID; this.period = period; } @Override public void run() { if (stopFlag.get()) { return; } int num = 0; ClientSessionFactory sf = sfs[serverID]; ClientSession session = null; ClientConsumer consumer = null; try { session = sf.createSession(false, true, false); consumer = session.createConsumer("queue0", null); session.start(); for (; num < batchSize || stopFlag.get(); num++) { ClientMessage message = consumer.receive(2000); if (message == null) { System.out.println("No more messages received!"); break; } message.acknowledge(); } session.commit(); } catch (ActiveMQException e) { e.printStackTrace(); } finally { System.out.println("received messages: " + num); if (consumer != null) { try { consumer.close(); } catch (ActiveMQException e) { e.printStackTrace(); } } if (session != null) { try { session.close(); } catch (ActiveMQException e) { e.printStackTrace(); } } //we only receive (numMsg - 200) to avoid the paging being cleaned up //when all paged messages are consumed. if (!stopFlag.get() && total.addAndGet(num) < numMsg - 200) { System.out.println("go for another batch " + total.get()); timer2.schedule(new PeriodicalReceiver(this.batchSize, this.serverID, this.period), period); } } } } }