/* * 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.server; import java.util.Arrays; import java.util.Collection; import java.util.Map; import org.apache.activemq.artemis.api.core.Message; import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.activemq.artemis.api.core.client.ActiveMQClient; 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.ClientProducer; import org.apache.activemq.artemis.api.core.client.ClientSession; import org.apache.activemq.artemis.api.core.client.ClientSessionFactory; import org.apache.activemq.artemis.core.config.ScaleDownConfiguration; import org.apache.activemq.artemis.core.config.ha.LiveOnlyPolicyConfiguration; import org.apache.activemq.artemis.core.postoffice.Binding; import org.apache.activemq.artemis.core.postoffice.impl.LocalQueueBinding; import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType; import org.apache.activemq.artemis.core.settings.impl.AddressSettings; import org.apache.activemq.artemis.tests.integration.cluster.distribution.ClusterTestBase; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(value = Parameterized.class) public class ScaleDownTest extends ClusterTestBase { private boolean useScaleDownGroupName; // this will ensure that all tests in this class are run twice, // once with "true" passed to the class' constructor and once with "false" @Parameterized.Parameters(name = "useScaleDownGroupName={0}") public static Collection getParameters() { return Arrays.asList(new Object[][]{{true}, {false}}); } public ScaleDownTest(boolean useScaleDownGroupName) { this.useScaleDownGroupName = useScaleDownGroupName; } @Override @Before public void setUp() throws Exception { super.setUp(); setupLiveServer(0, isFileStorage(), isNetty(), true); setupLiveServer(1, isFileStorage(), isNetty(), true); LiveOnlyPolicyConfiguration haPolicyConfiguration0 = (LiveOnlyPolicyConfiguration) servers[0].getConfiguration().getHAPolicyConfiguration(); haPolicyConfiguration0.setScaleDownConfiguration(new ScaleDownConfiguration()); LiveOnlyPolicyConfiguration haPolicyConfiguration1 = (LiveOnlyPolicyConfiguration) servers[1].getConfiguration().getHAPolicyConfiguration(); haPolicyConfiguration1.setScaleDownConfiguration(new ScaleDownConfiguration()); if (useScaleDownGroupName) { haPolicyConfiguration0.getScaleDownConfiguration().setGroupName("bill"); haPolicyConfiguration1.getScaleDownConfiguration().setGroupName("bill"); } setupClusterConnection("cluster0", "testAddress", MessageLoadBalancingType.ON_DEMAND, 1, isNetty(), 0, 1); setupClusterConnection("cluster0", "testAddress", MessageLoadBalancingType.ON_DEMAND, 1, isNetty(), 1, 0); haPolicyConfiguration0.getScaleDownConfiguration().getConnectors().addAll(servers[0].getConfiguration().getClusterConfigurations().iterator().next().getStaticConnectors()); haPolicyConfiguration1.getScaleDownConfiguration().getConnectors().addAll(servers[1].getConfiguration().getClusterConfigurations().iterator().next().getStaticConnectors()); servers[0].getConfiguration().getAddressesSettings().put("#", new AddressSettings().setRedistributionDelay(0)); servers[1].getConfiguration().getAddressesSettings().put("#", new AddressSettings().setRedistributionDelay(0)); startServers(0, 1); setupSessionFactory(0, isNetty()); setupSessionFactory(1, isNetty()); } protected boolean isNetty() { return true; } @Test public void testBasicScaleDown() throws Exception { final int TEST_SIZE = 2; final String addressName = "testAddress"; final String queueName1 = "testQueue1"; final String queueName2 = "testQueue2"; // create 2 queues on each node mapped to the same address createQueue(0, addressName, queueName1, null, true); createQueue(0, addressName, queueName2, null, true); createQueue(1, addressName, queueName1, null, true); createQueue(1, addressName, queueName2, null, true); // send messages to node 0 send(0, addressName, TEST_SIZE, true, null); // consume a message from queue 2 addConsumer(1, 0, queueName2, null, false); ClientMessage clientMessage = consumers[1].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); consumers[1].getSession().commit(); // removeConsumer(1); // at this point on node 0 there should be 2 messages in testQueue1 and 1 message in testQueue2 Assert.assertEquals(TEST_SIZE, getMessageCount(((LocalQueueBinding) servers[0].getPostOffice().getBinding(new SimpleString(queueName1))).getQueue())); Assert.assertEquals(TEST_SIZE - 1, getMessageCount(((LocalQueueBinding) servers[0].getPostOffice().getBinding(new SimpleString(queueName2))).getQueue())); // trigger scaleDown from node 0 to node 1 servers[0].stop(); // get the 2 messages from queue 1 addConsumer(0, 1, queueName1, null); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); // ensure there are no more messages on queue 1 clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); // get the 1 message from queue 2 addConsumer(0, 1, queueName2, null); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); // ensure there are no more messages on queue 1 clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); } @Test public void testStoreAndForward() throws Exception { final int TEST_SIZE = 50; final String addressName1 = "testAddress1"; final String addressName2 = "testAddress2"; final String queueName1 = "testQueue1"; final String queueName2 = "testQueue2"; // create queues on each node mapped to 2 addresses createQueue(0, addressName1, queueName1, null, false); createQueue(1, addressName1, queueName1, null, false); createQueue(0, addressName2, queueName2, null, false); createQueue(1, addressName2, queueName2, null, false); // find and pause the sf queue so no messages actually move from node 0 to node 1 String sfQueueName = null; for (Map.Entry<SimpleString, Binding> entry : servers[0].getPostOffice().getAllBindings().entrySet()) { String temp = entry.getValue().getAddress().toString(); if (temp.startsWith(servers[1].getInternalNamingPrefix() + "sf.") && temp.endsWith(servers[1].getNodeID().toString())) { // we found the sf queue for the other node // need to pause the sfQueue here ((LocalQueueBinding) entry.getValue()).getQueue().pause(); sfQueueName = temp; } } assertNotNull(sfQueueName); // send messages to node 0 send(0, addressName1, TEST_SIZE, false, null); send(0, addressName2, TEST_SIZE, false, null); // add consumers to node 1 to force messages messages to redistribute to node 2 through the paused sf queue addConsumer(0, 1, queueName1, null); addConsumer(1, 1, queueName2, null); LocalQueueBinding queue1Binding = ((LocalQueueBinding) servers[0].getPostOffice().getBinding(new SimpleString(queueName1))); LocalQueueBinding queue2Binding = ((LocalQueueBinding) servers[0].getPostOffice().getBinding(new SimpleString(queueName2))); LocalQueueBinding sfQueueBinding = ((LocalQueueBinding) servers[0].getPostOffice().getBinding(new SimpleString(sfQueueName))); long timeout = 5000; long start = System.currentTimeMillis(); while (getMessageCount(queue1Binding.getQueue()) > 0 && System.currentTimeMillis() - start <= timeout) { Thread.sleep(50); } start = System.currentTimeMillis(); while (getMessageCount(queue2Binding.getQueue()) > 0 && System.currentTimeMillis() - start <= timeout) { Thread.sleep(50); } start = System.currentTimeMillis(); while (getMessageCount(sfQueueBinding.getQueue()) < TEST_SIZE * 2 && System.currentTimeMillis() - start <= timeout) { Thread.sleep(50); } // at this point on node 0 there should be 0 messages in test queues and TEST_SIZE * 2 messages in the sf queue Assert.assertEquals(0, getMessageCount(queue1Binding.getQueue())); Assert.assertEquals(0, getMessageCount(queue2Binding.getQueue())); Assert.assertEquals(TEST_SIZE * 2, getMessageCount(sfQueueBinding.getQueue())); removeConsumer(0); removeConsumer(1); // trigger scaleDown from node 0 to node 1 servers[0].stop(); // get the messages from node 1 addConsumer(0, 1, queueName1, null); for (int i = 0; i < TEST_SIZE; i++) { ClientMessage clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); } ClientMessage clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); addConsumer(0, 1, queueName2, null); for (int i = 0; i < TEST_SIZE; i++) { clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); } clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); } @Test public void testScaleDownWithMissingQueue() throws Exception { final int TEST_SIZE = 2; final String addressName = "testAddress"; final String queueName1 = "testQueue1"; final String queueName2 = "testQueue2"; // create 2 queues on each node mapped to the same address createQueue(0, addressName, queueName1, null, false); createQueue(0, addressName, queueName2, null, false); createQueue(1, addressName, queueName1, null, false); // send messages to node 0 send(0, addressName, TEST_SIZE, false, null); // consume a message from node 0 addConsumer(1, 0, queueName2, null, false); ClientMessage clientMessage = consumers[1].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); consumers[1].getSession().commit(); // trigger scaleDown from node 0 to node 1 servers[0].stop(); // get the 2 messages from queue 1 addConsumer(0, 1, queueName1, null); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); // ensure there are no more messages on queue 1 clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); // get the 1 message from queue 2 addConsumer(0, 1, queueName2, null); clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNotNull(clientMessage); clientMessage.acknowledge(); // ensure there are no more messages on queue 1 clientMessage = consumers[0].getConsumer().receive(250); Assert.assertNull(clientMessage); removeConsumer(0); } @Test public void testMessageProperties() throws Exception { final int TEST_SIZE = 5; final String addressName = "testAddress"; final String queueName = "testQueue"; createQueue(0, addressName, queueName, null, false); createQueue(1, addressName, queueName, null, false); ClientSessionFactory sf = sfs[0]; ClientSession session = addClientSession(sf.createSession(false, true, true)); ClientProducer producer = addClientProducer(session.createProducer(addressName)); StringBuilder international = new StringBuilder(); for (char x = 800; x < 1200; x++) { international.append(x); } String special = "\"<>'&"; for (int i = 0; i < TEST_SIZE; i++) { ClientMessage msg = session.createMessage(true); msg.getBodyBuffer().writeString("Bob the giant pig " + i); msg.putBooleanProperty("myBooleanProperty", Boolean.TRUE); msg.putByteProperty("myByteProperty", new Byte("0")); msg.putBytesProperty("myBytesProperty", new byte[]{0, 1, 2, 3, 4}); msg.putDoubleProperty("myDoubleProperty", i * 1.6); msg.putFloatProperty("myFloatProperty", i * 2.5F); msg.putIntProperty("myIntProperty", i); msg.putLongProperty("myLongProperty", Long.MAX_VALUE - i); msg.putObjectProperty("myObjectProperty", i); msg.putShortProperty("myShortProperty", new Integer(i).shortValue()); msg.putStringProperty("myStringProperty", "myStringPropertyValue_" + i); msg.putStringProperty("myNonAsciiStringProperty", international.toString()); msg.putStringProperty("mySpecialCharacters", special); producer.send(msg); } servers[0].stop(); sf = sfs[1]; session = addClientSession(sf.createSession(false, true, true)); ClientConsumer consumer = addClientConsumer(session.createConsumer(queueName)); session.start(); for (int i = 0; i < 5; i++) { ClientMessage msg = consumer.receive(250); byte[] body = new byte[msg.getBodySize()]; msg.getBodyBuffer().readBytes(body); Assert.assertTrue(new String(body).contains("Bob the giant pig " + i)); Assert.assertEquals(msg.getBooleanProperty("myBooleanProperty"), Boolean.TRUE); Assert.assertEquals(msg.getByteProperty("myByteProperty"), new Byte("0")); byte[] bytes = msg.getBytesProperty("myBytesProperty"); for (int j = 0; j < 5; j++) { Assert.assertEquals(j, bytes[j]); } Assert.assertEquals(i * 1.6, msg.getDoubleProperty("myDoubleProperty"), 0.000001); Assert.assertEquals(i * 2.5F, msg.getFloatProperty("myFloatProperty"), 0.000001); Assert.assertEquals(i, msg.getIntProperty("myIntProperty").intValue()); Assert.assertEquals(Long.MAX_VALUE - i, msg.getLongProperty("myLongProperty").longValue()); Assert.assertEquals(i, msg.getObjectProperty("myObjectProperty")); Assert.assertEquals(new Integer(i).shortValue(), msg.getShortProperty("myShortProperty").shortValue()); Assert.assertEquals("myStringPropertyValue_" + i, msg.getStringProperty("myStringProperty")); Assert.assertEquals(international.toString(), msg.getStringProperty("myNonAsciiStringProperty")); Assert.assertEquals(special, msg.getStringProperty("mySpecialCharacters")); } } @Test public void testLargeMessage() throws Exception { final String addressName = "testAddress"; final String queueName = "testQueue"; createQueue(0, addressName, queueName, null, true); createQueue(1, addressName, queueName, null, true); ClientSessionFactory sf = sfs[0]; ClientSession session = addClientSession(sf.createSession(false, false)); ClientProducer producer = addClientProducer(session.createProducer(addressName)); byte[] buffer = new byte[2 * ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE]; for (int i = 0; i < buffer.length; i++) { buffer[i] = getSamplebyte(i); } for (int nmsg = 0; nmsg < 10; nmsg++) { ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeBytes(buffer); producer.send(message); session.commit(); } servers[0].stop(); sf = sfs[1]; session = addClientSession(sf.createSession(false, true, true)); ClientConsumer consumer = addClientConsumer(session.createConsumer(queueName)); session.start(); for (int nmsg = 0; nmsg < 10; nmsg++) { ClientMessage msg = consumer.receive(250); Assert.assertNotNull(msg); Assert.assertEquals(2 * ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE, msg.getBodySize()); for (int i = 0; i < 2 * ActiveMQClient.DEFAULT_MIN_LARGE_MESSAGE_SIZE; i++) { byte byteRead = msg.getBodyBuffer().readByte(); Assert.assertEquals(msg + " Is different", ActiveMQTestBase.getSamplebyte(i), byteRead); } msg.acknowledge(); session.commit(); } } @Test public void testPaging() throws Exception { final int CHUNK_SIZE = 50; int messageCount = 0; final String addressName = "testAddress"; final String queueName = "testQueue"; createQueue(0, addressName, queueName, null, false); createQueue(1, addressName, queueName, null, false); ClientSessionFactory sf = sfs[0]; ClientSession session = addClientSession(sf.createSession(false, false)); ClientProducer producer = addClientProducer(session.createProducer(addressName)); AddressSettings defaultSetting = new AddressSettings().setPageSizeBytes(10 * 1024).setMaxSizeBytes(20 * 1024); servers[0].getAddressSettingsRepository().addMatch("#", defaultSetting); while (!servers[0].getPagingManager().getPageStore(new SimpleString(addressName)).isPaging()) { for (int i = 0; i < CHUNK_SIZE; i++) { ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeBytes(new byte[1024]); producer.send(message); messageCount++; } session.commit(); } servers[0].stop(); addConsumer(0, 1, queueName, null); for (int i = 0; i < messageCount; i++) { Assert.assertNotNull(consumers[0].getConsumer().receive(250)); } Assert.assertNull(consumers[0].getConsumer().receive(250)); removeConsumer(0); } @Test public void testOrderWithPaging() throws Exception { final int CHUNK_SIZE = 50; int messageCount = 0; final String addressName = "testAddress"; final String queueName = "testQueue"; createQueue(0, addressName, queueName, null, false); createQueue(1, addressName, queueName, null, false); ClientSessionFactory sf = sfs[0]; ClientSession session = addClientSession(sf.createSession(false, false)); ClientProducer producer = addClientProducer(session.createProducer(addressName)); AddressSettings defaultSetting = new AddressSettings().setPageSizeBytes(10 * 1024).setMaxSizeBytes(20 * 1024); servers[0].getAddressSettingsRepository().addMatch("#", defaultSetting); while (!servers[0].getPagingManager().getPageStore(new SimpleString(addressName)).isPaging()) { for (int i = 0; i < CHUNK_SIZE; i++) { ClientMessage message = session.createMessage(true); message.getBodyBuffer().writeBytes(new byte[1024]); message.putIntProperty("order", i); producer.send(message); messageCount++; } session.commit(); } servers[0].stop(); addConsumer(0, 1, queueName, null); for (int i = 0; i < messageCount; i++) { Assert.assertEquals(i, consumers[0].getConsumer().receive(250).getIntProperty("order").intValue()); } Assert.assertNull(consumers[0].getConsumer().receive(250)); removeConsumer(0); } @Test public void testFilters() throws Exception { final int TEST_SIZE = 50; final String addressName = "testAddress"; final String evenQueue = "evenQueue"; final String oddQueue = "oddQueue"; createQueue(0, addressName, evenQueue, "0", false); createQueue(0, addressName, oddQueue, "1", false); createQueue(1, addressName, evenQueue, "0", false); createQueue(1, addressName, oddQueue, "1", false); ClientSessionFactory sf = sfs[0]; ClientSession session = addClientSession(sf.createSession(false, false)); ClientProducer producer = addClientProducer(session.createProducer(addressName)); for (int i = 0; i < TEST_SIZE; i++) { Message message = session.createMessage(false); if (i % 2 == 0) message.putStringProperty(ClusterTestBase.FILTER_PROP, new SimpleString("0")); else message.putStringProperty(ClusterTestBase.FILTER_PROP, new SimpleString("1")); producer.send(message); } session.commit(); servers[0].stop(); addConsumer(0, 1, evenQueue, null); addConsumer(1, 1, oddQueue, null); for (int i = 0; i < TEST_SIZE; i++) { String compare; ClientMessage message; if (i % 2 == 0) { message = consumers[0].getConsumer().receive(250); compare = "0"; } else { message = consumers[1].getConsumer().receive(250); compare = "1"; } Assert.assertEquals(compare, message.getStringProperty(ClusterTestBase.FILTER_PROP)); } Assert.assertNull(consumers[0].getConsumer().receive(250)); Assert.assertNull(consumers[1].getConsumer().receive(250)); removeConsumer(0); removeConsumer(1); } }