Skip to content

R2DBC ConnectionFactory bean silently not created when enabling pooling through R2DBC URL and properties #28144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mp911de opened this issue Sep 28, 2021 · 4 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@mp911de
Copy link
Member

mp911de commented Sep 28, 2021

When enabling connection pooling for R2DBC through both, the URL (spring.r2dbc.url=r2dbc:pool:mysql:…) and by setting the corresponding property (spring.r2dbc.pool.enabled=true), then Spring Boot backs off entirely from creating a ConnectionFactory. In further consequence, downstream components such as R2dbcEntityTemplate are not created leading to hard-to-understand errors such as:

Error creating bean with name 'healthCheckRepository' […] nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'r2dbcEntityTemplate' available
//... 
Description:

Parameter 0 of constructor in application.service.HealthCheckService required a bean named 'r2dbcEntityTemplate' that could not be found. 


Action:

Consider defining a bean named 'r2dbcEntityTemplate' in your configuration.

It would be good to improve the response by either failing hard in such a case or by dropping the pooling configuration advice from the property-based configuration.

Original ticket: spring-projects/spring-data-r2dbc#659

@wilkinsona
Copy link
Member

Thanks for raising this, @mp911de. Another discrepancy in the behaviour is when the user's indicated that they want to use pooling but r2dbc-pool isn't on the classpath. If they set spring.r2dbc.pool.enabled then the auto-configuration backs off and they're silently left without a ConnectionFactory. On the other hand, if they set spring.r2dbc.url=r2dbc:pool:… then the app fails to start:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'connectionFactory' defined in class path resource [org/springframework/boot/autoconfigure/r2dbc/ConnectionFactoryConfigurations$Generic.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.r2dbc.spi.ConnectionFactory]: Factory method 'connectionFactory' threw exception; nested exception is java.lang.IllegalStateException: Unable to create a ConnectionFactory for 'ConnectionFactoryOptions{options={driver=pool, protocol=h2:mem, database=testing-stuff, options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE}}'. Available drivers: [ h2 ]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.10.jar:5.3.10]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.10.jar:5.3.10]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:338) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332) ~[spring-boot-2.5.5.jar:2.5.5]
	at com.example.demo.R2dbcWithoutPoolTestApplication.main(R2dbcWithoutPoolTestApplication.java:12) ~[main/:na]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.r2dbc.spi.ConnectionFactory]: Factory method 'connectionFactory' threw exception; nested exception is java.lang.IllegalStateException: Unable to create a ConnectionFactory for 'ConnectionFactoryOptions{options={driver=pool, protocol=h2:mem, database=testing-stuff, options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE}}'. Available drivers: [ h2 ]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.10.jar:5.3.10]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.10.jar:5.3.10]
	... 18 common frames omitted
Caused by: java.lang.IllegalStateException: Unable to create a ConnectionFactory for 'ConnectionFactoryOptions{options={driver=pool, protocol=h2:mem, database=testing-stuff, options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE}}'. Available drivers: [ h2 ]
	at io.r2dbc.spi.ConnectionFactories.get(ConnectionFactories.java:145) ~[r2dbc-spi-0.8.5.RELEASE.jar:na]
	at org.springframework.boot.r2dbc.ConnectionFactoryBuilder$OptionsCapableWrapper.buildAndWrap(ConnectionFactoryBuilder.java:203) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.r2dbc.ConnectionFactoryBuilder.build(ConnectionFactoryBuilder.java:189) ~[spring-boot-2.5.5.jar:2.5.5]
	at org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryConfigurations.createConnectionFactory(ConnectionFactoryConfigurations.java:61) ~[spring-boot-autoconfigure-2.5.5.jar:2.5.5]
	at org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryConfigurations$Generic.connectionFactory(ConnectionFactoryConfigurations.java:100) ~[spring-boot-autoconfigure-2.5.5.jar:2.5.5]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.10.jar:5.3.10]
	... 19 common frames omitted

I'll flag this one for team discussion as I'm not sure what our best course of action is to make things behave more consistently and intuitively and what changes we'll be able to make in a maintenance release versus our next minor release.

@wilkinsona wilkinsona added type: bug A general bug for: team-meeting An issue we'd like to discuss as a team to make progress labels Sep 30, 2021
@philwebb philwebb added this to the 2.6.x milestone Oct 1, 2021
@philwebb philwebb removed status: waiting-for-triage An issue we've not yet triaged for: team-meeting An issue we'd like to discuss as a team to make progress labels Oct 1, 2021
@philwebb
Copy link
Member

philwebb commented Oct 1, 2021

We're going to make a URL with pool and the use of Pool properties mutually exclusive. Since this change might be a little involved, and there's a work-around, we'll fix this in 2.6.x rather than 2.4.x.

@wilkinsona wilkinsona self-assigned this Oct 19, 2021
@wilkinsona
Copy link
Member

Making the properties mutually exclusive isn't too bad: https://github.com/wilkinsona/spring-boot/tree/gh-28144.

The implementation thus far has raised a couple of questions:

  • What should happen if the user sets spring.r2dbc.url=r2dbc:pool:… and spring.r2dbc.pool.enabled=false? With the changes as currently written it fails due to the properties now being mutually exclusive. I wonder if users would expect it to honour the URL-based configuration but without pooling.
  • Do we want to do anything about the difference in behaviour with spring.r2dbc.pool.enabled=true or spring.r2dbc.url=r2dbc:pool:… when r2dbc-pool is missing. The former silently backs off and gives you a plain ConnectionFactory. The latter fails.

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Oct 19, 2021
@philwebb
Copy link
Member

What should happen if the user sets spring.r2dbc.url=r2dbc:pool:… and spring.r2dbc.pool.enabled=false? With the changes as currently written it fails due to the properties now being mutually exclusive.

I think that's our best option for now. We can always revisit it if someone raises an issue later.

Do we want to do anything about the difference in behaviour with spring.r2dbc.pool.enabled=true or spring.r2dbc.url=r2dbc:pool:… when r2dbc-pool is missing

I think we should always fail with an exception. It feels like a configuration error to me.

@wilkinsona wilkinsona removed the for: team-attention An issue we'd like other members of the team to review label Oct 20, 2021
@wilkinsona wilkinsona modified the milestones: 2.6.x, 2.6.0-RC1 Oct 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants