Skip to content

Improve startup time : save the auto-configuration discovery result #10426

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
afillatre opened this issue Sep 26, 2017 · 11 comments
Closed

Improve startup time : save the auto-configuration discovery result #10426

afillatre opened this issue Sep 26, 2017 · 11 comments

Comments

@afillatre
Copy link

I'm currently developing microservices using Spring Boot. this is great, except for the startup time. When using Spring Cloud + some other dependencies, there so many auto-configuration classes to find that the startup is very slow (about 20sec).

Disabling the auto-configuration, and using explicit @Import instead reduces the startup time by almost 2 (about 11 seconds). This is mainly due to scanning the whole class-path Vs providing the exact classes to read.

Putting aside the fact that I can certainly reduce the number of jars I have in my class-path, the startup difference is very big. Using the explicit import is hard to maintain, mostly because you need to verify it at each Spring Boot Upgrade.

I would very like having a way for Spring to remember where the auto-configuration classes are, and only read them at boot time. My typical use case is using Spring Boot with Docker. I would see the following workflow:

  • while building the docker image, look for all the auto-configuration classes
  • save the result somewhere (i.e. in the spring.factories format)
  • while launching a container from the previously built image, read the saved file and execute any found auto-configuration class, rather than doing a full class-path scan again

Note: I'm really only talking about saving where the files are, not the actual result of executing them, because that would change depending on the given runtime configurations. This would not make Spring start as fast as possible, but would definitely improve it big time.

What are you thoughts about that ?

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

Unfortunately, I think your analysis may be flawed.

Spring Boot doesn't scan for auto-configuration classes as they are already listed in spring.factories. Furthermore, the reading of the metadata about those classes is already optimised by writing that metadata to a file at build time to reduce the introspection that needs to be performed at runtime. This optimisation was added in 1cbda9b.

You may be interested in the in-depth benchmarking that has been done by @dsyer: https://github.com/dsyer/spring-boot-startup-bench and a number of other changes that are referenced from #7573.

If you can provide us with a sample application that illustrates the 20 second startup in one form and the 10 second startup in another, we can certainly take a more detailed look.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Sep 26, 2017
@afillatre
Copy link
Author

afillatre commented Oct 2, 2017

Hi @wilkinsona.

Sorry I may not have been clear on the issue. I was suggesting to use the spring.factories file to actually prevent the auto-configuration scan to occur in the first place. What I intended to say was that using either the explicit @Import annotation or the spring.factories file helps a lot to reduce startup time. The downside about using the annotation is that it's hard to maintain accros Boot updates, whereas dynamically create a spring.factories file at first launch would most likely do the job. I hope that's clearer.

I already took a look at the benchmark repo a couple weeks ago, and I saw you were still working on several ways to improve the starting time even better.

I'll try to do a sample application to show you the issue, as you asked.

@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 Oct 2, 2017
@dsyer
Copy link
Member

dsyer commented Oct 2, 2017

I hope that's clearer.

Not really. Honestly, there is no such thing as "the auto-configuration scan", so preventing it is not really an option. It would still be really useful to look at your sample app.

@snicoll snicoll added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Oct 2, 2017
@spring-projects-issues
Copy link
Collaborator

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label Oct 9, 2017
@afillatre
Copy link
Author

Sorry for the late feedback, I don't have much time to work on it.

So, as @wilkinsona pointed out, my understanding of the auto-configuration process was not very good. The first test I made threw me off track. Let me explain.

So, I was able to make a test project with some Netfllix dependencies. I did 4 tests (you can find the startup logs in the boot.log file of each branch):

  1. running the server without modifications, other than the maven dependencies (branch: master)
  2. running the same server as 1., but will all the autoconfiguration classes explicitly declared (no @EnableAutoConfiguration annotation) (branch: explicit_imports)
  3. running the same server as 2., but without any non-matching autoconfiguration classes (branch: explicit_import_without_unmatched)
  4. running the same server as 1., but excluding non-matching autoconfiguration classes (branch: explicit_exclusions)

It turns out that test 1 and 2 give about the same startup time, showing that explicitly defining auto-configuration classes is not faster (and you loose the ordering defined in their annotations).
Test 3 and 4 show a startup time about 35% faster, which indicates that the time is spent on reading the other classes' annotations, even if they don't match.

