Search in sources :

Example 1 with PoolStats

use of com.linkedin.pinot.transport.metrics.PoolStats in project pinot by linkedin.

the class NettySingleConnectionIntegrationTest method testServerShutdownLeak.

/*
   * This test attempts to use the connection mechanism the same way as ScatterGatherImpl.SingleRequestHandler does.
   *
   * WARNING: This test has potential failures due to timing.
   */
@Test
public void testServerShutdownLeak() throws Exception {
    final NettyClientMetrics metric = new NettyClientMetrics(null, "abc");
    final Timer timer = new HashedWheelTimer();
    final int minConns = 2;
    final int maxConns = 3;
    // 10M ms.
    final int maxIdleTimeoutMs = 10000000;
    final int maxBacklogPerServer = 1;
    MyServer server = new MyServer();
    Thread.sleep(1000);
    // used as a key to pool. Can be anything.
    final String serverName = "SomeServer";
    final ServerInstance serverInstance = server.getServerInstance();
    final MetricsRegistry metricsRegistry = new MetricsRegistry();
    EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    PooledNettyClientResourceManager resourceManager = new PooledNettyClientResourceManager(eventLoopGroup, new HashedWheelTimer(), metric);
    ExecutorService executorService = Executors.newCachedThreadPool();
    ScheduledExecutorService timeoutExecutor = new ScheduledThreadPoolExecutor(5);
    AsyncPoolResourceManagerAdapter<ServerInstance, NettyClientConnection> rmAdapter = new AsyncPoolResourceManagerAdapter<ServerInstance, NettyClientConnection>(serverInstance, resourceManager, executorService, metricsRegistry);
    KeyedPool<ServerInstance, NettyClientConnection> keyedPool = new KeyedPoolImpl<ServerInstance, NettyClientConnection>(minConns, maxConns, maxIdleTimeoutMs, maxBacklogPerServer, resourceManager, timeoutExecutor, executorService, metricsRegistry);
    resourceManager.setPool(keyedPool);
    keyedPool.start();
    Field keyedPoolMap = KeyedPoolImpl.class.getDeclaredField("_keyedPool");
    keyedPoolMap.setAccessible(true);
    KeyedFuture<ServerInstance, NettyClientConnection> keyedFuture = keyedPool.checkoutObject(serverInstance);
    // The connection pool for this server is created on demand, so we can now get a reference to the _keyedPool.
    // The act of calling checkoutObject() creates a new AsyncPoolImpl and places a request for a new connection.
    // Since no new connections are available in the beginning, we always end up creating one more than the min.
    Map<ServerInstance, AsyncPool<NettyClientConnection>> poolMap = (Map<ServerInstance, AsyncPool<NettyClientConnection>>) keyedPoolMap.get(keyedPool);
    AsyncPool<NettyClientConnection> asyncPool = poolMap.get(serverInstance);
    Field waiterList = AsyncPoolImpl.class.getDeclaredField("_waiters");
    waiterList.setAccessible(true);
    LinkedDequeue queue = (LinkedDequeue) waiterList.get(asyncPool);
    PoolStats stats;
    // If the number of waiters is = 0, then we will error out because the min connections may not have completed
    // by the time we check one out. If maxWaiters is > 0, then we may end up initiating a fresh connection while the
    // min is still being filled. So, best to sleep a little to make sure that the min pool size is filled out, so that
    // the stats are correct.
    Thread.sleep(2000L);
    stats = asyncPool.getStats();
    Assert.assertEquals(stats.getIdleCount(), minConns);
    Assert.assertEquals(stats.getPoolSize(), minConns + 1);
    NettyClientConnection conn = keyedFuture.getOne();
    LOGGER.debug("Got connection ID " + conn.getConnId());
    Assert.assertEquals(stats.getIdleCount(), minConns);
    Assert.assertEquals(stats.getPoolSize(), minConns + 1);
    // Now get two more connections to the server, since we have 2 idle, we should get those.
    // And leak them.
    keyedFuture = keyedPool.checkoutObject(serverInstance);
    conn = keyedFuture.getOne();
    LOGGER.debug("Got connection ID " + conn.getConnId());
    keyedFuture = keyedPool.checkoutObject(serverInstance);
    conn = keyedFuture.getOne();
    LOGGER.debug("Got connection ID " + conn.getConnId());
    // Now we should have 0 idle, and a pool size of 3 with no waiters.
    stats = asyncPool.getStats();
    Assert.assertEquals(stats.getIdleCount(), 0);
    Assert.assertEquals(stats.getPoolSize(), minConns + 1);
    Assert.assertEquals(queue.size(), 0);
    // Now, we will always get an exception because we don't have a free connection to the server.
    {
        keyedFuture = keyedPool.checkoutObject(serverInstance);
        boolean caughtException = false;
        LOGGER.debug("Will never get a connection here.");
        try {
            conn = keyedFuture.getOne(3, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            caughtException = true;
        }
        Assert.assertTrue(caughtException);
        keyedFuture.cancel(true);
    }
    // Now if the server goes down, we should release all three connections and be able to get a successful new connection
    LOGGER.info("Shutting down server instance");
    server.shutdown();
    // Give it time to clean up on the client side.
    Thread.sleep(2000L);
    stats = asyncPool.getStats();
    LOGGER.debug(stats.toString());
    // There will be a couple in idleCount in error state.
    Assert.assertEquals(stats.getIdleCount(), minConns);
    Assert.assertEquals(stats.getPoolSize(), minConns);
    LOGGER.debug("Restarting server instance");
    server.restart();
    Thread.sleep(3000);
    LOGGER.debug("Server restart successful\n" + asyncPool.getStats());
    // Now get 3 connections successfully
    for (int i = 0; i < 3; i++) {
        keyedFuture = keyedPool.checkoutObject(serverInstance);
        conn = keyedFuture.getOne();
        Assert.assertNotNull(conn);
    }
    server.shutdown();
}
Also used : ScheduledThreadPoolExecutor(java.util.concurrent.ScheduledThreadPoolExecutor) Field(java.lang.reflect.Field) ServerInstance(com.linkedin.pinot.common.response.ServerInstance) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) TimeoutException(java.util.concurrent.TimeoutException) MetricsRegistry(com.yammer.metrics.core.MetricsRegistry) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) NettyClientMetrics(com.linkedin.pinot.transport.metrics.NettyClientMetrics) AsyncPoolResourceManagerAdapter(com.linkedin.pinot.transport.pool.AsyncPoolResourceManagerAdapter) LinkedDequeue(com.linkedin.pinot.transport.common.LinkedDequeue) HashedWheelTimer(io.netty.util.HashedWheelTimer) KeyedPoolImpl(com.linkedin.pinot.transport.pool.KeyedPoolImpl) PoolStats(com.linkedin.pinot.transport.metrics.PoolStats) EventLoopGroup(io.netty.channel.EventLoopGroup) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) HashedWheelTimer(io.netty.util.HashedWheelTimer) Timer(io.netty.util.Timer) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ExecutorService(java.util.concurrent.ExecutorService) Map(java.util.Map) AsyncPool(com.linkedin.pinot.transport.pool.AsyncPool) Test(org.testng.annotations.Test)

