// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.
package com.rabbitmq.client.test.server;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeoutException;
import org.junit.Test;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.test.BrokerTestCase;
/**
* Test for bug 19219 - timeouts due to task parallelism in channel
* closure code.
*/
public class Bug19219Test extends BrokerTestCase {
/*
These settings require careful tuning. Depending on them we get
one of three outcomes:
1) this program terminates normally
2) the broker runs out of memory and eventually dies, and the
this program barfs and/or dies
3) there are lots of timeout errors in the broker log
It's the last case we are interested in.
The settings below work on tanto against default.
*/
private static final int Q_COUNT = 1500;
private static final int PUB_THREAD_COUNT = 100;
private static final int CLOSE_DELAY = 2000;
private static final Semaphore init = new Semaphore(0);
private static final CountDownLatch resume = new CountDownLatch(1);
private static void publish(final Channel ch)
throws IOException {
ch.basicPublish("amq.fanout", "",
MessageProperties.PERSISTENT_TEXT_PLAIN,
new byte[0]);
}
@Test public void it() throws IOException, InterruptedException {
final Consumer c = new DefaultConsumer(channel);
//1. create lots of auto-delete queues, bind them to the
//amq.fanout exchange, and set up a non-auto-ack consumer for
//each.
for (int i = 0; i < Q_COUNT; i++) {
String qName = channel.queueDeclare().getQueue();
channel.queueBind(qName, "amq.fanout", "");
channel.basicConsume(qName, false, c);
}
//2. send lots of messages in background, to keep the server,
//and especially the queues, busy
final Runnable r = new Runnable() {
public void run() {
try {
startPublisher();
} catch (IOException e) {
} catch (InterruptedException e) {
} catch (TimeoutException e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < PUB_THREAD_COUNT; i++) {
final Thread t = new Thread(r);
t.start();
//wait for thread to finish initialisation
init.acquire();
}
//tell all threads to resume
resume.countDown();
//wait for threads to get into full swing
Thread.sleep(CLOSE_DELAY);
//3. close channel. This will result in the server notifying
//all the queues in parallel, which in turn will requeue all
//the messages. The combined workload may result in some
//notifications timing out.
boolean success = false;
try {
channel.abort();
success = true;
} catch (ShutdownSignalException e) {
} finally {
//We deliberately do not perform a clean shutdown of all
//the connections. This test is pushing the server really
//hard, so we chose the quickest way to end things.
channel = null;
connection = null;
assertTrue(success);
}
}
private void startPublisher() throws IOException, InterruptedException, TimeoutException {
final Connection conn = connectionFactory.newConnection();
final Channel pubCh = conn.createChannel();
//This forces the initialisation of the guid generation, which
//is an interaction with the persister and not something we
//want to see delay things.
publish(pubCh);
//a synchronous request, to make sure the publish is done
pubCh.queueDeclare();
//signal the main thread
init.release();
//wait for main thread to let us resume
resume.await();
//publish lots of messages
while(true) {
publish(pubCh);
}
}
}