Skip to content

Embedded tomcat+jsp: unrequired JSP recompilation causes performance loss #2825

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

Closed
hanswesterbeek opened this issue Apr 15, 2015 · 11 comments
Closed
Assignees
Milestone

Comments

@hanswesterbeek
Copy link

I ported an existing Spring-MVC+JSP app to Spring Boot and came across this issue.

Jasper intermittently recompiles my JSP pages, causing a loss in performance.

To reproduce:

  • git clone https://github.com/hanswesterbeek/jsp-compilation-problem
  • cd jsp-compilation-problem
  • mvn package;java -jar target/jsp-compilation-problem-0.0.1-SNAPSHOT.war
  • Now point your browser at http://localhost:8080 and look at your terminal window where the app prints timing info to System.out

As expected, the first view is slow, the JSP must be compiled. When you do a quick reload, it is fast. So far so good.
But if you wait a couple of seconds, or do some browser reloads in quick succession, Jasper is triggered to recompile the JSP page. I suspect it has lost track of the compiled version of the JSP.

In the output below the problem is observed where we read +- 140 ms 'after rendering' :

Elapsed before jsp: 0
Elapsed after JSP rendering: 628

Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 144
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 2
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 141
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1
Elapsed before jsp: 0
Elapsed after JSP rendering: 1

I don't have a workaround as I can't find a way of setting the Servlet engine 'development' mode to false (it defaults to true). Or perhaps it is better to keep deploying into a regular Tomcat? In that case the documentation should perhaps not say "i.e. an executable war will work" in section 26.3.4 JSP limitations.

This problem does not occur when executing mvn spring-boot:run which is why I only noticed it after deploying and actually running with java -jar. When running via maven this log message does NOT appear:
2015-04-15 11:55:55.735 WARN 96817 --- [ main] .i.s.PathMatchingResourcePatternResolver : Skipping [/Users/hw/Desktop/jsp-compilation-problem/target/jsp-compilation-problem-0.0.1-SNAPSHOT.war] because it does not denote a directory

Note: I'd never have used JSP if it wasn't already present in the app...

@hanswesterbeek hanswesterbeek changed the title Embedded tomcat+jsp: JSP recompilation causes performance loss Embedded tomcat+jsp: unrequired JSP recompilation causes performance loss Apr 15, 2015
@wilkinsona wilkinsona self-assigned this Apr 15, 2015
@wilkinsona
Copy link
Member

Thanks for the reproduction

@wilkinsona
Copy link
Member

Part of the problem was gh-2826 which meant that, when checked, the JSP was always thought to have change and it was being recompiled. Fixing this has reduced that after JSP rendering time by an order of magnitude. It still takes longer than the mvn spring-boot:run case as it takes longer to check the JSP and the jars it depends upon (JSTL, etc) when they're nested in a JAR vs when they're available directly on the filesystem.

The second part of the problem is that this checking is being performed at all. This is because, as noted above, the JspServlet is in development mode. We should allow its init parameters to be configured via application.properties.

@hanswesterbeek
Copy link
Author

Thanks, good to see I was on to something. The above slowdown we observe with one tiny JSP page. In my actual app I see a slowdown of about 2 sec (as opposed to the .114 sec above). The page contains some jsp-includes+taglibs etc. That makes performance unacceptable.

@wilkinsona
Copy link
Member

We may have to wait for 1.3 to make the JSP servlet's init parameters configurable via application.properties as it'll require making changes to a public interface. In the meantime, you can work around the problem by adding the following bean to your application:

    @Bean
    public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
        return new EmbeddedServletContainerCustomizer() {

            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    customizeTomcat((TomcatEmbeddedServletContainerFactory)container); 
                }
            }

            private void customizeTomcat(TomcatEmbeddedServletContainerFactory tomcatFactory) {
                tomcatFactory.addContextCustomizers(new TomcatContextCustomizer() {

                    @Override
                    public void customize(Context context) {
                        Container jsp = context.findChild("jsp");
                        if (jsp instanceof Wrapper) {
                            ((Wrapper)jsp).addInitParameter("development", "false");
                        }

                    }

                });
            }

        };
    }

@hanswesterbeek
Copy link
Author

Brilliant, thank you!

@philwebb philwebb added this to the 1.3.0 milestone Apr 15, 2015
@iriopaul
Copy link

Note that in Spring Boot 1.1.7 we did not observe this issue. When we updated our application to Spring Boot 1.2.2, there was a sizable performance loss. Therefore we view it as a regression. Nothing was changed in our application except for the updated library versions. We tracked it down to Jasper performing a recompile on every page load.

Our application supports using precompiled jsps, so we were able to compare performance times when the jsps were not precompiled. When not precompiled, the performance loss is extremely significant.

Sure, we can we avoid the problem by disabling the JspServlet development mode as mentioned previously, although we view this is a workaround and do not believe it should be considered a solution.

Thanks for your help!

@hanswesterbeek
Copy link
Author

I think they have a real solution slated for 1.3.x so making do with this workaround is acceptable to me until then.

@fraulyoner
Copy link

@wilkinsona: Thank you! :)

@hennr
Copy link

hennr commented Sep 27, 2016

Any chance to have this toggle set to false and set true via spring-boot-devtools?
This behaviour really gave me a hard time for the last few days.

@wilkinsona
Copy link
Member

wilkinsona commented Sep 27, 2016

@hennr We could certainly consider it, but the suggestion's likely to get lost on a closed issue. Could you open a new one please?

@hennr
Copy link

hennr commented Sep 28, 2016

Great, here is the new issue #7039

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants