bbottema / simple-java-mail

Simple API, Complex Emails (Jakarta Mail smtp wrapper)

Home Page:http://www.simplejavamail.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Implement more comprehensive ThreadPoolExecutor and expose config options

bbottema opened this issue · comments

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.

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.

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.

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.

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

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.

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

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.

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?

@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 👍

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!

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!

Done. Will be in the 6.0.0 release!

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

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

@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!

6.0.0 has released as well, finally.