Hi David,
As a general rule, the client doesn't expect tasks to
be rejected by the executor. Tasks need to be executed,
one way or another, for the client to work as expected.
Executing in the current thread is often not an option
as it may lead to deadlocks.
So if we get a RejectedTaskExection, we shutdown the
client, as there's not much else we can do.
We have some contengency measure for shutdown in case of
RejectedTaskException that will temporarily execute rejected
tasks in the FJP instead when the executor rejects a task,
but this is only a best effort whose only goal is to make
it possible to complete the shutdown, and wakeup any calling
code that may be waiting on send or completable futures returned
by sendAsync.
best regards,
-- daniel
On 18/06/2025 17:24, David Nadeau wrote:
When overriding the HttpClient executor with a custom ThreadPoolExecutor
that uses the default RejectedExecutionHandler.AbortPolicy, the Java
HTTP client (java.net.http.HttpClient) becomes permanently unusable if
the delegate executor rejects a task.
This creates a situation where transient thread pool saturation results
in a non-recoverable client failure.
Reproduction of the issue:
1. The task rejection triggers a call to the error handler (onSubmitFailure)
2. This calls selmgr.abort(failure) with the rejection exception
3. SelectorManager.abort() sets this.closed = true permanently
4. All subsequent HTTP operations fail with "IOException: selector
manager closed"
The client does not recover from this state.
I was able to avoid this by using the DiscardPolicy instead of the
AbortPolicy. However, this behavior was quite a surprise to debug. Is
this behavior intentional, or does it make sense for the HttpClient to
treat task rejection as a recoverable error?