/* * JBoss, Home of Professional Open Source * Copyright 2010 Red Hat Inc. and/or its affiliates and other * contributors as indicated by the @author tags. All rights reserved. * See the copyright.txt in the distribution for a full listing of * individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.infinispan.client.hotrod; import org.apache.commons.pool.impl.GenericKeyedObjectPool; import org.infinispan.Cache; import org.infinispan.client.hotrod.impl.ConfigurationProperties; import org.infinispan.client.hotrod.impl.transport.tcp.TcpTransportFactory; import org.infinispan.commands.VisitableCommand; import org.infinispan.context.InvocationContext; import org.infinispan.interceptors.base.CommandInterceptor; import org.infinispan.server.hotrod.HotRodServer; import org.infinispan.test.MultipleCacheManagersTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.net.InetSocketAddress; import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import static org.testng.AssertJUnit.assertEquals; /** * @author Mircea.Markus@jboss.com * @since 4.1 */ @Test(testName = "client.hotrod.ClientConnectionPoolingTest", groups="functional") public class ClientConnectionPoolingTest extends MultipleCacheManagersTest { private static final Log log = LogFactory.getLog(ClientConnectionPoolingTest.class); Cache c1; Cache c2; private HotRodServer hotRodServer1; private HotRodServer hotRodServer2; RemoteCache<String, String> remoteCache; private RemoteCacheManager remoteCacheManager; private GenericKeyedObjectPool connectionPool; private InetSocketAddress hrServ1Addr; private InetSocketAddress hrServ2Addr; private WorkerThread workerThread1; private WorkerThread workerThread2; private WorkerThread workerThread3; private WorkerThread workerThread4; private WorkerThread workerThread5; private WorkerThread workerThread6; @Override protected void assertSupportedConfig() { return; } @Override protected void createCacheManagers() throws Throwable { // The caches are not configured to form a cluster // so the client will have to use round-robin for balancing. // This means requests will alternate between server 1 and server 2. c1 = TestCacheManagerFactory.createLocalCacheManager(false).getCache(); c2 = TestCacheManagerFactory.createLocalCacheManager(false).getCache(); registerCacheManager(c1.getCacheManager(), c2.getCacheManager()); hotRodServer1 = TestHelper.startHotRodServer(c1.getCacheManager()); hotRodServer2 = TestHelper.startHotRodServer(c2.getCacheManager()); String servers = TestHelper.getServersString(hotRodServer1, hotRodServer2); Properties hotrodClientConf = new Properties(); hotrodClientConf.put(ConfigurationProperties.SERVER_LIST, servers); hotrodClientConf.put("maxActive", 2); hotrodClientConf.put("maxTotal", 8); hotrodClientConf.put("maxIdle", 6); hotrodClientConf.put("whenExhaustedAction", 1); hotrodClientConf.put("testOnBorrow", "false"); hotrodClientConf.put("testOnReturn", "false"); hotrodClientConf.put("timeBetweenEvictionRunsMillis", "-2"); hotrodClientConf.put("minEvictableIdleTimeMillis", "7"); hotrodClientConf.put("testWhileIdle", "true"); hotrodClientConf.put("minIdle", "-5"); hotrodClientConf.put("lifo", "true"); hotrodClientConf.put("infinispan.client.hotrod.ping_on_startup", "false"); remoteCacheManager = new RemoteCacheManager(hotrodClientConf); remoteCache = remoteCacheManager.getCache(); TcpTransportFactory tcpConnectionFactory = (TcpTransportFactory) TestingUtil.extractField(remoteCacheManager, "transportFactory"); connectionPool = (GenericKeyedObjectPool) TestingUtil.extractField(tcpConnectionFactory, "connectionPool"); workerThread1 = new WorkerThread(remoteCache); workerThread2 = new WorkerThread(remoteCache); workerThread3 = new WorkerThread(remoteCache); workerThread4 = new WorkerThread(remoteCache); workerThread5 = new WorkerThread(remoteCache); workerThread6 = new WorkerThread(remoteCache); hrServ1Addr = new InetSocketAddress("localhost", hotRodServer1.getPort()); hrServ2Addr = new InetSocketAddress("localhost", hotRodServer2.getPort()); } @AfterMethod(alwaysRun = true) public void tearDown() throws ExecutionException, InterruptedException { hotRodServer1.stop(); hotRodServer2.stop(); workerThread1.stop(); workerThread2.stop(); workerThread3.stop(); workerThread4.stop(); workerThread5.stop(); workerThread6.stop(); workerThread1.awaitTermination(); workerThread2.awaitTermination(); workerThread3.awaitTermination(); workerThread4.awaitTermination(); workerThread5.awaitTermination(); workerThread6.awaitTermination(); remoteCacheManager.stop(); } @Test public void testPropsCorrectlySet() { assertEquals(2, connectionPool.getMaxActive()); assertEquals(8, connectionPool.getMaxTotal()); assertEquals(6, connectionPool.getMaxIdle()); assertEquals(1, connectionPool.getWhenExhaustedAction()); assertEquals(false, connectionPool.getTestOnBorrow()); assertEquals(false, connectionPool.getTestOnReturn()); assertEquals(-2, connectionPool.getTimeBetweenEvictionRunsMillis()); assertEquals(7, connectionPool.getMinEvictableIdleTimeMillis()); assertEquals(true, connectionPool.getTestWhileIdle()); assertEquals(-5, connectionPool.getMinIdle()); assertEquals(true, connectionPool.getLifo()); } public void testMaxActiveReached() throws Exception { workerThread1.put("k1", "v1"); workerThread1.put("k2", "v2"); // verify that each cache got a request assertEquals(1, c1.size()); assertEquals(1, c2.size()); assertEquals("v1", remoteCache.get("k1")); assertEquals(1, c1.size()); assertEquals("v2", remoteCache.get("k2")); assertEquals(1, c2.size()); // there should be no active connections to any server assertEquals(0, connectionPool.getNumActive(hrServ1Addr)); assertEquals(0, connectionPool.getNumActive(hrServ2Addr)); assertEquals(1, connectionPool.getNumIdle(hrServ1Addr)); assertEquals(1, connectionPool.getNumIdle(hrServ2Addr)); // install an interceptor that will block all requests on the server until the allow() call DelayTransportInterceptor dt1 = new DelayTransportInterceptor(true); DelayTransportInterceptor dt2 = new DelayTransportInterceptor(true); c1.getAdvancedCache().addInterceptor(dt1, 0); c2.getAdvancedCache().addInterceptor(dt2, 0); log.info("Cache operations blocked"); try { // start one operation on each server, using the existing connections workerThread1.putAsync("k3", "v3"); workerThread2.putAsync("k4", "v4"); log.info("Async calls for k3 and k4 is done."); // give the worker thread some time to start their requests Thread.sleep(100); assertEquals(1, connectionPool.getNumActive(hrServ1Addr)); assertEquals(1, connectionPool.getNumActive(hrServ2Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ1Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ2Addr)); // another operation for each server, creating new connections workerThread3.putAsync("k5", "v5"); workerThread4.putAsync("k6", "v6"); Thread.sleep(100); assertEquals(2, connectionPool.getNumActive(hrServ1Addr)); assertEquals(2, connectionPool.getNumActive(hrServ2Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ1Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ2Addr)); // we've reached the connection pool limit, the new operations will block // until a connection is released workerThread5.putAsync("k7", "v7"); workerThread6.putAsync("k8", "v8"); Thread.sleep(100); assertEquals(2, connectionPool.getNumActive(hrServ1Addr)); assertEquals(2, connectionPool.getNumActive(hrServ2Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ1Addr)); assertEquals(0, connectionPool.getNumIdle(hrServ2Addr)); } catch (Exception e) { log.error(e); } finally { //now allow dt1.allow(); dt2.allow(); } // give the servers some time to process the operations eventually(new Condition() { public boolean isSatisfied() throws Exception { return connectionPool.getNumActive() == 0; } }, 1000); assertExistKeyValue("k3", "v3"); assertExistKeyValue("k4", "v4"); assertExistKeyValue("k5", "v5"); assertExistKeyValue("k6", "v6"); assertExistKeyValue("k7", "v7"); assertExistKeyValue("k8", "v8"); // all the connections have been released to the pool, but haven't been closed assertEquals(0, connectionPool.getNumActive(hrServ1Addr)); assertEquals(0, connectionPool.getNumActive(hrServ2Addr)); assertEquals(2, connectionPool.getNumIdle(hrServ1Addr)); assertEquals(2, connectionPool.getNumIdle(hrServ2Addr)); } private void assertExistKeyValue(String key, String value) throws InterruptedException { boolean exists = false; for (int i = 0; i < 10; i++) { exists = value.equals(remoteCache.get(key)) || value.equals(remoteCache.get(key)); if (exists) break; Thread.sleep(1000); } assertEquals("key value not found: (" + key + ", " + value + ")", true, exists); } public static class DelayTransportInterceptor extends CommandInterceptor { private final ReentrantLock lock = new ReentrantLock(); public DelayTransportInterceptor(boolean lock) { if (lock) block(); } @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { log.trace("Acquiring lock. " + lockInfo()); lock.lock(); try { return super.handleDefault(ctx, command); } finally { log.trace("Done operation, releasing lock" + lockInfo()); lock.unlock(); } } private String lockInfo() { return " Is locked? " + lock.isLocked() + ". Lock held by me? " + lock.isHeldByCurrentThread(); } public void block() { log.trace("block. " + lockInfo()); lock.lock(); } public void allow() { log.trace("allow." + lockInfo()); lock.unlock(); } } }