<div dir="ltr">Hi Daniel,<div><br></div><div>Thank you for this reply.<br>In our case, the underlying thread pool doesn't typically become overloaded, but when it does, we’re forced to manually restart applications because the HttpClient becomes permanently stalled.</div><div><br></div><div>Why is it that tasks need to be executed? Dropping work can be an acceptable means of backpressure for a threadpool. Are there some tasks that the HttpClient submits to this delegate threadpool which are essential for the client to continue behaving correctly? And dropping such a task could cause the client to continue working, but in a potentially broken way?</div><div>If this is the case, it may be risky to apply my fix of switching to a DiscardPolicy. As this would allow the threadpool to silently drop work while keeping the http client open. I wonder if it would be safer to use your FJP idea to write a RejectedExceptionHandler that falls back to submitting rejected tasks there.</div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, Jun 18, 2025 at 10:42 AM Daniel Fuchs <<a href="mailto:daniel.fuchs@oracle.com">daniel.fuchs@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi David,<br>
<br>
As a general rule, the client doesn't expect tasks to<br>
be rejected by the executor. Tasks need to be executed,<br>
one way or another, for the client to work as expected.<br>
Executing in the current thread is often not an option<br>
as it may lead to deadlocks.<br>
So if we get a RejectedTaskExection, we shutdown the<br>
client, as there's not much else we can do.<br>
<br>
We have some contengency measure for shutdown in case of<br>
RejectedTaskException that will temporarily execute rejected<br>
tasks in the FJP instead when the executor rejects a task,<br>
but this is only a best effort whose only goal is to make<br>
it possible to complete the shutdown, and wakeup any calling<br>
code that may be waiting on send or completable futures returned<br>
by sendAsync.<br>
<br>
best regards,<br>
<br>
-- daniel<br>
<br>
On 18/06/2025 17:24, David Nadeau wrote:<br>
> When overriding the HttpClient executor with a custom ThreadPoolExecutor <br>
> that uses the default RejectedExecutionHandler.AbortPolicy, the Java <br>
> HTTP client (java.net.http.HttpClient) becomes permanently unusable if <br>
> the delegate executor rejects a task.<br>
> <br>
> This creates a situation where transient thread pool saturation results <br>
> in a non-recoverable client failure.<br>
> <br>
> Reproduction of the issue:<br>
> 1. The task rejection triggers a call to the error handler (onSubmitFailure)<br>
> 2. This calls selmgr.abort(failure) with the rejection exception<br>
> 3. SelectorManager.abort() sets this.closed = true permanently<br>
> 4. All subsequent HTTP operations fail with "IOException: selector <br>
> manager closed"<br>
> <br>
> The client does not recover from this state.<br>
> <br>
> I was able to avoid this by using the DiscardPolicy instead of the <br>
> AbortPolicy. However, this behavior was quite a surprise to debug. Is <br>
> this behavior intentional, or does it make sense for the HttpClient to <br>
> treat task rejection as a recoverable error?<br>
<br>
</blockquote></div>