[ 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)