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

Thomas Vandahl commented on JCS-248:
------------------------------------

Regarding 1. and 2.: There is only one ElementEventQueue in JCS (see 
CompositeCacheManager), so this should not be an issue. Actually, the explicit 
call to disposeExecutorService is now indeed obsolete as this will be handled 
by the ThreadPoolManager lifecycle as intended.

Your hint pointed me to another problem, however. In CompositeCache the central 
instance of ElementEventQueue is injected but dispose() shuts it down for every 
cache instance. This is clearly wrong.

> ElementEventQueue.dispose() does not shut down its owned executor, leaking 
> threads across web application redeployments
> -----------------------------------------------------------------------------------------------------------------------
>
>                 Key: JCS-248
>                 URL: https://issues.apache.org/jira/browse/JCS-248
>             Project: Commons JCS
>          Issue Type: Bug
>          Components: Composite Cache
>    Affects Versions: jcs-3.2.1, jcs-4.0
>         Environment: Apache Commons JCS 3.2.1; Apache Tomcat 11.0.24; Eclipse 
> Temurin JDK 25; Spring web application packaged as a WAR; local 
> non-distributed cache.
>            Reporter: Thomas Buffagni
>            Assignee: Thomas Vandahl
>            Priority: Major
>              Labels: thread-leak,, tomcat,, webapp-lifecycle
>             Fix For: jcs-4.0
>
>
> *How the issue was discovered*
> The issue was discovered while running a Tomcat web-application lifecycle 
> benchmark. The benchmark repeatedly performs the following sequence:
> 1. Deploy the Spring WAR.
> 2. Initialize and exercise the local JCS cache.
> 3. Stop the Spring application context and invoke JCS.shutdown().
> 4. Undeploy the WAR from Tomcat.
> 5. Deploy it again and repeat the sequence.
> During WAR undeployment, Tomcat reported that threads created by the web 
> application had not been stopped. The threads named in the warnings were 
> JCS-ElementEventQueue-* workers.
> Inspection of the JVM after undeployment confirmed that two 
> JCS-ElementEventQueue-* worker threads remained alive even though the 
> application had been stopped and JCS.shutdown() had been invoked.
> Repeating the deploy/undeploy cycle caused additional worker threads to 
> accumulate and produced further Tomcat thread-leak warnings. After five 
> lifecycle cycles, ten warnings had been recorded and the number of live 
> threads showed an estimated growth of 2.3 threads per cycle.
> This undeployment behavior led to the inspection of 
> ElementEventQueue.dispose() and to the identification of the executor 
> lifecycle problem described below.
> *Problem*
> ElementEventQueue creates its own executor by calling:
> ThreadPoolManager.getInstance().createPool(...)
> The returned executor is not registered in the ThreadPoolManager internal 
> pool maps. Consequently, ThreadPoolManager.dispose() cannot shut it down.
> ElementEventQueue.dispose() sets the destroyed flag, but the 
> queueProcessor.shutdownNow() call is commented out. When JCS is used inside a 
> Tomcat web application, the executor threads survive application undeployment.
> The issue was reproduced at runtime with JCS 3.2.1. Code inspection confirms 
> that the same lifecycle problem is present in the current JCS 4.0.0-SNAPSHOT 
> source.
> *Steps to reproduce*
> 1. Deploy a Spring WAR that configures and uses a local JCS cache.
> 2. Execute a workload that creates the ElementEventQueue workers.
> 3. Invoke JCS.shutdown() while stopping the Spring application context.
> 4. Undeploy the WAR from Tomcat.
> 5. Deploy the WAR again and repeat the lifecycle cycle.
> 6. Inspect the Tomcat logs and live JVM threads after each undeployment.
> *Actual result*
> Two additional JCS-ElementEventQueue-* threads remain alive after each 
> application lifecycle cycle.
> In a five-cycle deploy/workload/undeploy test with JCS 3.2.1, the unpatched 
> implementation produced:
> - 10 Tomcat thread-leak warnings
> - final thread counts of 39, 41, 43, 46, and 48
> - an estimated thread-count slope of +2.3 threads per cycle
> *Expected result*
> ElementEventQueue.dispose() should terminate the executor owned by the queue. 
> No JCS-ElementEventQueue-* worker should remain alive after JCS shutdown and 
> WAR undeployment.
> *Root cause*
> ElementEventQueue obtains a newly created and unregistered executor from 
> ThreadPoolManager.createPool(). Because the queue owns this executor, it must 
> also terminate it explicitly.
> *Proposed fix*
> Call queueProcessor.shutdownNow() during the first execution of 
> ElementEventQueue.dispose().
> A regression test verifies that:
> - the worker thread is running before disposal
> - the worker terminates after disposal
> - repeated calls to dispose() remain safe
> *Validation*
> The patched JCS 3.2.1 JAR was built from source, packaged inside the test 
> WAR, and tested through five complete Tomcat deploy/workload/undeploy cycles.
> *Results after the patch:*
> - 0 Tomcat thread-leak warnings
> - final thread counts of 38, 38, 37, 37, and 38
> - an estimated thread-count slope of -0.1 threads per cycle
> The corresponding focused regression test also passes against the JCS 4 
> source tree.
> A pull request containing the fix and regression test will be submitted after 
> this issue provides the JCS issue identifier.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to