How to use @RefreshScope with your Datasource for dynamic property updates at runtime?
As directed by Spring Boot best practices, configuration properties for Spring Boot apps are externalized via Git, local file system, HashiCorp Vault, etc. However, when these properties are updated in the source itself, they must be delineated to the Spring Boot apps again. This does not happen automatically and can be achieved in one of the following two ways:
- Update the properties inside the source and simply restart the application; this approach is as simple as it can get but leads to application downtime – something that does not sit well with most organizations and especially, their customer-facing applications.
- Use /actuator/refresh endpoint to dynamically fetch new property values without requiring an application restart.
The second approach is perfect since it can be easily automated and allows property update with zero-downtime. To allow for property update in zero-downtime via /actuator/refresh, it is very important to understand how to use the @RefreshScope
annotation correctly.
The @RefreshScope
annotation
The @RefreshScope
annotation is an implementation of a Spring Boot Scope that allows dynamic refresh of beans at runtime via the /actuator/refresh call. Once a bean is refreshed, the next time it is accessed (i.e. a method is called that uses the refreshed bean), a new instance of the Bean is created. This means that @RefreshScope
forces the lazy initialization semantics since new Bean instance after refresh is created only when a method using it is invoked. All lifecycle methods are applied to the bean instances, so any destruction callbacks that were registered in the bean factory are called when it is refreshed, and then the initialization callbacks are invoked as normal when the new instance is created. A new bean instance is created from the original bean definition, so any externalized content (property placeholders or expressions in string literals) is re-evaluated when it is created.
Using @RefreshScope
with your Datasource
To enable dynamic updates of Datasource properties via /actuator/refresh, the Datasource beans must be annotated with @RefreshScope
. This means that the Datasource must not be configured via the application.properties file. Instead, it must be provided via a Configuration class. This is because the Beans autoconfigured from the application.properties settings are not annotated with @RefreshScope
by default, and thus, the /actuator/refresh will not force the creation of a new Bean with the new values. To enable this via Configuration class, we need to create two classes:
- DatasourceProperties.java – this is a simple POJO class that holds properties from the external configuration source and getter, setter methods for the same.
- DatabaseConfiguration.java – This
@Configuration
class varies according to the database being used. However, two methods annotated with@Bean
and@RefreshScope
must be present:@Bean @RefreshScope public DatasourceProperties datasourceProperties() { return new DatasourceProperties(); }
@Bean @RefreshScope public <Datasource> datasource() { // use datasourceProperties Bean to configure your connection to the database }
Conclusion
Using this setup allows for zero-downtime update of database properties and is especially useful in scenarios when database properties have to be updated periodically and need to be delineated to the consuming applications.
/actuator/refresh can be called manually after this setup is done in your Spring Boot application or a simple automation script can call this endpoint for all your applications. In case your applications have multiple instances, these instances can be connected via a Spring Cloud Bus and you can call the /actuator/bus-refresh endpoint on any one of the services, forcing all instances of the application to be refreshed dynamically.