Example 2 with PoolStats

use of com.linkedin.pinot.transport.metrics.PoolStats in project pinot by linkedin.

the class NettySingleConnectionIntegrationTest method testValidatePool.

/*
   * WARNING: This test has potential failures due to timing.
   */
@Test
public void testValidatePool() throws Exception {
    NettyClientMetrics metric = new NettyClientMetrics(null, "abc");
    Timer timer = new HashedWheelTimer();
    MyServer server = new MyServer();
    Thread.sleep(1000);
    // used as a key to pool. Can be anything.
    final String serverName = "SomeServer";
    ServerInstance serverInstance = server.getServerInstance();
    MetricsRegistry metricsRegistry = new MetricsRegistry();
    EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    PooledNettyClientResourceManager resourceManager = new PooledNettyClientResourceManager(eventLoopGroup, new HashedWheelTimer(), metric);
    ExecutorService executorService = Executors.newCachedThreadPool();
    ScheduledExecutorService timeoutExecutor = new ScheduledThreadPoolExecutor(5);
    AsyncPoolResourceManagerAdapter<ServerInstance, NettyClientConnection> rmAdapter = new AsyncPoolResourceManagerAdapter<ServerInstance, NettyClientConnection>(serverInstance, resourceManager, executorService, metricsRegistry);
    AsyncPool pool = new AsyncPoolImpl<NettyClientConnection>(serverName, rmAdapter, /*maxSize=*/
    5, /*idleTimeoutMs=*/
    100000, timeoutExecutor, executorService, /*maxWaiters=*/
    10, AsyncPoolImpl.Strategy.LRU, /*minSize=*/
    2, metricsRegistry);
    pool.start();
    Callback<NoneType> callback;
    callback = new Callback<NoneType>() {

        @Override
        public void onSuccess(NoneType arg0) {
        }

        @Override
        public void onError(Throwable arg0) {
            Assert.fail("Shutdown error");
        }
    };
    boolean serverShutdown = false;
    try {
        PoolStats stats;
        /* Validate with no connection in pool */
        // Give the pool enough time to create connections (in this case, 2 connections minSize)
        Thread.sleep(3000);
        //      System.out.println("Validating with no used objects in the pool");
        pool.validate(false);
        // System.out.println(stats);
        stats = pool.getStats();
        Assert.assertEquals(2, stats.getPoolSize());
        Assert.assertEquals(0, stats.getTotalBadDestroyed());
        /* checkout one connection, it should not destroy anything */
        AsyncResponseFuture<ServerInstance, NettyClientConnection> future = new AsyncResponseFuture<ServerInstance, NettyClientConnection>(serverInstance, "Future for " + serverName);
        Cancellable cancellable = pool.get(future);
        future.setCancellable(cancellable);
        NettyClientConnection conn = future.getOne();
        // System.out.println(stats);
        stats = pool.getStats();
        //      System.out.println("Validating with one used object in the pool");
        pool.validate(false);
        Assert.assertEquals(2, stats.getPoolSize());
        Assert.assertEquals(0, stats.getTotalBadDestroyed());
        Assert.assertEquals(1, stats.getCheckedOut());
        // Now stop the server, so that the checked out connection is invalidated.
        server.shutdown();
        serverShutdown = true;
        ;
        // Wait for the client channel to be closed.
        Thread.sleep(2000);
        pool.validate(false);
        // Wait for the callback into AsyncPoolImpl after the destroy thread completes destroying the connection
        Thread.sleep(5000);
        //      System.out.println("Validating with one used object in the pool, after server shutdown");
        // System.out.println(stats);
        stats = pool.getStats();
        Assert.assertEquals(2, stats.getPoolSize());
        Assert.assertEquals(1, stats.getTotalBadDestroyed());
        Assert.assertEquals(1, stats.getCheckedOut());
    } finally {
        server.shutdown();
        pool.shutdown(callback);
        executorService.shutdown();
        timeoutExecutor.shutdown();
    }
}
Also used : ScheduledThreadPoolExecutor(java.util.concurrent.ScheduledThreadPoolExecutor) Cancellable(com.linkedin.pinot.transport.common.Cancellable) ServerInstance(com.linkedin.pinot.common.response.ServerInstance) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) MetricsRegistry(com.yammer.metrics.core.MetricsRegistry) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) NettyClientMetrics(com.linkedin.pinot.transport.metrics.NettyClientMetrics) AsyncPoolResourceManagerAdapter(com.linkedin.pinot.transport.pool.AsyncPoolResourceManagerAdapter) NoneType(com.linkedin.pinot.transport.common.NoneType) HashedWheelTimer(io.netty.util.HashedWheelTimer) AsyncResponseFuture(com.linkedin.pinot.transport.common.AsyncResponseFuture) PoolStats(com.linkedin.pinot.transport.metrics.PoolStats) EventLoopGroup(io.netty.channel.EventLoopGroup) NioEventLoopGroup(io.netty.channel.nio.NioEventLoopGroup) HashedWheelTimer(io.netty.util.HashedWheelTimer) Timer(io.netty.util.Timer) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) ExecutorService(java.util.concurrent.ExecutorService) AsyncPoolImpl(com.linkedin.pinot.transport.pool.AsyncPoolImpl) AsyncPool(com.linkedin.pinot.transport.pool.AsyncPool) Test(org.testng.annotations.Test)

