spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.94k stars 40.65k forks source link

DynamicPropertyRegistry Values Not Set After Dev Tools Reload for RestartScope Containers #41552

Open shawnweeks opened 3 months ago

shawnweeks commented 3 months ago

When using a combination of @RestartScope Test Containers and the DynamicPropertyRegistry registry property values are not set back after Spring Dev Tools reload the project. The issue was partially resolved in https://github.com/spring-projects/spring-boot/issues/35786 but that fix does not appear to resolve the entire issue. I've linked to a small example project demonstrating the issue. See https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issue

nosan commented 4 weeks ago

I did some research regarding this bug, and frankly, it is quite a tricky one.

First run:


TestcontainersLifecycleBeanPostProcessor
  -- initializeContainers()

RestartScopeInitializer.RestartScope
  -- Restarter.getOrAddAttribute('redisContainer', ObjectFactory<?> factory)
  -- 'redisContainer' does not exist in attributes, so ObjectFactory<?> is being called.

TestcontainersPropertySourceAutoConfiguration.dynamicPropertyRegistry() is being called because it is needed for 'redisContainer'
-- Attach 'testcontainersPropertySource'
-- Register bean definition named: EventPublisherRegistrar.class.getName()

TestcontainersConfiguration.redisContainer()
  -- create RedisContainer
  -- Set RedisContainer properties to the 'dynamicPropertyRegistry'

TestcontainersLifecycleBeanPostProcessor.initializeStartables()
 -- Start containers

DemoController @Value annotations are being processed.

Restart:


TestcontainersLifecycleBeanPostProcessor
  -- initializeContainers()
RestartScopeInitializer.RestartScope
  -- Restarter.getOrAddAttribute("redisContainer", ObjectFactory<?>)
  -- "redisContainer" exists in attributes, so ObjectFactory<?> **is not being called.**

DemoController @Value annotations are not being processed due to Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'MY_REDIS_HOST' in value "${MY_REDIS_HOST}"

There are two potential issues with this bug.

The first one TestcontainersPropertySourceAutoConfiguration.dynamicPropertyRegistry() is being called too late if there is no direct reference to it, so TestcontainersPropertySource will not be available during @Value annotation processing.

The second one is that methods annotated @RestartScope are being called only once. That is why dynamicPropertyRegistry will not be created earlier, and container properties are ignored. (Properties are being set inside the method).

A potential fix is:

This branch contains an implementation of the fix that I have tried to describe above. https://github.com/spring-projects/spring-boot/compare/main...nosan:spring-boot:41552

I used this example for research: https://github.com/shawnweeks/spring_boot_testcontainers_restartscope_issue