|
@@ -44,12 +44,16 @@ import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
import java.util.Random;
|
|
|
+import java.util.concurrent.BrokenBarrierException;
|
|
|
+import java.util.concurrent.CountDownLatch;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
import javax.net.SocketFactory;
|
|
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.fs.CommonConfigurationKeys;
|
|
|
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
|
|
import org.apache.hadoop.io.IOUtils;
|
|
|
import org.apache.hadoop.io.IntWritable;
|
|
@@ -613,6 +617,148 @@ public class TestIPC {
|
|
|
server.stop();
|
|
|
}
|
|
|
|
|
|
+ private static class TestServerQueue extends Server {
|
|
|
+ final CountDownLatch firstCallLatch = new CountDownLatch(1);
|
|
|
+ final CountDownLatch callBlockLatch = new CountDownLatch(1);
|
|
|
+
|
|
|
+ TestServerQueue(int expectedCalls, int readers, int callQ, int handlers,
|
|
|
+ Configuration conf) throws IOException {
|
|
|
+ super(ADDRESS, 0, LongWritable.class, handlers, readers, callQ, conf, null, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Writable call(RPC.RpcKind rpcKind, String protocol, Writable param,
|
|
|
+ long receiveTime) throws IOException {
|
|
|
+ firstCallLatch.countDown();
|
|
|
+ try {
|
|
|
+ callBlockLatch.await();
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ throw new IOException(e);
|
|
|
+ }
|
|
|
+ return param;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Check that reader queueing works
|
|
|
+ * @throws BrokenBarrierException
|
|
|
+ * @throws InterruptedException
|
|
|
+ */
|
|
|
+ @Test(timeout=60000)
|
|
|
+ public void testIpcWithReaderQueuing() throws Exception {
|
|
|
+ // 1 reader, 1 connectionQ slot, 1 callq
|
|
|
+ for (int i=0; i < 10; i++) {
|
|
|
+ checkBlocking(1, 1, 1);
|
|
|
+ }
|
|
|
+ // 4 readers, 5 connectionQ slots, 2 callq
|
|
|
+ for (int i=0; i < 10; i++) {
|
|
|
+ checkBlocking(4, 5, 2);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // goal is to jam a handler with a connection, fill the callq with
|
|
|
+ // connections, in turn jamming the readers - then flood the server and
|
|
|
+ // ensure that the listener blocks when the reader connection queues fill
|
|
|
+ private void checkBlocking(int readers, int readerQ, int callQ) throws Exception {
|
|
|
+ int handlers = 1; // makes it easier
|
|
|
+
|
|
|
+ final Configuration conf = new Configuration();
|
|
|
+ conf.setInt(CommonConfigurationKeys.IPC_SERVER_RPC_READ_CONNECTION_QUEUE_SIZE_KEY, readerQ);
|
|
|
+
|
|
|
+ // send in enough clients to block up the handlers, callq, and readers
|
|
|
+ int initialClients = readers + callQ + handlers;
|
|
|
+ // max connections we should ever end up accepting at once
|
|
|
+ int maxAccept = initialClients + readers*readerQ + 1; // 1 = listener
|
|
|
+ // stress it with 2X the max
|
|
|
+ int clients = maxAccept*2;
|
|
|
+
|
|
|
+ final AtomicInteger failures = new AtomicInteger(0);
|
|
|
+ final CountDownLatch callFinishedLatch = new CountDownLatch(clients);
|
|
|
+
|
|
|
+ // start server
|
|
|
+ final TestServerQueue server =
|
|
|
+ new TestServerQueue(clients, readers, callQ, handlers, conf);
|
|
|
+ final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
|
|
+ server.start();
|
|
|
+
|
|
|
+ Client.setConnectTimeout(conf, 10000);
|
|
|
+
|
|
|
+ // instantiate the threads, will start in batches
|
|
|
+ Thread[] threads = new Thread[clients];
|
|
|
+ for (int i=0; i<clients; i++) {
|
|
|
+ threads[i] = new Thread(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ Client client = new Client(LongWritable.class, conf);
|
|
|
+ try {
|
|
|
+ client.call(new LongWritable(Thread.currentThread().getId()),
|
|
|
+ addr, null, null, 60000, conf);
|
|
|
+ } catch (Throwable e) {
|
|
|
+ LOG.error(e);
|
|
|
+ failures.incrementAndGet();
|
|
|
+ return;
|
|
|
+ } finally {
|
|
|
+ callFinishedLatch.countDown();
|
|
|
+ client.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // start enough clients to block up the handler, callq, and each reader;
|
|
|
+ // let the calls sequentially slot in to avoid some readers blocking
|
|
|
+ // and others not blocking in the race to fill the callq
|
|
|
+ for (int i=0; i < initialClients; i++) {
|
|
|
+ threads[i].start();
|
|
|
+ if (i==0) {
|
|
|
+ // let first reader block in a call
|
|
|
+ server.firstCallLatch.await();
|
|
|
+ } else if (i <= callQ) {
|
|
|
+ // let subsequent readers jam the callq, will happen immediately
|
|
|
+ while (server.getCallQueueLen() != i) {
|
|
|
+ Thread.sleep(1);
|
|
|
+ }
|
|
|
+ } // additional threads block the readers trying to add to the callq
|
|
|
+ }
|
|
|
+
|
|
|
+ // wait till everything is slotted, should happen immediately
|
|
|
+ Thread.sleep(10);
|
|
|
+ if (server.getNumOpenConnections() < initialClients) {
|
|
|
+ LOG.info("(initial clients) need:"+initialClients+" connections have:"+server.getNumOpenConnections());
|
|
|
+ Thread.sleep(100);
|
|
|
+ }
|
|
|
+ LOG.info("ipc layer should be blocked");
|
|
|
+ assertEquals(callQ, server.getCallQueueLen());
|
|
|
+ assertEquals(initialClients, server.getNumOpenConnections());
|
|
|
+
|
|
|
+ // now flood the server with the rest of the connections, the reader's
|
|
|
+ // connection queues should fill and then the listener should block
|
|
|
+ for (int i=initialClients; i<clients; i++) {
|
|
|
+ threads[i].start();
|
|
|
+ }
|
|
|
+ Thread.sleep(10);
|
|
|
+ if (server.getNumOpenConnections() < maxAccept) {
|
|
|
+ LOG.info("(max clients) need:"+maxAccept+" connections have:"+server.getNumOpenConnections());
|
|
|
+ Thread.sleep(100);
|
|
|
+ }
|
|
|
+ // check a few times to make sure we didn't go over
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ assertEquals(maxAccept, server.getNumOpenConnections());
|
|
|
+ Thread.sleep(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ // sanity check that no calls have finished
|
|
|
+ assertEquals(clients, callFinishedLatch.getCount());
|
|
|
+ LOG.info("releasing the calls");
|
|
|
+ server.callBlockLatch.countDown();
|
|
|
+ callFinishedLatch.await();
|
|
|
+ for (Thread t : threads) {
|
|
|
+ t.join();
|
|
|
+ }
|
|
|
+ assertEquals(0, failures.get());
|
|
|
+ server.stop();
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Make a call from a client and verify if header info is changed in server side
|
|
|
*/
|