spring-projects / spring-projects/spring-boot

@ServiceConnection on a PostgreSQL container produces JdbcConnectionDetails but not R2dbcConnectionDetails

Open
#51,502 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: feedback-provided status: waiting-for-triage
Dominant language
Java
Stars
81.5k
Forks
42.7k
Avg merge
2d 4h
Merged PRs (30d)
65

Description

@ServiceConnection on a PostgreSQLContainer produces JdbcConnectionDetails but not R2dbcConnectionDetails

Spring Boot version: 4.0.0
Testcontainers version: reproduced on both 2.0.4 and 1.21.2
Java: 25 · Kotlin: 2.4.10 · Build: Gradle, io.spring.dependency-management
Docker: 28.3.2 (macOS, aarch64)

Summary

A PostgreSQLContainer @Bean annotated @ServiceConnection in a @TestConfiguration supplies connection
details to Flyway (JDBC) but not to R2DBC. Flyway connects to the container and migrates successfully, proving
JdbcConnectionDetails is published. In the same context, the application's ConnectionFactory ignores the
container and uses whatever spring.r2dbc.url resolves to, which means R2dbcConnectionDetails is absent.

Because the fallback is a working property rather than an error, this fails as an obscure connection error to
whatever host the property names, rather than as a missing-bean or misconfiguration message.

Reproduction

@TestConfiguration(proxyBeanMethods = false)
class ContainerConfiguration {

    @Bean
    @ServiceConnection
    fun postgres(): PostgreSQLContainer = PostgreSQLContainer("postgres:17")
}

@SpringBootApplication
@Import(ContainerConfiguration::class)
class TestApplication
@SpringBootTest(classes = [TestApplication::class])
@ActiveProfiles("test")
class SomeIntegrationTest { /* autowires a repository backed by R2DBC */ }

application-test.yaml:

spring:
  r2dbc:
    url: r2dbc:postgresql://ignored.invalid:5432/ignored   # deliberately unreachable
    pool:
      enabled: true

Expected: the container's R2dbcConnectionDetails takes precedence and the application connects to the
container, as it does for Flyway over JDBC.

Actual:

org.springframework.dao.DataAccessResourceFailureException: Failed to obtain R2DBC Connection
Caused by: io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException:
          [08003] Cannot connect to ignored.invalid/<unresolved>:5432
Caused by: java.net.UnknownHostException: ignored.invalid

The container is running and healthy throughout.

Evidence that R2dbcConnectionDetails is genuinely absent

R2dbcAutoConfiguration.propertiesR2dbcConnectionDetails is annotated
@ConditionalOnMissingBean(R2dbcConnectionDetails.class). The properties-backed implementation is therefore
created only when no other R2dbcConnectionDetails bean exists. Since the application demonstrably uses the
property value, the container's bean is not present.

Cross-check: removing spring.r2dbc.url from the test profile and giving the placeholder in the main
application.yaml an unreachable default made the application connect to that default instead — again showing
the property is being consulted rather than overridden.

Flyway, in the same context and from the same container bean, connects correctly and migrates the full schema.
So @ServiceConnection processing is working; only the R2DBC side produces nothing.

What was ruled out

Each of these was changed independently and the behaviour did not change:

variable values tried
Testcontainers version 2.0.4 (org.testcontainers.postgresql.PostgreSQLContainer) and 1.21.2 (org.testcontainers.containers.PostgreSQLContainer)
container image custom image built via ImageFromDockerfile + asCompatibleSubstituteFor("postgres"), and stock postgres:17
where the configuration is imported @Import on the @SpringBootApplication class named in @SpringBootTest(classes = …), and @Import directly on the test class
connection name default, and explicit @ServiceConnection("postgresql")
Flyway enabled and disabled, to remove it as a factor

The factory itself appears correctly wired:

  • org.springframework.boot.r2dbc.testcontainers.PostgresR2dbcContainerConnectionDetailsFactory is present in
    spring-boot-r2dbc-4.0.0.jar
  • it is registered in that jar's META-INF/spring.factories
  • its generic parameter is org.testcontainers.postgresql.PostgreSQLContainer — the exact class of the bean
  • its required class, io.r2dbc.spi.ConnectionFactoryOptions, is on the test runtime classpath
    (io.r2dbc:r2dbc-spi:1.0.0.RELEASE, org.postgresql:r2dbc-postgresql:1.1.1.RELEASE)

Note the jar also ships DeprecatedPostgresR2dbcContainerConnectionDetailsFactory, whose generic parameter is the
1.x org.testcontainers.containers.PostgreSQLContainer<?>. Neither path produced details in our testing.

spring-boot-testcontainers-4.0.0.jar itself contains no Postgres classes; the factories live in the
technology-specific modules, which may be relevant to how they are discovered.

Workaround

Publishing the connection as system properties from the container works, since system properties outrank
configuration files:

init {
    CONTAINER.start()
    System.setProperty("spring.r2dbc.url",
        "r2dbc:postgresql://${CONTAINER.host}:${CONTAINER.firstMappedPort}/$DATABASE_NAME")
    System.setProperty("spring.r2dbc.username", CONTAINER.username)
    System.setProperty("spring.r2dbc.password", CONTAINER.password)
}

Notes

  • The application is WebFlux with spring.main.web-application-type: none in the test profile.
  • Dependency versions are managed by io.spring.dependency-management; the org.testcontainers coordinates are
    not managed by it under Boot 4 and carry explicit versions.
  • We did not test the static-field / @ImportTestcontainers style of declaring the container, only the
    @Bean + @ServiceConnection style shown above.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing PostgresR2dbcContainerConnectionDetailsFactory and DeprecatedPostgresR2dbcContainerConnectionDetailsFactory in spring-boot-r2dbc-4.0.0, then inspect their spring.factories registration and how the @Bean plus @ServiceConnection path discovers factories. Reproduce the Kotlin configuration with an unreachable spring.r2dbc.url and add a regression test showing that R2dbcConnectionDetails is published and the container connection takes precedence.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, kotlin, postgresql, spring-boot
Domain
backend, databases, testing
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.