package voldemort.server.socket; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import voldemort.ServerTestUtils; import voldemort.client.protocol.RequestFormatFactory; import voldemort.client.protocol.RequestFormatType; import voldemort.server.AbstractSocketService; import voldemort.server.RequestRoutingType; import voldemort.server.StoreRepository; import voldemort.server.protocol.RequestHandlerFactory; import voldemort.store.SleepyStore; import voldemort.store.Store; import voldemort.store.UnreachableStoreException; import voldemort.store.memory.InMemoryStorageEngine; import voldemort.store.nonblockingstore.NonblockingStoreCallback; import voldemort.store.socket.SocketDestination; import voldemort.store.socket.clientrequest.ClientRequestExecutor; import voldemort.store.socket.clientrequest.ClientRequestExecutorPool; import voldemort.store.socket.clientrequest.GetClientRequest; import voldemort.utils.ByteArray; @RunWith(Parameterized.class) public class NioSelectorManagerTest { private int port; private ClientRequestExecutorPool pool; private AbstractSocketService socketService; private RequestHandlerFactory factory; private SocketDestination dest1; private SleepyStore<ByteArray, byte[], byte[]> sleepyStore; private final static String STORE_NAME = "test"; // This can be never below the selector poll time of 500 ms. private final long MAX_HEART_BEAT_MS = 600; private final int numSelectors; public NioSelectorManagerTest(int numSelectors) { this.numSelectors = numSelectors; } @Parameters public static Collection<Object[]> configs() { return Arrays.asList(new Object[][] { { 1 }, { 2 } }); } @Before public void setUp() throws IOException { this.port = ServerTestUtils.findFreePort(); StoreRepository repository = new StoreRepository(); sleepyStore = new SleepyStore<ByteArray, byte[], byte[]>(Long.MAX_VALUE, new InMemoryStorageEngine<ByteArray, byte[], byte[]>(STORE_NAME)); repository.addLocalStore(sleepyStore); repository.addRoutedStore(sleepyStore); this.pool = new ClientRequestExecutorPool(50, 300, 250, 32 * 1024); this.dest1 = new SocketDestination("localhost", port, RequestFormatType.VOLDEMORT_V1); final Store<ByteArray, byte[], byte[]> socketStore = pool.create(STORE_NAME, "localhost", port, RequestFormatType.VOLDEMORT_V1, RequestRoutingType.NORMAL); factory = ServerTestUtils.getSocketRequestHandlerFactory(repository); socketService = ServerTestUtils.getSocketService(true, factory, port, numSelectors, 10, 1000, MAX_HEART_BEAT_MS); socketService.start(); } @After public void tearDown() throws IOException { this.pool.close(); this.socketService.stop(); } private GetClientRequest getClientRequest() { GetClientRequest clientRequest = new GetClientRequest(STORE_NAME, new RequestFormatFactory().getRequestFormat(dest1.getRequestFormatType()), RequestRoutingType.ROUTED, new ByteArray(new byte[] { 1, 2, 3 }), null); return clientRequest; } private NonblockingStoreCallback getCallBack() { final AtomicInteger cancelledEvents = new AtomicInteger(0); NonblockingStoreCallback callback = new NonblockingStoreCallback() { @Override public void requestComplete(Object result, long requestTime) { if (result instanceof UnreachableStoreException) cancelledEvents.incrementAndGet(); else fail("The request must have failed with UnreachableException" + result); } }; return callback; } @Test public void testSelectorHeartBeat() throws Exception { // Open a request so that Sleepy store hangs. pool.submitAsync(dest1, getClientRequest(), getCallBack(), 250, "get"); // wait for MAX heart beat + some more time so that the selector // threads should be marked dead Thread.sleep(MAX_HEART_BEAT_MS + 50); // submitAsync creates two connections if no connection is available when // the request is submitted. First when trying to get a connection for the // request, if not it calls proceesLoop which creates one more. try { pool.checkout(dest1); } catch (Exception ex) { } // If the server is started with single selector, all the requests to the server should // fail with IOException as the Server should close the connection because of not // receiving the heart beats // If the server is started with more than one selector, the requests should succeed // and should not be assigned to the hung selector. // Note that, this is just checking out a connection and not doing any operation on it // If an operation is attempted, the operation will time out as the store being used // here sleeps for infinite time for (int i = 0; i < 20; i++) { try { ClientRequestExecutor executor = pool.checkout(dest1); if (numSelectors == 1) { fail("Single selector expected an exception"); } else { assertNotNull("multiple selector should have valid executor", executor); } } catch (UnreachableStoreException ex) { if (numSelectors > 1) { fail("more than one selector, no exception should be observed" + ex); } else { assertTrue("inner exception should be instance of IOException", ex.getCause() instanceof IOException); } } } sleepyStore.releaseThreads(); } }