[ 
https://issues.apache.org/jira/browse/POOL-404?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17480759#comment-17480759
 ] 

Patrick Barry commented on POOL-404:
------------------------------------

Here is the test I extracted. This definitely demonstrates the confusing 
logging statement with incrementing pool #'s, but surprisingly does not show 
the evictor thread still running. Trying to figure out what is different with 
our mainline code.

Lettuce 6.1.6Release, and latest generic pool 2.11.1

Startup a local running redis server. 
{code:java}
package com.moproblems.core.cache.redis.client;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.resource.DefaultClientResources;
import io.lettuce.core.resource.Delay;
import io.lettuce.core.resource.DirContextDnsResolver;
import io.lettuce.core.support.ConnectionPoolSupport;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timer;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static java.time.Duration.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

public class PoolProblem {
    static Logger logger = LoggerFactory.getLogger(PoolProblem.class);

    public static void main(String[] args) throws InterruptedException {
        RedisURI elasticacheUri = RedisURI.Builder.redis("localhost", 6379)
                .build();

        Timer timer = new HashedWheelTimer(new 
DefaultThreadFactory("my-lettuce-timer", true));
        RedisClient redisClient = 
RedisClient.create(DefaultClientResources.builder()
                .ioThreadPoolSize(8)
                .computationThreadPoolSize(8)
                .dnsResolver(new DirContextDnsResolver())
                .reconnectDelay(Delay.exponential(0, 1500, MILLISECONDS, 2))
                .timer(timer)
                .build(), elasticacheUri);

        redisClient.setOptions(ClientOptions.builder()
                .autoReconnect(true)
                .cancelCommandsOnReconnectFailure(true)
                
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .pingBeforeActivateConnection(true)
                .requestQueueSize(10)
                .build());

        GenericObjectPoolConfig<StatefulRedisConnection<String, String>> config 
= new GenericObjectPoolConfig<>();
        config.setMinIdle(2);
        config.setMaxTotal(10);
        config.setMaxIdle(10);
        config.setMaxWait(ofSeconds(1));
        config.setMinEvictableIdleTime(ofMinutes(5));
        config.setTestOnBorrow(true);
        config.setTimeBetweenEvictionRuns(ofMinutes(5));

        List<Callable<String>> tasks = new ArrayList<>();
        GenericObjectPool<StatefulRedisConnection<String, String>> pool = 
ConnectionPoolSupport.createGenericObjectPool(redisClient::connect, config);

        for (int i = 0; i < 5; i++) {
            ExecutorService execService = Executors.newFixedThreadPool(25);
            for (int count = 0; count < 100; count++) {
                Callable<String> c = () -> {
                    try (StatefulRedisConnection<String, String> connection = 
pool.borrowObject()) {
                        logger.info("Running test on pool {}, connection {}", 
System.identityHashCode(pool), System.identityHashCode(connection));
                        Thread.sleep(100);
                        return connection.sync().set("key", "100");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    return "{}";
                };
                tasks.add(c);
            }
            List<Future<String>> results = execService.invokeAll(tasks);
            logger.info("Completed {} tests", results.size());
            execService.shutdownNow();
        }
        logger.info("Done");

        timer.stop();
        pool.close();

        redisClient.shutdown(ZERO, Duration.ofSeconds(20));
        List<String> threadsStillAlive = new ArrayList<>();
        for (Thread t : Thread.getAllStackTraces().keySet()) {
            if (t.getName().contains("lettuce-") || 
t.getName().contains("pool")) {
                threadsStillAlive.add(t.getName());
            }
        }
        logger.info("Threads still alive after attempted shutdown: {}", 
threadsStillAlive);
    }
}
 {code}

> No way to close evictor thread
> ------------------------------
>
>                 Key: POOL-404
>                 URL: https://issues.apache.org/jira/browse/POOL-404
>             Project: Commons Pool
>          Issue Type: Bug
>    Affects Versions: 2.11.1
>            Reporter: Patrick Barry
>            Priority: Major
>
> Using GenericObjectPool<StatefulRedisConnection<String, String>> to help with 
> lettuce client/redis connection management.   I have everything shutting down 
> cleanly except the commons-pool-evictor.  I see this problem has been 
> reported many times in the past, but all the changes are still not allowing 
> this thread to shut down cleanly on close.  I am using version 2.11.1.  I 
> have tried to code around this issue, but because EvictionTimer.java is so 
> locked down, there is very little that can done to change the behavior of how 
> this class interacts with GenericObjectPool.
> For this thread to shutdown, the taskMap has to be empty, which it never is 
> in my case. So even though we call close() on the pool, this class fails to 
> shutdown the embedded executor because it thinks it has more tasks.
> Looking at this code, it did remove 1 entry from taskMap, but we had many 
> more in that map. Is there a way to clear this map, so it will allow this 
> thread/executor to shutdown? 
> {code:java}
> static synchronized void cancel(final BaseGenericObjectPool<?>.Evictor 
> evictor, final Duration timeout,
>         final boolean restarting) {
>     if (evictor != null) {
>         evictor.cancel();   //why does this not interrupt!?
>         remove(evictor);    
>     }
>     if (!restarting && executor != null && taskMap.isEmpty()) {  //<-- How do 
> you force taskMap to be empty!?
>         executor.shutdown();
>         try {
>             executor.awaitTermination(timeout.toMillis(), 
> TimeUnit.MILLISECONDS);
>         } catch (final InterruptedException e) {
>             // Swallow
>             // Significant API changes would be required to propagate this
>         }
>         executor.setCorePoolSize(0);
>         executor = null;
>     }
> }{code}
> }
> I had all these entries in the taskMap when trying to shut down:
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@73d4066e
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@3c69362a
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@2412a42b
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@45404d5
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@29138d3a
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@5cbe2654
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@6dbcf214
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@496a31da
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@7c251f90
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@51841ac6
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@5ba26eb0
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@435e60ff
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@17d32e9b
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@66f0548d
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@2e6f610d
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@1e86a5a7
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@10afe71a
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@741f8dbe
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@212dfd39
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@a2ddf26
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@49ede9c7
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@65d57e4e
> org.apache.commons.pool2.impl.BaseGenericObjectPool$Evictor@6daf7d37
> *Method calls:*
> pool.close() ->
> stopEvictor();  -> 
> startEvictor(Duration.ofMillis(-1L)); -> 
> EvictionTimer.cancel(evictor, evictorShutdownTimeoutDuration, false); ->
>  



--
This message was sent by Atlassian Jira
(v8.20.1#820001)

Reply via email to