Repository navigation
Config refresh fails and leaves refreshScope DOWN when spring.datasource.connection-fetch=lazy is used #1778
Description
Versions: Spring Boot 4.1.2, Spring Cloud 2025.1.4 (spring-cloud-context 5.0.4), HikariCP. Also seen with Spring Cloud 2025.1.3 (spring-cloud-context 5.0.3), with a worse outcome (see below).
Reproducer: https://github.com/tommyk-gears/spring-cloud-lazy-datasource-refresh. Running ./gradlew test shows the failure: the same refresh scenario fails with connection-fetch=lazy and passes with connection-fetch=eager.
What happens
With spring.datasource.connection-fetch=lazy, a config refresh (POST /actuator/refresh, or ContextRefresher.refresh()) fails:
- The refresh throws
IllegalArgumentException: 'existingValue' must be an instance of com.zaxxer.hikari.HikariDataSource, andPOST /actuator/refreshreturns 500. - The
refreshScopehealth indicator is DOWN from then on, so the overall health is DOWN too. It doesn't recover without a restart. - With spring-cloud-context 5.0.3, every query also fails from then on, with
IllegalStateException: No 'targetDataSource' set. With 5.0.4 queries keep working (explained below).
In Kubernetes, this got our pods restarted by the liveness probe after a config refresh, and on 5.0.3 they served database errors until then. Without lazy connection fetching, the same refresh has no effect on the DataSource.
What we found
With lazy fetching, Boot's LazyConnectionDataSourceConfiguration wraps the @ConfigurationProperties HikariDataSource bean dataSource in a LazyConnectionDataSourceProxy, using a BeanPostProcessor. ConfigurationPropertiesRebinder rebinds the bean returned by getBean("dataSource"), which is the proxy. The proxy's class isn't in spring.cloud.refresh.never-refreshable, so the rebinder resets it and then binds the Hikari properties onto it. That binding fails, because the binding target from the bean definition is HikariDataSource.
Why queries only fail on 5.0.3: before rebinding, the rebinder resets the bean to the defaults of a fresh instance, and a fresh LazyConnectionDataSourceProxy has no target. 5.0.3 does this for any class with a no-arg constructor, so the proxy's target becomes null. 5.0.4 only does it for classes whose only constructor takes no arguments (gh-1733). LazyConnectionDataSourceProxy also has a LazyConnectionDataSourceProxy(DataSource) constructor, so the reset is skipped and the target is kept. With debug logging, 5.0.4 logs No default constructor for org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; skipping property reset before rebinding.
Workaround
Add org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy to spring.cloud.refresh.never-refreshable. As documented for #1746, a custom value replaces the default, so com.zaxxer.hikari.HikariDataSource has to be listed too.
Possible directions, just as food for thought
- Rebind the instance recorded by
ConfigurationPropertiesBeans, i.e. the object the properties were bound to, rather than the post-processed bean. That also covers a wrapped DataSource type that does support rebinding. - Include
LazyConnectionDataSourceProxyin the defaultnever-refreshablevalue. That only skips the proxy (fine for Hikari, but a rebindable target would no longer be rebound), and like the Hikari default it is lost when a service sets its own value.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
- StatusShow more project fieldsDone
Activity
ryanjbaxter commentedon Oct 27, 2026
Thanks @tommyk-gears.
Can you test the fix in #1779 to validate it addresses the issue?
tommyk-gears commentedon Oct 27, 2026
Thanks @ryanjbaxter! I tested #1779 (
c75dd966), built locally as spring-cloud-context 5.0.5-SNAPSHOT:refreshScopestays UP. With 5.0.4 it fails as described. The eager variant passes in both./actuator/refreshfailed with 5.0.3 ('existingValue' must be an instance of ...HikariDataSource, thenNo 'targetDataSource' set), and succeeds with Check if the bean is wrapped after properties are bound when rebinding #1779, with the DataSource andrefreshScopehealth staying UP.Looks good to me.
Check if the bean is wrapped after properties are bound when rebinding