mongodb / mongodb/mongo-java-driver
Resource Leak: BufferPoolPruner thread is not stopped on MongoClient.close() when using Tailable Cursors on Capped Collections
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 2.7k
- Forks
- 1.5k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 10
Description
When using the MongoDB Java Driver within an Apache Tomcat environment, undeploying or reloading the web application triggers a severe memory leak warning regarding the BufferPoolPruner thread.
This issue specifically surfaces when the application utilizes a continuous listening mechanism (e.g., a Tailable Cursor with Await) on a Capped Collection. Calling MongoClient.close() during the application shutdown lifecycle (e.g., inside a ServletContextListener) fails to terminate this background thread. Because the thread is tied to the static PowerOfTwoBufferPool.DEFAULT instance, it remains actively running and holds references, preventing Tomcat from garbage collecting the webapp.
Steps to Reproduce:
- Deploy a web application using the MongoDB Java driver in Apache Tomcat.
- Initialize a MongoClient during application startup.
- Open a continuous query/listener using a Tailable Cursor (e.g., CursorType.TailableAwait) on a Capped Collection.
- Trigger the application shutdown phase and call MongoClient.close().
- Undeploy or reload the application via Tomcat Manager.
- Check the Tomcat logs.
Actual Results:
Tomcat reports the following severe warning preventing proper garbage collection of the WebappClassLoader:
"The web application [ROOT] appears to have started a thread named [BufferPoolPruner-1-thread-1] but has failed to stop it. This is very likely to create a memory leak."
Expected Behavior:
The driver should properly interrupt and clean up the BufferPoolPruner thread when MongoClient.close() is called, even if tailable cursors or background listeners were active on capped collections. Alternatively, a public API method should be provided to gracefully shut down static pool threads upon container undeployment.
Current Workaround:
To prevent the memory leak on undeploy, we currently have to rely on reflection to access driver internals and explicitly call the disablePruning() method on the static pool to stop the thread:
/**
* The driver owns a static PowerOfTwoBufferPool.DEFAULT which starts a "BufferPoolPruner" thread.
* When using tailable cursors on capped collections, MongoClient.close() does not stop it,
* causing Tomcat to report a leak on undeploy.
* Both the field holder and disablePruning() are driver internals, requiring reflection.
*/
private static void stopBufferPoolPruner() {
try {
Class<?> poolClass = Class.forName("com.mongodb.internal.connection.PowerOfTwoBufferPool");
Object pool = poolClass.getField("DEFAULT").get(null);
java.lang.reflect.Method disablePruning = poolClass.getDeclaredMethod("disablePruning");
disablePruning.setAccessible(true);
disablePruning.invoke(pool);
} catch (Exception e) {
Fx.log("disablePruning---->");
e.printStackTrace();
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing MongoClient.close() and the tailable-cursor shutdown path to com.mongodb.internal.connection.PowerOfTwoBufferPool and its DEFAULT instance. Reproduce the Tomcat undeploy scenario with a TailableAwait cursor, then verify that closing the client stops BufferPoolPruner-1-thread-1 without reflection and removes the classloader leak warning.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, mongodb
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100