Jacques-Etienne Beaudet created HTTPCLIENT-2434:
---------------------------------------------------

             Summary: Improve Idle connection management customization
                 Key: HTTPCLIENT-2434
                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-2434
             Project: HttpComponents HttpClient
          Issue Type: Improvement
          Components: HttpClient (async), HttpClient (classic)
    Affects Versions: 5.6.4
            Reporter: Jacques-Etienne Beaudet


Hi

Follow-up to HTTPCLIENT-2388, as suggested by Oleg on the users list:
[https://lists.apache.org/thread/hymc9p6srgyx4nbxgqzqz6gn036rgt8g]

Since HTTPCLIENT-2388, HttpClientBuilder passes sleepTime = null to
IdleConnectionEvictor, so the sweep interval becomes maxIdleTime/10 with a
1 second floor. It used to pass sleepTime = maxIdleTime. The sweep is now 10x
more frequent at any value of maxIdleTime, and there is no way to set
sleepTime through the builder.

The process of removing idle connections is not free: closeExpired() and 
closeIdle() go
through StrictConnPool.enumAvailable(), which holds the pool lock while
calling PoolEntry.discardConnection(GRACEFUL), and DefaultDisposalCallback
clamps the socket timeout to 1 second for that close. So a single slow
close holds the pool lock for up to a second. With a 1 second
connectionRequestTimeout we sometimes got:

RequestFailedException: Request execution failed
at InternalExecRuntime.acquireEndpoint(InternalExecRuntime.java:133)
Caused by: DeadlineTimeoutException: Deadline: ..., -116 MILLISECONDS overdue
at StrictConnPool.lease(StrictConnPool.java:216)

I've included the stack trace below.

Two things that could be considered:

1. A way to set sleepTime from the builder, for example an
evictIdleConnections(TimeValue sleepTime, TimeValue maxIdleTime) overload on
HttpClientBuilder and HttpAsyncClientBuilder.

Managing the evictor ourselves does work, but tying its lifecycle to the
client means subclassing the builder, because addCloseable is protected. This 
is the hack we ended up with:
{code:java}
public static class CloseableExposedHttpClientBuilder extends HttpClientBuilder
        {
            @Override
            public void addCloseable(Closeable closeable)
            {
                super.addCloseable(closeable);
            }
        }


        IdleConnectionEvictor evictor =
                new IdleConnectionEvictor(connectionManager, sleepTime, 
maxIdleTime);
        builder.addCloseable(() -> {
            evictor.shutdown();
            try {
                evictor.awaitTermination(Timeout.ofSeconds(1));
            } catch (InterruptedException ignored) {
                Thread.currentThread().interrupt();
            }
        });
        evictor.start();
{code}
2. Raise the floor of 1 second to something a bit higher like 5 or 10 seconds, 
given the contention that this process has.

 

Stack trace of the errors we've been seeing : 
{code:java}
   org.apache.hc.client5.http.impl.classic.RequestFailedException: Request 
execution failed
     at 
org.apache.hc.client5.http.impl.classic.InternalExecRuntime.acquireEndpoint(InternalExecRuntime.java:133)
     at 
org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:127)
     at 
org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
     at 
org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
     at 
org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
     at 
org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:138)
     at 
org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
     at 
org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:112)
     at 
org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
     at 
org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:185)
     at 
org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:87)
     at 
org.apache.hc.client5.http.classic.HttpClient.executeOpen(HttpClient.java:183)
     ... application frames omitted ...
   Caused by: org.apache.hc.core5.util.DeadlineTimeoutException: Deadline: 
2026-09-16T21:11:31.165+0000, -116 MILLISECONDS overdue
     at 
org.apache.hc.core5.util.DeadlineTimeoutException.from(DeadlineTimeoutException.java:49)
     at org.apache.hc.core5.pool.StrictConnPool.lease(StrictConnPool.java:216)
     at 
org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.lease(PoolingHttpClientConnectionManager.java:366)
     at 
org.apache.hc.client5.http.impl.classic.InternalExecRuntime.acquireEndpoint(InternalExecRuntime.java:105)
 {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to