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

Document that the shutdown endpoint is not intended for use when deploying a war to a servlet container #17398

Closed
membersound opened this issue Jul 2, 2019 · 13 comments
Labels
type: documentation A documentation update
Milestone

Comments

@membersound
Copy link

When a *.war file is undeployed from Tomcat (eg via the tomcat web frontend), the following memory leak is detected:

02-Jul-2019 11:50:40.143 WARNING [http-nio-8080-exec-5] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [my-app] appears to have started a thread named [Thread-22] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 java.base@11.0.3/java.lang.Thread.sleep(Native Method)
 org.springframework.boot.actuate.context.ShutdownEndpoint.performShutdown(ShutdownEndpoint.java:67)
 org.springframework.boot.actuate.context.ShutdownEndpoint$$Lambda$3977/0x000000084242f040.run(Unknown Source)
 java.base@11.0.3/java.lang.Thread.run(Thread.java:834)

Could you fix that?

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

The shutdown endpoint will only create a thread to close the application context if you have called it. In other words, I don't believe that undeploying a war file from Tomcat will be sufficient to recreate this problem. Furthermore, the shutdown endpoint is disabled by default and is not intended for use when deploying a war file to a container. Instead, you should use the container's standard mechanisms to shut down and undeploy the application.

@wilkinsona wilkinsona added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 2, 2019
@membersound
Copy link
Author

membersound commented Jul 2, 2019

I will try to create an example project.

But in the meantime: the documentation says, the /shutdown endpoint "Lets the application be gracefully shutdown". So, is the endpoint only for shutting down the app in case of running an embedded jar file, not a war container? If so, maybe this could be added to the docs as a note?
https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html

Then, what would be the correct way of shutting down a Spring war in a tomcat container?

@wilkinsona
Copy link
Member

That's a fair point. We should improve the docs here.

Then, what would be the correct way of shutting down a Spring war in a tomcat container?

Use the container's standard mechanism for undeploying an application. As part of that, the container will call the contextDestroyed(ServletContext) method on the ContextLoaderListener that Boot automatically registers which will result in the application context being closed.

@wilkinsona wilkinsona added type: documentation A documentation update and removed status: invalid An issue that we don't feel is valid labels Jul 2, 2019
@wilkinsona wilkinsona changed the title ShutdownEndpoint creates memory leak in Tomcat Document that the shutdown endpoint is not intended for use when deploying a war to a servlet container Jul 2, 2019
@wilkinsona wilkinsona reopened this Jul 2, 2019
@membersound
Copy link
Author

membersound commented Jul 2, 2019

The root cause is management.endpoints.enabled-by-default=true. If set (which I use in conjunction with spring-boot-admin-starter to just expose any of my actuator endpoints to the SBA interface), then also the ShutdownEndpoint is initialized (though not exposed to SBA by default).

So, if the docs state enabled by default = No in the table for shutdown endpoint, then I would expect the application property ...enabled-by-default=true having the same effect, and the shutdown endpoint should still be disabled and must be explicit opt in.

Sidenote: I'm setting this property explicit due to different profiles. This way, I can disable the actuator completely in dev+test, and enable it in production by just overriding the property mentioned:

application.properties [dev]:
management.endpoints.enabled-by-default=false

application-test.properties
(inherits from application.properties)

application-production.properties
management.endpoints.enabled-by-default=true

@wilkinsona
Copy link
Member

The default for enabled-by-default is true so either there's a bug here or there's more to it than you have described. A sample war that reproduces the problem would be useful.

@membersound
Copy link
Author

Find example attached. It's a simple as adding the actuator dependency to pom.xml and setting the property management.endpoints.enabled-by-default=true.

Put your breakpoint into ShutdownEndpoint.setApplicationContext(). It is only invoked if property above is set. Remove the property completely and the ShutdownEndpoint is never called (though as you said, the default of that property is also =true).

shutdown-example.zip

@membersound
Copy link
Author

membersound commented Jul 2, 2019

Also, if you compile my example project with <packaging>war</packaging>, deploy it to a tomcat webserver, access the deployed app, then simply undeploy the app. Then the memory leak logs can be found in catalina.out logs indeed.

So, if the ShutdownEndpoint is started inside a tomcat container, it definitely creates a memory leak. Probably we have multiple different issues here?

@wilkinsona
Copy link
Member

The default for enabled-by-default is true

I had misremembered this, sorry. The default for enabled-by-default is that it has no value which means that each individual endpoint's default enablement is used. That's false for the shutdown endpoint and true for all other endpoints as documented. When you set management.endpoints.enabled-by-default=true it configures the default enablement of every endpoint. The enablement can then be fine-tuned using the individual management.endpoint.<id>.enabled properties.

@wilkinsona
Copy link
Member

wilkinsona commented Jul 2, 2019

The shutdown endpoint will only create a thread to close the application context if you have called it. In other words, I don't believe that undeploying a war file from Tomcat will be sufficient to recreate this problem.

I was wrong here too. Sorry again. The sample has allowed me to see what's happening. When the shutdown endpoint is enabled, it's exposed as a bean. Framework's support for inferring a bean's destroy method and wrapping it in a DisposableBeanAdapter finds the shutdown method and assumes that it's a destroy method that should be called when the context is being closed. When Tomcat undeploys the application the context is closed. This causes the endpoint's shutdown method to be called as it's considered to be a destroy method. As a result, a thread is created to shut down the app by closing the context. This thread blocks for the remainder of the close processing that is already in progress which causes Tomcat to detect it as a leaked thread. Once the first close attempt has completed, the second will then no-op and the thread will exit.

