8000 Pre-create the shutdown Thread by YannRobert · Pull Request #86 · rabbitmq/rabbitmq-java-client · GitHub
[go: up one dir, main page]

Skip to content

Pre-create the shutdown Thread #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

YannRobert
Copy link
Contributor

On some condition, we can get the following Error in the application logs

java.lang.OutOfMemoryError: unable to create new native thread
    at java.lang.Thread.start0(Native Method) ~[na:1.8.0_51-]
    at java.lang.Thread.start(Thread.java:714) ~[na:1.8.0_51-]
    at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950) ~[na:1.8.0_51-]
    at java.util.concurrent.ThreadPoolExecutor.processWorkerExit(ThreadPoolExecutor.java:1018) ~[na:1.8.0_51-]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160) ~[na:1.8.0_51-]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_51-]
    at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_51-] 

Investigations shows that the Thread being created is a Thread that will have to shutdown stuff in RabbitMQ Client.
And that the condition for this to occur, is the tomcat container being shutdown by Cloud Foundry.

It appears that the ThreadFactory cannot create new Native Threads while the container is being shutdown. At the same time, the application tries to shutdown allocated resources and running processes. So when RabbitMQ client tries to shutdown, it creates a new Thread that will do those shutdown tasks.
But as a new Native Thread cannot be created, the Threads fails to start, and the shutdown task is not executed.

As the condtion for this to occur is that the application and JVM is shutting down, this may sound like it's not a real issue. In fact, I am concerned by the OutOfMemoryError appearing in the logs. Logs are monitored and trigger alerts, so you don't want to see this in your logs unless it's a real problem.

As a proof of concept, I patched the RabbitMQ client to create the shutdown Thread as soon at the ConnectionFactory is created : ie a ThreadPool of size 1 is created with the ConnectionFactory, so that the native Thread is created and is ready to execute any task submited to it.
It turned out that this patch did fix the problem: there is no OOM Error at application shutdown anymore.

I don't know for sure if the fix is good enough for every cases.
Like, this patch makes a new Thread waiting for the whole life of the ConnectionFactory. As if you have multiple ConnectionFactories in your app, you have the same number of ThreadPools waiting for the shutdown task.
I guess a typical application would only have only 2 ConnectionFactories anyways (1 for consumers and 1 for producers) so this may not be a big overhead. And maybe we can rework the code to ensure we only use as much as 1 ThreadPool for every ConnectionFactories in an application ?

Hoping it's a good start anyways.

Please review and comment.

…allocated, in order to prevent an OOM when allocating new native thread
…nFactory) in order to have only 1 of it per ConnectionFactory, managing the shutdown of potentially multiple ChannelManagers. The ScheduledThreadPoolExecutor ensures a Thread is ready WAITING for a Task to be submitted.
@michaelklishin
Copy link
Contributor

@YannRobert thank you for your time. I feel the real solution is to bump Tomcat and OS limits so that your JVM can allocate new threads or use fewer connections (and more channels). One (temporary) shutdown thread per connection doesn't sound too bad.

The suggested implementation complicates things quite a bit for a pretty minor gain. I'd first explore the option with using the higher limit/fewer connections and only modify the client if that is absolutely impossible.

@YannRobert
Copy link
Contributor Author

Hi @michaelklishin

On some environment, like on public clouds providers, you cannot really fine tune OS limits and application resources.

In Cloud Foundry Public Clouds for instance, you will get the following limits

 time(seconds)        unlimited
 file(blocks)         unlimited
 data(kbytes)         unlimited
 stack(kbytes)        8192
 coredump(blocks)     0
 memory(kbytes)       unlimited
 locked memory(kbytes) 64
 process              512
 nofiles              16384
 vmemory(kbytes)      unlimited
 locks                unlimited 

for a 1 GB memory application.

When this is sufficient for the application to run and handle normal workload, you don't want to go to an upper plan (to get more resources) just so that the application can shutdown without an Error.

As for the number of connections, it is already really low, and cannot reasonably be lowered more without affecting throughput.

@michaelklishin
Copy link
Contributor

@YannRobert so you run on run.pivotal.io, correct?

@YannRobert
Copy link
Contributor Author

@michaelklishin I run on both api.run.pivotal.io and api.de.a9s.eu

@michaelklishin
Copy link
Contributor

Got it. The beauty of portability ;)

@dumbbell dumbbell added this to the n/a milestone Sep 9, 2015
stream-iori pushed a commit to stream-iori/rabbitmq-java-client that referenced this pull request 73E8 Mar 20, 2022
* optimize maven dependencies format (rabbitmq#86)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0