I am not sure whether to file the issue here or to Spring Cloud Config directly because I don't know at which part the issue occurs.
I want to port an application to Spring Boot Native with Spring Cloud Config. We followed the documentation of
Spring Boot Native here: https://docs.spring.io/spring-boot/reference/packaging/native-image/index.html and
Spring Cloud Config Native here: https://docs.spring.io/spring-cloud-config/docs/current/reference/html/#_aot_and_native_image_support_2
I created a repository to demonstrate the issue: https://github.com/klopfdreh/native-cloud-config-test and provided 9 steps in the README.md to reproduce it.
Short summary: Spring AOT processes the application with the profile native_runtime which the running application logs as active on startup. From the config server how ever the definition of the profile is not fetched - only default.
What we wanted is that the native image is running with no profile active and only the default configuration is used.
The logging that a profile is active and not fetched from the config server is somehow confusing.
Comparison:
Java Application with native_runtime profile activated:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.1)
2024-07-19T13:48:10.853+02:00 INFO 8811 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : Starting ConfigClientApplication using Java 18.0.1 with PID 8811 (/Users/klopfdreh/Documents/Projects/native-cloud-config-test/client/target/classes started by klopfdreh in /Users/klopfdreh/Documents/Projects/native-cloud-config-test)
2024-07-19T13:48:10.855+02:00 INFO 8811 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : The following 1 profile is active: "native_runtime"
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://127.0.0.1:8888
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=application, profiles=[default], label=null, version=null, state=null
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://127.0.0.1:8888
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=Config Client Application, profiles=[native_runtime], label=null, version=null, state=null
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://127.0.0.1:8888
2024-07-19T13:48:10.871+02:00 INFO 8811 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=Config Client Application, profiles=[default], label=null, version=null, state=null
2024-07-19T13:48:11.043+02:00 INFO 8811 --- [Config Client Application] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
Native Application:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.1)
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : Starting AOT-processed ConfigClientApplication using Java 17.0.10 with PID 1 (/native-image/config-client-application started by ? in /native-image-build)
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : The following 1 profile is active: "native_runtime"
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://host.docker.internal:8888
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=application, profiles=[default], label=null, version=null, state=null
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://host.docker.internal:8888
2024-07-19T11:22:17.919Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=Config Client Application, profiles=[default], label=null, version=null, state=null
2024-07-19T11:22:17.931Z INFO 1 --- [Config Client Application] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-07-19T11:22:17.932Z INFO 1 --- [Config Client Application] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final
2024-07-19T11:22:17.933Z INFO 1 --- [Config Client Application] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
Native Application with env var SPRING_PROFILES_ACTIVE=native_runtime activated:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.1)
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : Starting AOT-processed ConfigClientApplication using Java 17.0.10 with PID 1 (/native-image/config-client-application started by ? in /native-image-build)
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] n.c.c.t.client.ConfigClientApplication : The following 1 profile is active: "native_runtime"
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://host.docker.internal:8888
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=application, profiles=[default], label=null, version=null, state=null
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://host.docker.internal:8888
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=Config Client Application, profiles=[native_runtime], label=null, version=null, state=null
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://host.docker.internal:8888
2024-07-19T11:54:13.945Z INFO 1 --- [Config Client Application] [ main] o.s.c.c.c.ConfigServerConfigDataLoader : Located environment: name=Config Client Application, profiles=[default], label=null, version=null, state=null
2024-07-19T11:54:13.970Z INFO 1 --- [Config Client Application] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-07-19T11:54:13.974Z INFO 1 --- [Config Client Application] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.5.2.Final
2024-07-19T11:54:13.976Z INFO 1 --- [Config Client Application] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled
Comment From: wilkinsona
It is to be expected that the native_runtime profile is active at runtime if it was active at build time:
When AOT optimizations are included, some decisions that have been taken at build-time are hard-coded in the application setup. For instance, profiles that have been enabled at build-time are automatically enabled at runtime as well.
The profile's set in the initialiser that's generated by Spring Framework. For your client application, that initialiser looks like this:
@Generated
public class ConfigClientApplication__ApplicationContextInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
@Override
public void initialize(GenericApplicationContext applicationContext) {
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
applicationContext.getEnvironment().addActiveProfile("native_runtime");
addImportAwareBeanPostProcessors(beanFactory);
new ConfigClientApplication__BeanFactoryRegistrations().registerBeanDefinitions(beanFactory);
new ConfigClientApplication__BeanFactoryRegistrations().registerAliases(beanFactory);
}
/**
* Add ImportAwareBeanPostProcessor to support ImportAware beans.
*/
private void addImportAwareBeanPostProcessors(DefaultListableBeanFactory beanFactory) {
Map<String, String> mappings = new HashMap<>();
mappings.put("org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration", "org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration");
RootBeanDefinition beanDefinition = new RootBeanDefinition(ImportAwareAotBeanPostProcessor.class);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDefinition.setInstanceSupplier(() -> new ImportAwareAotBeanPostProcessor(mappings));
beanFactory.registerBeanDefinition("org.springframework.context.annotation.internalImportAwareAotProcessor", beanDefinition);
}
}
The call to add native_runtime as an active profile is happening too late to influence the loading of config data but early enough to influence the log message that lists the active profiles. I agree that this is confusing and we'll have to see if we can improve the situation. In the meantime, please run the native application with the native_runtime profile explicitly activated.
Comment From: klopfdreh
Hey @wilkinsona - thanks a lot for the fast feedback. It would be great to see this fix soon as we need to adjust our build / runtime environment to add an additional profile which overrides the properties set by the aot-process.
Example:
- Build process uses profile
native_buildand h2 database definitions to initialize the corresponding beans during aot-process - Runtime uses
native_buildfrom aot-process and overrides it withnative_runtimewhich overrides the database definitions with postgresql, so that the native application connects to the real database
This way you can easily see that the build profile is merged with the runtime profile. The load hierarchy would be default > native_build > native_runtime
Can you label this ticket correspondingly?
Comment From: wilkinsona
Can you explain why there's some urgency for a fix? If we do something here, I think it will be to ensure that native_build is "fully" active so that it affects the loading of config data. You can achieve the same yourself by explicitly setting the native_build profile as active when running the native image. You can then also set native_runtime active as well if needs be.
Comment From: klopfdreh
The issue is not to fully enable native_build but to ensure that all profiles are enabled in the correct order and the configuration are acquired correctly as we rely on heavy configured spring cloud task applications.
Because of aot-process I guess there is no way to change the profile order, is there?
native_runtime should override all properties of native_build
Comment From: snicoll
The issue is not to fully enable native_build but to ensure that all profiles are enabled in the correct order
Just specify the active profiles as they should be loaded as Andy already explained. The AOT processed code will enable the profile only if it hasn't been enabled previously.
Comment From: klopfdreh
So basically this way: SPRING_PROFILES_ACTIVE=native_build,native_runtime?
If yes there is indeed no urgency. 👍
Comment From: klopfdreh
Hey @snicoll and @wilkinsona - again thanks a lot for the help! 🎉
As mentioned in the comment: https://github.com/spring-projects/spring-boot/issues/41562#issuecomment-2239230384 - just to summarize: We are going to separate the YML file into 3 profiles:
default- defines all properties valid for build and runtimenative_build- defines all properties for the aot-process like h2 in memory database to not perform any kind of db connectionsruntime- defines all properties for the actual runtime in any environment
Changes: https://github.com/klopfdreh/native-cloud-config-test/commit/38c937940a6f7716b7b9b075b6ba8971650b1566
To run the image we are going to use: docker run --rm -e SPRING_JPA_DATASOURCE_HOST=host.docker.internal -e SPRING_PROFILES_ACTIVE=native_build,runtime <image_id> so that all profiles are activated in the correct order. (Also native_build which is needed due to the late initialization to influence the the loading of config data)
Comment From: klopfdreh
Thanks a lot for fixing this. 👍