@membersound
Copy link
Author

So in my case I'd have to explicit set management.endpoint.shutdown.enabled=false to disable the shutdown endpoint.

@philwebb philwebb added for: team-attention An issue we'd like other members of the team to review and removed for: team-attention An issue we'd like other members of the team to review labels Jul 2, 2019
@philwebb philwebb added this to the 2.x milestone Nov 15, 2019
@philwebb philwebb modified the milestones: 2.x, 2.6.x Aug 19, 2022
@freeman9397
Copy link

I am deploying Spring Boot application on Tomcat with the war packaging and also getting memory leak error message when I deploy it locally on Tomcat. BTW, I have implemented graceful shutdown in my application by referring to this document here.

Application Versions:

  • Tomcat - 9.0.64
  • Spring Boot - 2.7.1
  • Packaging - War
  • Java - 1.8
  • Project - Gradle
  • Gradle - 7.4.1

I have found another way to implementing it manually by getting the Tomcat's connector object and waiting for grace period to allow all inflight requests to complete before moving to the shutdown phase with the help of @PreDestroy annotation. However, I just wanted to know if there is a better way to do this as this is a trivial problem that many developers would have faced.

@philwebb
Copy link
Member

philwebb commented Oct 7, 2022

@freeman9397 Graceful shutdown as documented in the section you linked to is for embedded web servers. If you're deploying a war file to Tomcat then shutdown is outside of Spring Boot's control.

@freeman9397
Copy link

freeman9397 commented Oct 7, 2022

@philwebb Thanks for the quick update. Do you know of a way how to handle graceful shutdown when application is deployed through a war file in the best way other than I mentioned?

@mhalbritter mhalbritter modified the milestones: 2.7.x, 2.7.7 Nov 29, 2022
krenson pushed a commit to krenson/test-push that referenced this issue Mar 15, 2023
…ot-starter-parent from 2.3.5.RELEASE to 2.7.7 (minor)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [org.springframework.boot:spring-boot-starter-parent](https://spring.io/projects/spring-boot) ([source](https://github.com/spring-projects/spring-boot)) | parent | minor | `2.3.5.RELEASE` -> `2.7.7` |

---

### Release Notes

<details>
<summary>spring-projects/spring-boot</summary>

### [`v2.7.7`](https://github.com/spring-projects/spring-boot/releases/tag/v2.7.7)

[Compare Source](spring-projects/spring-boot@v2.7.6...v2.7.7)

#### 🐞 Bug Fixes

-   Fix typo in LocalDevToolsAutoConfiguration logging [#&#8203;33569](spring-projects/spring-boot#33569)
-   Web server fails to start due to "Resource location must not be null" when attempting to use a PKCS 11 KeyStore [#&#8203;32179](spring-projects/spring-boot#32179)

#### 📔 Documentation

-   Improve gradle plugin tags documentation [#&#8203;33614](spring-projects/spring-boot#33614)
-   Improve maven plugin tags documentation [#&#8203;33609](spring-projects/spring-boot#33609)
-   Fix typo in tomcat accesslog checkExists doc [#&#8203;33460](spring-projects/spring-boot#33460)
-   Document that the shutdown endpoint is not intended for use when deploying a war to a servlet container [#&#8203;17398](spring-projects/spring-boot#17398)

#### 🔨 Dependency Upgrades

-   Upgrade to Byte Buddy 1.12.20 [#&#8203;33570](spring-projects/spring-boot#33570)
-   Upgrade to Dropwizard Metrics 4.2.14 [#&#8203;33571](spring-projects/spring-boot#33571)
-   Upgrade to Elasticsearch 7.17.8 [#&#8203;33572](spring-projects/spring-boot#33572)
-   Upgrade to HttpClient 4.5.14 [#&#8203;33573](spring-projects/spring-boot#33573)
-   Upgrade to HttpCore 4.4.16 [#&#8203;33574](spring-projects/spring-boot#33574)
-   Upgrade to Infinispan 13.0.14.Final [#&#8203;33575](spring-projects/spring-boot#33575)
-   Upgrade to Jaybird 4.0.8.java8 [#&#8203;33576](spring-projects/spring-boot#33576)
-   Upgrade to Jetty 9.4.50.v20221201 [#&#8203;33577](spring-projects/spring-boot#33577)
-   Upgrade to MSSQL JDBC 10.2.2.jre8 [#&#8203;33578](spring-projects/spring-boot#33578)
-   Upgrade to Neo4j Java Driver 4.4.11 [#&#8203;33579](spring-projects/spring-boot#33579)
-   Upgrade to Netty 4.1.86.Final [#&#8203;33580](spring-projects/spring-boot#33580)
-   Upgrade to Reactor 2020.0.26 [#&#8203;33543](spring-projects/spring-boot#33543)
-   Upgrade to Spring Integration 5.5.16 [#&#8203;33581](https://github.com/spring-projects/spring...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation update
Projects
None yet
Development

No branches or pull requests

6 participants