spring-projects / spring-boot

Spring Boot

Home Page:https://spring.io/projects/spring-boot

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optimize startup time: make validation initialization lazy

dsyer opened this issue · comments

If I'm not really using validation except in the web tier it's wasteful to initialize it on startup, and it's expensive: 200-300ms out of 3s to start a Pet Clinic.

Will target to 1.5 to see what are options are. It might slip.

I think this should be DevTools-specific or opt-in. In production, I'd prefer to pay the cost of validation initialisation at startup and be safe in the knowledge that it's initialised successfully rather than deferring the cost and potential failure until a request is routed to the app.

It's also used in @ConfigurationProperties processing, at least optionally, so I'd want that to be on if I'm not in dev mode. It's a tricky one. It does seem inordinately expensive to bootstrap a local validator, compared to the value it provides. I guess maybe we should stick to explicit Spring validators for our own config properties, or something cheaper than Hibernate if it exists.

for our own config properties

We can't do that. Validation is an advertized feature so users are free to use it as advertized (read in their config properties). I don't think that changing something for our own code is something we should ever do if the related feature is public.

I did a bit of digging with this, it seems like the majority of the time is spent trying to work out of a @ConfigurationProperties instance has any validator constraints. The logic is quite complicated, scanning all method and parameters for annotations and working up the type hierarchy.

95% of our properties classes don't use JSR-330 constraints, I think if we switch validation off for our classes, but leave it on for user defined configuration classes, we should be able to improve startup time.

I think it would be easier to require @Validated on the class to enable validation. That way we can have a quick check to figure out if we need to enable it or not.

@snicoll I like that idea, but is it a sensible change for 1.5? My approach so far has been to add a vaidate attribute to @ConfigurationProperties and set that to false for all our own instances.

Change isn't noticeable in SampleTomcatApplication:

before

2017-01-16 23:31:40.055  INFO 91149 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.494 seconds (JVM running for 1.744)
2017-01-16 23:32:05.928  INFO 91185 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.494 seconds (JVM running for 1.743)
2017-01-16 23:32:21.487  INFO 91207 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.493 seconds (JVM running for 1.746)

after


2017-01-16 23:27:27.669  INFO 90702 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.764 seconds (JVM running for 2.23)
2017-01-16 23:27:44.806  INFO 90731 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.498 seconds (JVM running for 1.744)
2017-01-16 23:27:55.165  INFO 90746 --- [           main] sample.tomcat.SampleTomcatApplication    : Started SampleTomcatApplication in 1.518 seconds (JVM running for 1.771)

But it is for Pet Clinic:

before

2017-01-16 23:30:28.164  INFO 91031 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 8.411 seconds (JVM running for 8.841)
2017-01-16 23:30:52.690  INFO 91068 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.988 seconds (JVM running for 8.424)
2017-01-16 23:31:14.828  INFO 91101 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.928 seconds (JVM running for 8.359)

after


2017-01-16 23:28:31.642  INFO 90792 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.654 seconds (JVM running for 8.143)
2017-01-16 23:28:48.329  INFO 90815 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.437 seconds (JVM running for 7.863)
2017-01-16 23:29:04.505  INFO 90841 --- [  restartedMain] o.s.s.petclinic.PetClinicApplication     : Started PetClinicApplication in 7.624 seconds (JVM running for 8.106)

That flag is better if you want this to be made 100% transparent but it's a bit weird. Someone might wonder why you wouldn't want validation in the first place. Our reasoning here is performance so making it that opt-in (i.e. you need the relevant annotation now) makes more sense to me.

Surely the current state is awesome because nobody has to change anything but I fear we'll regret that design pretty soon.

I like the idea of requiring @Validated and that feels like where we want to get to. I'm not sure how to get there from where we are today, but adding a validate attribute to @ConfigurationProperties doesn't feel like the right way to me. I'm not keen on adding an attribute in 1.5 that we'd then, presumably, want to remove in 2.0 in favour of requiring @Validated.

I think we either need to jump straight into @Validated in 1.5 or we should wait for 2.0 where requiring @Validated will be less of a shock.

+1 for @Validated (in 1.5 if possible).

Seems like @Validated is the winner. Will try for 1.5.

For 1.5 we'll still validate all configuration classes (with the exception of Spring Boot's own) even if there's not a @Validated annotation. We'll log a warning telling people what to do. We can flip the default in 2.0.

Hi @philwebb ,

I tried this feature using latest snapshot version of 1.5.
But same warning log are output twice if not specify the @Validated on configuration properties class as follow:

2017-01-21 01:44:59.434  WARN 15563 --- [           main] figurationPropertiesBindingPostProcessor : The @ConfigurationProperties bean class com.example.AppProperties contains validation constraints but had not been annotated with @Validated.
2017-01-21 01:45:00.201  WARN 15563 --- [           main] figurationPropertiesBindingPostProcessor : The @ConfigurationProperties bean class com.example.AppProperties contains validation constraints but had not been annotated with @Validated.

Is this behavior work as design ?
I think better that it output only just once. What do you think ?

@kazuki43zoo Once only would be better

@philwebb quick question regarding the use of @PostConstruct annotated methods to validate properties.

Is this how it should be done on all properties classes from now on?
Is it strictly limited to validation code (or general initialization code is accepted)?
Why are setters not considered for this?

@bclozel I think setters might actually be better. I tried them at one point but hit some issues (I can't remember why). I'd say try setters first.

I'll try to fix the ones I changed.

@philwebb the only use case I have in mind is if you set the value to null for some reason and later (before the bean is initalized) you provide a valid value. If we put the validation in the setter it will fail hard as soon as we attempt to set null. Not sure if that's a legit use case but I wish we wouldn't use @PostConstruct to replace the validation.

We're going to live with the double log message. I've replaced the @PostConstruct-based validation with setter-based validation in f823599.