/*
* 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.client;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.activemq.artemis.api.core.ActiveMQObjectClosedException;
import org.apache.activemq.artemis.api.core.SimpleString;
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.api.core.client.MessageHandler;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.core.client.impl.ClientProducerCreditManagerImpl;
import org.apache.activemq.artemis.core.client.impl.ClientProducerCredits;
import org.apache.activemq.artemis.core.client.impl.ClientProducerInternal;
import org.apache.activemq.artemis.core.client.impl.ClientSessionInternal;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.tests.integration.IntegrationTestLogger;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ProducerFlowControlTest extends ActiveMQTestBase {
private static final IntegrationTestLogger log = IntegrationTestLogger.LOGGER;
private ServerLocator locator;
private ClientSessionFactory sf;
private ClientSession session;
private ActiveMQServer server;
protected boolean isNetty() {
return false;
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
locator = createFactory(isNetty());
}
// TODO need to test crashing a producer with unused credits returns them to the pool
@Test
public void testFlowControlSingleConsumer() throws Exception {
testFlowControl(1000, 500, 10 * 1024, 1024, 1024, 1024, 1, 1, 0, false);
}
@Test
public void testFlowControlAnon() throws Exception {
testFlowControl(1000, 500, 10 * 1024, 1024, 1024, 1024, 1, 1, 0, true);
}
@Test
public void testFlowControlSingleConsumerLargeMaxSize() throws Exception {
testFlowControl(1000, 500, 1024 * 1024, 1024, 1024, 1024, 1, 1, 0, false);
}
@Test
public void testFlowControlMultipleConsumers() throws Exception {
testFlowControl(1000, 500, -1, 1024, 1024, 1024, 5, 1, 0, false);
}
@Test
public void testFlowControlZeroConsumerWindowSize() throws Exception {
testFlowControl(1000, 500, 10 * 1024, 1024, 0, 1024, 1, 1, 0, false);
}
@Test
public void testFlowControlZeroAckBatchSize() throws Exception {
testFlowControl(1000, 500, 10 * 1024, 1024, 1024, 0, 1, 1, 0, false);
}
@Test
public void testFlowControlSingleConsumerSlowConsumer() throws Exception {
testFlowControl(100, 500, 1024, 512, 512, 512, 1, 1, 10, false);
}
@Test
public void testFlowControlSmallMessages() throws Exception {
testFlowControl(1000, 0, 10 * 1024, 1024, 1024, 1024, 1, 1, 0, false);
}
@Test
public void testFlowControlLargerMessagesSmallWindowSize() throws Exception {
testFlowControl(1000, 10 * 1024, 10 * 1024, 1024, 1024, 1024, 1, 1, 0, false);
}
@Test
public void testFlowControlMultipleProducers() throws Exception {
testFlowControl(1000, 500, 1024 * 1024, 1024, 1024, 1024, 1, 5, 0, false);
}
@Test
public void testFlowControlMultipleProducersAndConsumers() throws Exception {
testFlowControl(500, 500, 100 * 1024, 1024, 1024, 1024, 1, 3, 3, false);
}
@Test
public void testFlowControlMultipleProducersAnon() throws Exception {
testFlowControl(1000, 500, 1024 * 1024, 1024, 1024, 1024, 1, 5, 0, true);
}
@Test
public void testFlowControlLargeMessages2() throws Exception {
testFlowControl(1000, 10000, -1, 1024, 0, 0, 1, 1, 0, false, 1000, true);
}
@Test
public void testFlowControlLargeMessages3() throws Exception {
testFlowControl(1000, 10000, 100 * 1024, 1024, 1024, 0, 1, 1, 0, false, 1000, true);
}
@Test
public void testFlowControlLargeMessages4() throws Exception {
testFlowControl(1000, 10000, 100 * 1024, 1024, 1024, 1024, 1, 1, 0, false, 1000, true);
}
@Test
public void testFlowControlLargeMessages5() throws Exception {
testFlowControl(1000, 10000, 100 * 1024, 1024, -1, 1024, 1, 1, 0, false, 1000, true);
}
@Test
public void testFlowControlLargeMessages6() throws Exception {
testFlowControl(1000, 10000, 100 * 1024, 1024, 1024, 1024, 1, 1, 0, true, 1000, true);
}
@Test
public void testFlowControlLargeMessages7() throws Exception {
testFlowControl(1000, 10000, 100 * 1024, 1024, 1024, 1024, 2, 2, 0, true, 1000, true);
}
private void testFlowControl(final int numMessages,
final int messageSize,
final int maxSize,
final int producerWindowSize,
final int consumerWindowSize,
final int ackBatchSize,
final int numConsumers,
final int numProducers,
final long consumerDelay,
final boolean anon) throws Exception {
testFlowControl(numMessages, messageSize, maxSize, producerWindowSize, consumerWindowSize, ackBatchSize, numConsumers, numProducers, consumerDelay, anon, -1, false);
}
private void testFlowControl(final int numMessages,
final int messageSize,
final int maxSize,
final int producerWindowSize,
final int consumerWindowSize,
final int ackBatchSize,
final int numConsumers,
final int numProducers,
final long consumerDelay,
final boolean anon,
final int minLargeMessageSize,
final boolean realFiles) throws Exception {
final SimpleString address = new SimpleString("testaddress");
server = createServer(realFiles, isNetty());
AddressSettings addressSettings = new AddressSettings().setMaxSizeBytes(maxSize).setAddressFullMessagePolicy(AddressFullMessagePolicy.BLOCK);
HierarchicalRepository<AddressSettings> repos = server.getAddressSettingsRepository();
repos.addMatch(address.toString(), addressSettings);
server.start();
waitForServerToStart(server);
locator.setProducerWindowSize(producerWindowSize).setConsumerWindowSize(consumerWindowSize).setAckBatchSize(ackBatchSize);
if (minLargeMessageSize != -1) {
locator.setMinLargeMessageSize(minLargeMessageSize);
}
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.start();
final String queueName = "testqueue";
for (int i = 0; i < numConsumers; i++) {
session.createQueue(address, new SimpleString(queueName + i), null, false);
}
final byte[] bytes = RandomUtil.randomBytes(messageSize);
class MyHandler implements MessageHandler {
int count = 0;
final CountDownLatch latch = new CountDownLatch(1);
volatile Exception exception;
@Override
public void onMessage(final ClientMessage message) {
try {
byte[] bytesRead = new byte[messageSize];
message.getBodyBuffer().readBytes(bytesRead);
ActiveMQTestBase.assertEqualsByteArrays(bytes, bytesRead);
message.acknowledge();
if (++count == numMessages * numProducers) {
latch.countDown();
}
if (consumerDelay > 0) {
Thread.sleep(consumerDelay);
}
} catch (Exception e) {
ProducerFlowControlTest.log.error("Failed to handle message", e);
exception = e;
latch.countDown();
}
}
}
MyHandler[] handlers = new MyHandler[numConsumers];
for (int i = 0; i < numConsumers; i++) {
handlers[i] = new MyHandler();
ClientConsumer consumer = session.createConsumer(new SimpleString(queueName + i));
consumer.setMessageHandler(handlers[i]);
}
ClientProducer[] producers = new ClientProducer[numProducers];
for (int i = 0; i < numProducers; i++) {
if (anon) {
producers[i] = session.createProducer();
} else {
producers[i] = session.createProducer(address);
}
}
long start = System.currentTimeMillis();
for (int i = 0; i < numMessages; i++) {
ClientMessage message = session.createMessage(false);
message.getBodyBuffer().writeBytes(bytes);
for (int j = 0; j < numProducers; j++) {
if (anon) {
producers[j].send(address, message);
} else {
producers[j].send(message);
}
}
}
for (int i = 0; i < numConsumers; i++) {
Assert.assertTrue(handlers[i].latch.await(5, TimeUnit.MINUTES));
Assert.assertNull(handlers[i].exception);
}
long end = System.currentTimeMillis();
double rate = 1000 * (double) numMessages / (end - start);
ProducerFlowControlTest.log.info("rate is " + rate + " msgs / sec");
}
@Test
public void testClosingSessionUnblocksBlockedProducer() throws Exception {
final SimpleString address = new SimpleString("testaddress");
server = createServer(false, isNetty());
AddressSettings addressSettings = new AddressSettings().setMaxSizeBytes(1024).setAddressFullMessagePolicy(AddressFullMessagePolicy.BLOCK);
HierarchicalRepository<AddressSettings> repos = server.getAddressSettingsRepository();
repos.addMatch(address.toString(), addressSettings);
server.start();
waitForServerToStart(server);
locator.setProducerWindowSize(1024).setConsumerWindowSize(1024).setAckBatchSize(1024);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
final SimpleString queueName = new SimpleString("testqueue");
session.createQueue(address, queueName, null, false);
ClientProducer producer = session.createProducer(address);
byte[] bytes = new byte[2000];
ClientMessage message = session.createMessage(false);
message.getBodyBuffer().writeBytes(bytes);
final AtomicBoolean closed = new AtomicBoolean(false);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
closed.set(true);
session.close();
} catch (Exception e) {
}
}
});
t.start();
try {
// This will block
for (int i = 0; i < 10; i++) {
producer.send(message);
}
} catch (ActiveMQObjectClosedException expected) {
}
Assert.assertTrue(closed.get());
t.join();
}
@Test
public void testFlowControlMessageNotRouted() throws Exception {
final SimpleString address = new SimpleString("testaddress");
server = createServer(false, isNetty());
AddressSettings addressSettings = new AddressSettings().setMaxSizeBytes(1024).setAddressFullMessagePolicy(AddressFullMessagePolicy.BLOCK);
HierarchicalRepository<AddressSettings> repos = server.getAddressSettingsRepository();
repos.addMatch(address.toString(), addressSettings);
server.start();
waitForServerToStart(server);
locator.setProducerWindowSize(1024).setConsumerWindowSize(1024).setAckBatchSize(1024);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
ClientProducer producer = session.createProducer(address);
byte[] bytes = new byte[100];
final int numMessages = 1000;
for (int i = 0; i < numMessages; i++) {
ClientMessage message = session.createMessage(false);
message.getBodyBuffer().writeBytes(bytes);
producer.send(message);
}
}
// Not technically a flow control test, but what the hell
@Test
public void testMultipleConsumers() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
session.createQueue("address", "queue2", null, false);
session.createQueue("address", "queue3", null, false);
session.createQueue("address", "queue4", null, false);
session.createQueue("address", "queue5", null, false);
ClientConsumer consumer1 = session.createConsumer("queue1");
ClientConsumer consumer2 = session.createConsumer("queue2");
ClientConsumer consumer3 = session.createConsumer("queue3");
ClientConsumer consumer4 = session.createConsumer("queue4");
ClientConsumer consumer5 = session.createConsumer("queue5");
ClientProducer producer = session.createProducer("address");
byte[] bytes = new byte[2000];
ClientMessage message = session.createMessage(false);
message.getBodyBuffer().writeBytes(bytes);
final int numMessages = 1000;
for (int i = 0; i < numMessages; i++) {
producer.send(message);
}
session.start();
for (int i = 0; i < numMessages; i++) {
ClientMessage msg = consumer1.receive(1000);
Assert.assertNotNull(msg);
msg = consumer2.receive(5000);
Assert.assertNotNull(msg);
msg = consumer3.receive(5000);
Assert.assertNotNull(msg);
msg = consumer4.receive(5000);
Assert.assertNotNull(msg);
msg = consumer5.receive(5000);
Assert.assertNotNull(msg);
}
}
@Test
public void testProducerCreditsCaching1() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducerCredits credits = null;
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE * 2; i++) {
ClientProducer prod = session.createProducer("address");
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
if (credits != null) {
Assert.assertTrue(newCredits == credits);
}
credits = newCredits;
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching2() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducerCredits credits = null;
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE * 2; i++) {
ClientProducer prod = session.createProducer("address");
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
if (credits != null) {
Assert.assertTrue(newCredits == credits);
}
credits = newCredits;
prod.close();
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching3() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducerCredits credits = null;
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer("address" + i);
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
if (credits != null) {
Assert.assertFalse(newCredits == credits);
}
credits = newCredits;
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching4() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducerCredits credits = null;
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer("address" + i);
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
if (credits != null) {
Assert.assertFalse(newCredits == credits);
}
credits = newCredits;
prod.close();
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching5() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducerCredits credits = null;
List<ClientProducerCredits> creditsList = new ArrayList<>();
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer("address" + i);
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
if (credits != null) {
Assert.assertFalse(newCredits == credits);
}
credits = newCredits;
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
creditsList.add(credits);
}
Iterator<ClientProducerCredits> iter = creditsList.iterator();
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer("address" + i);
ClientProducerCredits newCredits = ((ClientProducerInternal) prod).getProducerCredits();
Assert.assertTrue(newCredits == iter.next());
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
for (int i = 0; i < 10; i++) {
session.createProducer("address" + (i + ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE));
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE + i + 1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching6() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer((String) null);
prod.send("address", session.createMessage(false));
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsCaching7() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
for (int i = 0; i < ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE; i++) {
ClientProducer prod = session.createProducer((String) null);
prod.send("address" + i, session.createMessage(false));
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(i + 1, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
for (int i = 0; i < 10; i++) {
ClientProducer prod = session.createProducer((String) null);
prod.send("address" + i, session.createMessage(false));
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
for (int i = 0; i < 10; i++) {
ClientProducer prod = session.createProducer((String) null);
prod.send("address2-" + i, session.createMessage(false));
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(ClientProducerCreditManagerImpl.MAX_UNREFERENCED_CREDITS_CACHE_SIZE, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}
@Test
public void testProducerCreditsRefCounting() throws Exception {
server = createServer(false, isNetty());
server.start();
waitForServerToStart(server);
sf = createSessionFactory(locator);
session = sf.createSession(false, true, true, true);
session.createQueue("address", "queue1", null, false);
ClientProducer prod1 = session.createProducer("address");
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
ClientProducer prod2 = session.createProducer("address");
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
ClientProducer prod3 = session.createProducer("address");
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
prod1.close();
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
prod2.close();
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(0, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
prod3.close();
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().creditsMapSize());
Assert.assertEquals(1, ((ClientSessionInternal) session).getProducerCreditManager().unReferencedCreditsSize());
}
}