Aggregations

ServerInstance (com.linkedin.pinot.common.response.ServerInstance)2 NettyClientMetrics (com.linkedin.pinot.transport.metrics.NettyClientMetrics)2 PoolStats (com.linkedin.pinot.transport.metrics.PoolStats)2 AsyncPool (com.linkedin.pinot.transport.pool.AsyncPool)2 AsyncPoolResourceManagerAdapter (com.linkedin.pinot.transport.pool.AsyncPoolResourceManagerAdapter)2 MetricsRegistry (com.yammer.metrics.core.MetricsRegistry)2 EventLoopGroup (io.netty.channel.EventLoopGroup)2 NioEventLoopGroup (io.netty.channel.nio.NioEventLoopGroup)2 HashedWheelTimer (io.netty.util.HashedWheelTimer)2 Timer (io.netty.util.Timer)2 ExecutorService (java.util.concurrent.ExecutorService)2 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)2 ScheduledThreadPoolExecutor (java.util.concurrent.ScheduledThreadPoolExecutor)2 Test (org.testng.annotations.Test)2 AsyncResponseFuture (com.linkedin.pinot.transport.common.AsyncResponseFuture)1 Cancellable (com.linkedin.pinot.transport.common.Cancellable)1 LinkedDequeue (com.linkedin.pinot.transport.common.LinkedDequeue)1 NoneType (com.linkedin.pinot.transport.common.NoneType)1 AsyncPoolImpl (com.linkedin.pinot.transport.pool.AsyncPoolImpl)1 KeyedPoolImpl (com.linkedin.pinot.transport.pool.KeyedPoolImpl)1