Search code examples
spring-bootazureazure-app-configuration

Azure Spring Cloud AppConfiguration refresh is not working


im having some problems refreshing anything, be it @ConfigurationProperties or @Value, using the

implementation("com.azure.spring:azure-spring-cloud-appconfiguration-config:2.1.1")

library. From what i could find and debug, the inner AppConfigurationRefresh class is called and the RefreshEvent is created reacting correctly to changes done in Azure Config Server. Problem is that, when context is updated, there also should be new values recognized by the ContextRefresher, which is not the case for me.

Spring Boot ContextRefresher

    public synchronized Set<String> refreshEnvironment() {
        Map<String, Object> before = extract(this.context.getEnvironment().getPropertySources());
        updateEnvironment();
        Set<String> keys = changes(before, extract(this.context.getEnvironment().getPropertySources())).keySet();
        this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
        return keys;
    }

The result of that refresh method is always empty, which means no changes were found.

Logs generated by the refresh event:

2021-11-29 19:53:03.543  INFO [] 34820 --- [         task-2] c.a.s.c.config.AppConfigurationRefresh   : Configuration Refresh Event triggered by /myprefix/my.config.value
2021-11-29 19:53:53.694  INFO [] 34820 --- [         task-2] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]
2021-11-29 19:53:53.719  INFO [] 34820 --- [         task-2] o.s.boot.SpringApplication               : The following profiles are active: messaging,db,dev
2021-11-29 19:53:53.736  INFO [] 34820 --- [         task-2] o.s.boot.SpringApplication               : Started application in 3.347 seconds (JVM running for 158.594)
2021-11-29 19:54:01.265  INFO [] 34820 --- [         task-2] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: []
2021-11-29 19:54:03.553  INFO [] 34820 --- [   scheduling-1] d.l.d.a.s.c.AppConfigurationUpdater      : All configurations were refreshed.

bootstrap.yml

spring:
      cloud:
        azure:
          appconfiguration:
            stores:
              - connection-string: ${connection-string}
                selects:
                  - key-filter: '/myprefix/'
                    label-filter: dev
    
                monitoring:
                  enabled: true
                  refresh-interval: 1s
                  triggers:
                    -
                      label: dev
                      key: /myprefix/my.config.value

I only noticed one thing that could be relevant to this, looking at log from start of the application (where everything is loaded properly) and at the point of refresh:

2021-11-29 19:51:31.578  INFO [] 34820 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/myprefix/https://my-config-store-stage.azconfig.io/dev'}, BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]

2021-11-29 19:53:53.694  INFO [] 34820 --- [         task-2] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]

It seems that when refreshing, the Spring is not able to locate all BootstrapPropertySources and maybe thats why there are no changes found. Am i missing some configuration somewhere to specify these or does anyone know whats the problem here. Thanks


Solution

  • The Problem

    The changed values in azure appconfig store are triggering refresh event (either automaticaly using "web" version of library or through manual call to AppConfigurationRefresh.refreshConfigurations) and you can see it in the logs like this:

    2021-11-29 19:53:03.543  INFO [] 34820 --- [         task-2] c.a.s.c.config.AppConfigurationRefresh   : Configuration Refresh Event triggered by /myprefix/my.config.value
    2021-11-29 19:53:53.694  INFO [] 34820 --- [         task-2] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]
    2021-11-29 19:53:53.719  INFO [] 34820 --- [         task-2] o.s.boot.SpringApplication               : The following profiles are active: messaging,db,dev
    2021-11-29 19:53:53.736  INFO [] 34820 --- [         task-2] o.s.boot.SpringApplication               : Started application in 3.347 seconds (JVM running for 158.594)
    2021-11-29 19:54:01.265  INFO [] 34820 --- [         task-2] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: []
    

    However the Spring Boot is unable to locate any changes in PropertySources as is evident from:

    2021-11-29 19:54:01.265  INFO [] 34820 --- [         task-2] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: []
    

    The Research

    What actually was deciding factor for me to find the issue, was indeed the difference between the found BootstrapPropertySources at the start of the application and at the refresh.

    2021-11-29 19:51:31.578  INFO [] 34820 --- [           main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/myprefix/https://my-config-store-stage.azconfig.io/dev'}, BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]
    
    2021-11-29 19:53:53.694  INFO [] 34820 --- [         task-2] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-/application/https://my-config-store-stage.azconfig.io/dev'}]
    

    The culprit for the not found changes is indeed the missing BootstrapPropertySource at the update. From my testing its evident, that all configuration properties are dependant on the name of the PropertySource they came from and if its missing, they will retain their original old value.

    The problem is in the way the appconfig library is locating/creating the BootstrapPropertySources and does not differentiate between startup and update.

    The following code is from appconfiguration library and i only took the part that is causing the bug.

    public final class AppConfigurationPropertySourceLocator implements PropertySourceLocator {
    ...
    @Override
    public PropertySource<?> locate(Environment environment) {
    ...
        String applicationName = this.properties.getName();
        if (!StringUtils.hasText(applicationName)) {
            applicationName = env.getProperty(SPRING_APP_NAME_PROP);
        }
    ...
    }
    ...
    }
    

    The problem here is that env.getProperty(SPRING_APP_NAME_PROP); is filled with "spring.application.name" during startup, because spring loads all .yml files at once, but is not available during update. Also the AppConfigurationProperties properties.name is never mentioned in any documentation from azure, but is crutial to overcome this problem.

    The Solution

    If you are using custom spring.application.name include some name also into the bootstrap.yml like this:

    spring:
      cloud:
        azure:
          appconfiguration:
            name: your-name #any value will work
            stores:
              - connection-string: ${connection-string}
                selects:
                  - key-filter: '/myprefix/'
                    label-filter: dev
    
                monitoring:
                  enabled: true
                  refresh-interval: 1s
                  triggers:
                    -
                      label: dev
                      key: /myprefix/my.config.value
    

    This will make the library use the properties name value at all times and avoid usage of the problematic spring.application.name value.