What I saw here, especially with the fourth test, is that there is a lot of auto-configuration classes that I will never need, and that come from the spring-boot-autoconfigure dependency. For instance, is there any reason why CouchbaseAutoConfiguration is not in the spring-boot-starter-data-cassandra dependency, and therefore is being loaded in every Spring App (like to insure this auto-configuration class is on the package scan path) ?

@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 status: feedback-reminder We've sent a reminder that we need additional information before we can continue labels Oct 10, 2017
@philwebb
Copy link
Member

Thanks for the sample. Have you tried any similar timings for pure Spring Boot applications (without Cloud)? I'm curious what the difference might be.

What I saw here, especially with the fourth test, is that there is a lot of auto-configuration classes that I will never need, and that come from the spring-boot-autoconfigure dependency. For instance, is there any reason why CouchbaseAutoConfiguration is not in the spring-boot-starter-data-cassandra dependency.

Putting the auto-configuration in a central module rather than the starters was an intentional design decision. It allows us to separate the concerns of auto-configuration from those of providing dependencies. It's particularly helpful when auto-configuration is still needed, but dependencies are managed in a different way (for example in a Java EE server where things are already provided).

In #7573 we really tried to minimize the impact of having many auto-configuration classes. The @ConditionalOnClass now implements AutoConfigurationImportFilter which means we don't need to read bytecode for configurations where the class is not found.

We'd like to do another round of profiling before we release Spring Boot 2.0, but to be honest this is going to take a lower priority than the feature work we've got left. If you find time, running your tests on a vanilla Spring Boot application (without Spring Cloud) and against the latest 2.0 SNAPSHOT would be very useful.

@dsyer
Copy link
Member

dsyer commented Oct 11, 2017

I put some benchmarks on those apps (see my fork: https://github.com/dsyer/spring-boot-issue-10426) and I don't see a great deal of difference between the different versions (maybe 2%, nothing like 30%). the "unamatched" sample actually had fewer beans in the application context, so we could expect it to be slightly faster (i.e. it isn't a faithful copy of the original autoconfigured sample). Anyone care to guess at the difference between my tests and the OP? My best guess would be measuring error caused by noisy neighbours (other stuff running). I was running on my laptop, which seems to have roughly comparable startup times to the OP: 6 seconds or so. Note that this is nowhere near the 20 seconds initially reported, so we are probably looking at something different (the app that starts slowly is doing more stuff, and probably not related to Spring Boot).

@dsyer
Copy link
Member

dsyer commented Oct 11, 2017

N.B. the sample apps don't actually use a lot of the Spring Cloud stack (we often require @Enable* to make things work, more than in Spring Boot). So that might explain some of the 6s-20s discrepancy.

@afillatre
Copy link
Author

The 20sec I mentioned where on one of my real apps, not the one I pushed as a test project to demonstrate this issue. Sorry if that wasn't clear.

For the follow up, what I did to create the "unmatched" application is:

  • launch the application in debug mode (to have the configuration report)
  • remove all auto-configuration class that where in the "Negative match" section, and not in the "Positive match" one.

Maybe there is something here that would explain the bean difference ?

Looking at the changes you made in your fork (very instructive btw, thanks), I don't understand why you have times that close, whereas mines are in 2 distinct groups. For completeness sake, here they are (best time out of 5 consecutive manual launch for each branch):

App Startup time (in sec)
Vanilla 7.07
Imports 7.101
Explicits 5.399
Unmatched 5.233

I'll try your benchmarck later, and see if I have the same differences.

@wilkinsona wilkinsona added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels Oct 25, 2017
@spring-projects-issues
Copy link
Collaborator

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

@spring-projects-issues spring-projects-issues added the status: feedback-reminder We've sent a reminder that we need additional information before we can continue label Nov 1, 2017
@spring-projects-issues
Copy link
Collaborator

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

@spring-projects-issues spring-projects-issues removed status: waiting-for-feedback We need additional information before we can continue status: feedback-reminder We've sent a reminder that we need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged labels Nov 8, 2017
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