[ 
https://issues.apache.org/jira/browse/HTTPCLIENT-2434?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Jacques-Etienne Beaudet updated HTTPCLIENT-2434:
------------------------------------------------
    Description: 
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.

 

Thanks!

 

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}

  was:
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}


> 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
>            Priority: Major
>
> 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.
>  
> Thanks!
>  
> 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