Skip to content
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

Implement more comprehensive ThreadPoolExecutor and expose config options #207

Closed
bbottema opened this issue Apr 27, 2019 · 16 comments
Closed

Comments

@bbottema
Copy link
Owner

bbottema commented Apr 27, 2019

This is the new path for the discussions fragmented over #204, #205 and #206. Paging Mr. @amanteaux.

Let's continue our threading discussion here? I really appreciate your input and you have already brought up some interesting points. And also, because of you I did some research and now know (again?) about some cool new things like BlockingQueue, SynchronousQueue and TransferQueue 👊

I backported the changes to "207-configurable-threadpoolexecutor" branched from develop, so we can implement this for 6.0.0.

@amanteaux
Copy link
Contributor

Thank you for following this subject @bbottema !

As you ask in the SO question, I am not sure allowing to configure the max number of threads in the pool is necessary:

I actually already encounter a similar use case when I had some issues with the default implementation of ThreadPoolExecutor. I needed the thread pool to reuse already created core threads instead of creating new ones until maxCoreThreads is reached (but also I didn't wanted core threads to expire). I eventually used the solution 2 proposed in https://reachmnadeem.wordpress.com/2017/01/15/scalable-java-tpe/
That worked perfectly in my project, https://github.com/Coreoz/Wisp, because I don't need to configure the thread pool rejection policy.
But in the case of Simple Java Mail, I am not sure adding an additional thread pool class in the project is worth it.

bbottema added a commit that referenced this issue Apr 28, 2019
- Configuring ThreadPoolExecutor now with separate config options (thread pool core size, max size and keepAliveTime)
- Added builder API for executor config options, including Javadoc
- Added support for application.properties for these options, included properties in the web documentation
@bbottema
Copy link
Owner Author

bbottema commented Apr 28, 2019

I pushed the changes which includes full configuration wiring, properties and builder API.

either core threads can't expire: in this case we sort of need to use a bounded queue to use max threads, see https://reachmnadeem.wordpress.com/2017/01/15/scalable-java-tpe/ for details.

Why is a bounded queue needed here, @amanteaux? It is my understanding that number of threads can't exceed max threads. The fact they don't expire only means the threads are kept around for handling new tasks, not that you would get potentially unlimited threads resulting in an OutOfMemoryException. I also don't want to reject tasks that can't be picked up immediately because all threads are busy.

@bbottema
Copy link
Owner Author

bbottema commented Apr 28, 2019

Also, I'm thinking here that perhaps Simple Java Mail should offer API that accepts a custom BlockingQueue so advanced use cases are made possible such as maximising email processing without causes starvation, Out Of Memory, or rejected tasks (producers matching consumer throughput).

Admitted this would only be useful for bulk users that generate a lot of new addresses or new emails.

@amanteaux
Copy link
Contributor

amanteaux commented Apr 28, 2019

Why is a bounded queue needed here

You are right, the number of threads can't exceed max threads. However, for the thread pool to start using new threads from coreThreads to maxThreads, the queue needs to reject new tasks: https://stackoverflow.com/questions/15485840/threadpoolexecutor-with-unbounded-queue-not-creating-new-threads

@bbottema
Copy link
Owner Author

bbottema commented Apr 28, 2019

Wow, did not know that! And also I don't understand it. If a queue refuses a task (because it's bounded), new threads will be made, even though the queue is already full? Won't it just reject the task again?

Sigh, the more I read and discover about ThreadPoolExecutors, the less I understand about it it seems :/

Maybe we need a SimpleThreadPoolExecutor project to accompany SimpleJavaMail.

I'm going to take a step back from this topic for for a few days. If you have some good ideas, by all means, use this branch, @amanteaux.

@amanteaux
Copy link
Contributor

Hehe I completely agree, the default thread pool executor behavior is counter intuitive. I think that tasks will start being rejected once the queue is full and at the same time max threads is reached.

Though in SimpleJavaMail, I am not sure that providing more support than just configuring the max thread pool size + timeout makes a lot of sense. For very advance usages, it is possible to use synchronous sending and using a custom thread pool. I am leaving to you the measurement of the ratio: (benefits added by creating a custom thread pool + providing ways to configure it) / added complexity

@bbottema
Copy link
Owner Author

Makes sense. Perhaps just offering the option to provide a custom ThreadPoolExecutor covers all the cases leaving it to the users to decide if they need more advanced processing.

bbottema added a commit that referenced this issue May 24, 2019
…ExecutorService (ThreadPoolExecutor), with a default keepAliveTime of 1ms, allowing core threads to die idly as well
@bbottema
Copy link
Owner Author

bbottema commented May 24, 2019

Hi @amanteaux, I finally got around to this topic again. I think I've finished the changes we had in mind:

  1. Kept to a single pool size property, applied to both core and max size
  2. Allowing a keepAliveTime to be set through the mailer builder API, with a default of 1ms (reasoning below)
  3. Allowing a complete custom Executor Service (ThreadPoolExecutor) to be set through the mailer builder API for maximum freedom for advanced users

I've set the default keepAliveTime to the smallest non-zero number, so that it kills off idle threads immediately keeping the JVM free to shut down without delay. I'm not sure what benefit there is to keep the threads alive for possible chained batches. Anyway new threads will spin up when needed. I figured this setup comes closest to the old behavior, except with less configuration and pool shutdowns/boots.

Perhaps you can take a look?

@bbottema
Copy link
Owner Author

bbottema commented May 29, 2019

@amanteaux If you agree that this should behave pretty much the same as the old setup, I'll merge it to develop so I can continue on other stories. This is kinda in the way now :D

I do feel this simplified configuration in the MailSender 👍

@amanteaux
Copy link
Contributor

Hello @bbottema, sorry for the late answer! I will try to have a quick look at it this afternoon. It sounds good anyway. Thank you a lot for this!

@amanteaux
Copy link
Contributor

Hi @bbottema, it looks good! The thread pool options should cover 99% of the use cases, and the option to provide the custom thread pool will cover the 1% remaining use cases. I don't think we can expect more from Simple Java Mail. Thank you!

bbottema added a commit that referenced this issue May 30, 2019
@bbottema
Copy link
Owner Author

Done. Will be in the 6.0.0 release!

@bbottema
Copy link
Owner Author

@amanteaux, released in 6.0.0-rc1!! Check out the new website...

@amanteaux
Copy link
Contributor

@bbottema Thank you!! It looks really good :)
I will try to use this new version in our framework!

@bbottema
Copy link
Owner Author

@bbottema Thank you!! It looks really good :)
I will try to use this new version in our framework!

Great, let me know if you run into anything!

@bbottema
Copy link
Owner Author

6.0.0 has released as well, finally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants