-
Notifications
You must be signed in to change notification settings - Fork 40.6k
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
MapPropertySource changes are not reflected in the adapted ConfigurationPropertySource #13344
MapPropertySource changes are not reflected in the adapted ConfigurationPropertySource #13344
Conversation
35139a8
to
9068fc3
Compare
In spring-boot 2x `Maps` in `@ConfigurationProperties` are not being updated from the **second** `EnvironmentChangeEvent` onwards. i.e. Following test fails in spring-boot 2x, but passed in spring-boot 1x. - In case of `MapPropertySource`, `SpringIterableConfigurationPropertySource#getCacheKey()` [here](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java#L146) always returns the reference to the same object in the heap (for different invocations), even though the property-source state has changed. i.e. <br/> `return ((MapPropertySource) getPropertySource()).getSource().keySet();` - As a result, `if (ObjectUtils.nullSafeEquals(cacheKey, this.cacheKey))` [here](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/source/SpringIterableConfigurationPropertySource.java#L136) always evaluates to true (same reference) and `getCache()` returns the old/ initial cache. Consequently, `SpringIterableConfigurationPropertySource#iterator()` having reference only to the initial/ old property-keys - hence the `MapBinder` fails [here](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java#L152) to bind updated properties.
9068fc3
to
15e642a
Compare
@@ -500,12 +501,23 @@ public void systemPropertiesAreSetForLoggingConfiguration() { | |||
@Test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below test case was passing before due to a side-effect of the issue I reported in SpringIterableConfigurationPropertySource
. ${pid}
was resolvable.
Thanks for pull-request and the detailed analysis. I need to look in a a bit more detail to see what's going on before merging this. The use of |
@philwebb I just want to point out that there are failures in spring cloud if you didn't notice the link above spring-cloud/spring-cloud-netflix#2538 |
@spencergibb Thanks! We fixed something around this before didn't we? |
Indeed it was a refresh that only happened on the first request. The key wasn't checked because the property source was new. |
Update `SpringIterableConfigurationPropertySource` so that the cache key from a `MapPropertySource` is invalidated when the map contents changes. Prior to this commit, the actual keys of the map were used as the key. This meant that if the underlying map changed, they key wouldn't be invalidated because it ultimately pointed to the same object instance. See gh-13344
* pr/13344: Polish "Fix caching issues with map property sources" Fix caching issues with map property sources
@fahimfarookme Thanks very much for the pull-request. I've merged this into 2.0.x and master with an additional commit that should hopefully mean we keep the performance characteristics of the previous version. |
Issue
In spring-boot 2x
Maps
in@ConfigurationProperties
are not being updated from the secondEnvironmentChangeEvent
onwards. i.e. Following test fails in spring-boot 2x, but passed in spring-boot 1x.Analysis
In case of
MapPropertySource
,SpringIterableConfigurationPropertySource#getCacheKey()
here always returns the reference to the same object in the heap (for different invocations), even though the property-source state has changed. i.e.
return ((MapPropertySource) getPropertySource()).getSource().keySet();
As a result,
if (ObjectUtils.nullSafeEquals(cacheKey, this.cacheKey))
here always evaluates to true (same reference) andgetCache()
returns the old/ initial cache.Consequently,
SpringIterableConfigurationPropertySource#iterator()
having reference only to the initial/ old property-keys - hence theMapBinder
fails here to bind updated properties.Fix
SpringIterableConfigurationPropertySource#getCacheKey()
should return a new object. This branch -if (getPropertySource() instanceof MapPropertySource)
is not necessary.Whenever the
cacheKey
is updated,Cache
is reinitialized (with all the property keys)Sample
This issue supersedes this one.