Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When using Jetty, idle timeout for IO operations and delayed dispatch cannot be set to less than 30000ms #38960

Closed
AElmecker opened this issue Jan 3, 2024 · 6 comments
Labels
type: regression A regression from a previous release
Milestone

Comments

@AElmecker
Copy link

AElmecker commented Jan 3, 2024

Encountered behaviour

After the upgrade to spring boot 3.2.0 or 3.2.1 (jetty 12) the HTTP connections always time out after 30000 ms, regardless of the value set through the server.jetty.connection-idle-timout property.

java.util.concurrent.TimeoutException: Idle timeout expired: 30004/30000 ms

It seems that the timeout value was hardcoded during the upgrade to jetty 12 with this change.

Behaviour before upgrade

Previously (with spring-boot 3.1.5 in combination with jetty 11), no http idle timeout was set in the HttpConfiguration, so the default of jetty is to use the connection idle timeout, set via the property server.jetty.connection-idle-timout.

How to reproduce

If needed, I can provide an example and steps how to reproduce the issue.

Conclusion

Was this change intentional or an oversight?
Either way, imo this idle-timeout should be somehow configurable and not hard-coded.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 3, 2024
@wilkinsona
Copy link
Member

Any change in this area wasn't intentional but as far as I can tell it continues to be configurable. The configured idle timeout is applied to each of the server's connectors:

private void customizeIdleTimeout(ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) {
factory.addServerCustomizers((server) -> {
for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) {
if (connector instanceof AbstractConnector abstractConnector) {
abstractConnector.setIdleTimeout(connectionTimeout.toMillis());
}
}
});
}

The result of this can be seen when enabling debug-level logging for org.eclipse.jetty.io. For example, here's the output with an idle timeout of 1000ms:

2024-01-03T09:48:19.999Z DEBUG 93967 --- [tp1746578747-39] org.eclipse.jetty.io.IdleTimeout         : Setting idle timeout 0 -> 1000 on SocketChannelEndPoint@d98ae3b[{l=/[0:0:0:0:0:0:0:1]:8080,r=/[0:0:0:0:0:0:0:1]:61039,OPEN,fill=-,flush=-,to=2/1000}{io=0/0,kio=0,kro=0}]->[<null>]

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Jan 3, 2024
@AElmecker
Copy link
Author

Yes, the connector itself gets configured with the right idle-timeout through the property and the timeout is also applied:
https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java#L445-L450

But at a later point when the HTTP request is initiated, the timeout gets updated with the value of the HttpConfiguration as you can see here:
https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/internal/HttpChannelState.java#L259-L262

Previously in spring-boot 3.1.5 the value for the http idle-timeout (in HttpConfiguration) was not overridden thus being jettys default value of -1, which means that it won't override the connection idle-timeout.

Sure, I will create a small example and provide it here for you.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jan 3, 2024
@wilkinsona
Copy link
Member

Sure, I will create a small example and provide it here for you.

No need now, thank you. The additional explanation above has made the cause clear.

Unfortunately, GitHub's collapsing of larger diffs meant that
ed5d16d#diff-ac1beefcaa8b25ba950438b71795d0ff1b585ed0589d8151755b5aaace5c69e9R216 didn't link to a specific line.

@wilkinsona wilkinsona added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Jan 3, 2024
@wilkinsona wilkinsona changed the title Jetty HTTP connections always use a 30 second idle timeout after spring-boot 3.2.x upgrade Jetty HTTP connections always use a 30 second idle timeout after 3.2.x upgrade Jan 3, 2024
@wilkinsona wilkinsona added this to the 3.2.x milestone Jan 3, 2024
@wilkinsona wilkinsona changed the title Jetty HTTP connections always use a 30 second idle timeout after 3.2.x upgrade When using Jetty, idle timeout for IO operations and delayed dispatch cannot be set to less then 30000ms Jan 3, 2024
@wilkinsona wilkinsona changed the title When using Jetty, idle timeout for IO operations and delayed dispatch cannot be set to less then 30000ms When using Jetty, idle timeout for IO operations and delayed dispatch cannot be set to less than 30000ms Jan 3, 2024
@AElmecker
Copy link
Author

You're welcome, if you need any more help/insight I can provide, please say so.
I already did some investigation before creating the issue, so I might be able to provide useful info.

Oh I see, that's a bummer with the large diffs.

@AElmecker
Copy link
Author

As a short term fix for now, I came up with this:

@Override
    public void customize(ConfigurableJettyWebServerFactory factory) {
        factory.addServerCustomizers(
            server -> {
                for (Connector connector : server.getConnectors()) {
                    connector.getConnectionFactories().forEach(c -> {
                        if (c instanceof HttpConnectionFactory) {
                            // -1 for idle-timeout means use the connection-idle-timeout, in our case defined by the
                            // spring property: jetty.connection-idle-timeout
                            ((HttpConnectionFactory) c).getHttpConfiguration().setIdleTimeout(-1);
                        }
                    });
                }
            }
        );
    }

To override the hardcoded value in the HttpConfiguration, not sure if that's the smartest way...

@wilkinsona wilkinsona modified the milestones: 3.2.x, 3.2.2 Jan 3, 2024
@GitXm123
Copy link

GitXm123 commented Apr 20, 2024

Hi @AElmecker  Thanks for raising this issue. I encountered the same problem in my springboot 3.2 project and using RestTemplate when calling an external service that takes more than 30 seconds to complete. Can you please explain a bit more about the fix you proposed. Are you creating a bean that extends the JettyWebServerFactoryCustomizer class and override the customize method? I tried to do this but I got application startup-error "A bean with that name has already been defined "

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: regression A regression from a previous release
Projects
None yet
Development

No branches or pull requests

4 participants